I'm having trouble setting the value of a property in a class when the property type is and interface using PropertyInfo.SetValue.
This code is for unit testing which I'd like to use to fake out the IDbSets in my Entity.
public static void AddFakeDbSets<T>(this Mock<T> obj) where T : class
{
var iDbSets = typeof(T).GetProperties().Where(p =>
p.PropertyType.IsGenericType == true &&
p.PropertyType.GetGenericTypeDefinition().Name.StartsWith("IDbSet"));
Type baseType = typeof(FakeDbSet<>);
foreach (var iDbSet in iDbSets)
{
if (iDbSet.GetValue(obj.Object) == null)
{
var genericType = baseType.MakeGenericType(iDbSet.PropertyType);
var concreteObject = Activator.CreateInstance(genericType);
iDbSet.SetValue(obj.Object, concreteObject);
}
}
}
Specifically it crashes at iDbSet.SetValue() with the following error:
Object of type
'lnEcommAPI.Tests.FakeDbSet1[System.Data.Entity.IDbSet1[LNWebServicesRepository.WhatsNew]]'
cannot be converted to type
'System.Data.Entity.IDbSet`1[LNWebServicesRepository.WhatsNew]'.
My guess: replace
var genericType = baseType.MakeGenericType(iDbSet.PropertyType);
by
var genericType = baseType.MakeGenericType(iDbSet.PropertyType.GetGenericArguments()[0]);
The problem is that iDbSet.PropertyType is equal to IDbSet<XXX>.
Thus, typeof(FakeDbSet<>).MakeGenericType(iDbSet.PropertyType) creates a FakeDbSet<IDbSet<XXX>>, while you're expecting a FakeDbSet<XXX>
Related
I have an interface
public interface IBsonClassMap<T>
where T : class
{
void Configure(BsonClassMap<T> map);
}
which serves as base for all mappings for mongo collections.
An implementation of it looks like this
public class StudentClassMap : IBsonClassMap<Student>
{
void IBsonClassMap<Student>.Configure(BsonClassMap<Student> map)
{
}
}
I'm using an extension method to scan an assembly and invoke each mapping found.
This is it.
public static void ApplyConfigurationFromAssemblies(this IServiceCollection services, params Assembly[] assemblies)
{
Type _unboundGeneric = typeof(IBsonClassMap<>);
List<(Type Type, Type Handler, Type Argument)> types = new List<(Type, Type, Type)>();
foreach (Assembly assembly in assemblies)
{
types.AddRange(assembly
.GetExportedTypes()
.Where(type =>
{
bool implementsType = type.GetInterfaces().Any(#interface => #interface.IsGenericType && #interface.GetGenericTypeDefinition() == _unboundGeneric);
return !type.IsInterface && !type.IsAbstract && implementsType;
})
.Select(type =>
{
Type #inteface = type.GetInterfaces().SingleOrDefault(type => type.GetGenericTypeDefinition() == _unboundGeneric);
Type argument = #inteface.GetGenericArguments()[0];
return (type, #inteface, argument);
}));
}
types.ForEach(type =>
{
object classMapInstance = Activator.CreateInstance(type.Type);
Type unboundGeneric = typeof(BsonClassMap<>);
Type boundedGeneric = unboundGeneric.MakeGenericType(type.Argument);
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { boundedGeneric });
});
}
The issue is taht I'm getting
Object of type 'System.RuntimeType' cannot be converted to type
'MongoDB.Bson.Serialization.BsonClassMap`1[Platform.Concepts.Mongo.Collections.Student]'.
Also, everything works as expected if I'm removing the argument in the Configure method of the IBsonClassMap, and addapt everhting accordingly. The method ends up getting invoked.
So instead of this
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { boundedGeneric });
I have this
type.Handler.GetMethod("Configure").Invoke(classMapInstance, null);
You are passing a Type into a method that expects a concrete class of BsonClassMap<T>
It seems you want
object classMapInstance = Activator.CreateInstance(type.Type);
Type unboundGeneric = typeof(BsonClassMap<>);
Type boundedGeneric = unboundGeneric.MakeGenericType(type.Argument);
// create the generic instance
object o = Activator.CreateInstance(boundedGeneric);
type.Handler.GetMethod("Configure").Invoke(classMapInstance, new object[] { o });
Note : Completely untested, and based entirely on my spidey senses
I'm trying to access a property that is the same type of which is passed into a generic.
Look at the code:
class CustomClass
{
CustomProperty property {get; set;}
}
class CustomProperty
{
}
Main
{
// Create a new instance of my custom class
CustomClass myClass = new CustomClass();
// Create a new instance of another class that is the same type as myClass.property
CustomProperty myProp = new CustomProperty();
// Call the generic method
DynamicallyAccessPropertyOnObject<CustomProperty>(myProp, myClass);
}
private void DynamicallyAccessPropertyOnObject<T>(this T propertyToAccess, CustomClass class)
{
// I want to access the property (In class) that is the same type of that which is passed in the generic (typeof(propertyToAccess))
// TODO: I need help accessing the correct property based on the type passed in
}
If you can't see from the code. Basically I want to be able to pass in a something into a generic and then access the property on a class that is of the same type as the thing that was passed in.
Is there a good way to do this?
If you need clarification let me know...
You can use Reflection, and LINQ:
private static void DynamicallyAccessPropertyOnObject<T>()
{
var customClass = typeof(CustomClass);
var property = customClass
.GetProperties()
.FirstOrDefault(x => x.PropertyType == typeof(T));
}
If you are doing this for CustomClass only, you can remove both parameters.Then you can call it:
DynamicallyAccessPropertyOnObject<CustomProperty>();
If you want to generalize it, use two generic arguments:
private static void DynamicallyAccessPropertyOnObject<T, K>(K targetObj)
{
var targetType = targetObj.GetType();
var property = targetType
.GetProperties()
.FirstOrDefault(x => x.PropertyType == typeof(T));
if(property != null)
{
var value = (T)property.GetValue(targetObj);
}
}
Then call it:
DynamicallyAccessPropertyOnObject<CustomProperty,CustomClass>(myClass);
If there's only one such property you can do:
var prop = typeof(CustomClass).GetProperties().First(p => p.PropertyType == typeof(T));
object value = prop.GetValue(#class, null);
you can set the value with SetValue:
object valueToSet = ...
prop.SetValue(#class, valueToSet);
I 'm trying to build a DI container and I 've stumbled on to the following problem: I have a method that retrieves a list of registered instances for a given type and I want to use that to inject IEnumerable<T> properties in a given object. An example of what I am trying to achieve would be the following:
class A { public IList<IExample> Objects { get; set; } }
class B: IExample {}
class C: IExample {}
Container.Register<IExample>(new B());
Container.Register<IExample>(new C());
var obj = new A();
Container.Inject(A);
Debug.Assert(A.Objects != null && A.Objects.Count == 2);
My Retrieve method returns an IList<object>, mainly because I have no type information at that moment, so I am attempting to convert that list into a List<T> at injection time. Here is a succint form of the methods doing the work:
public virtual IList<object> Retrieve(Type type)
{
var instances = Registry[type];
foreach(var instance in instances)
Inject(type, instance); // omitted
return instances;
}
public virtual void Inject<T>(T instance)
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!IsIEnumerable(propertyType)) continue;
var genericType = propertyType.GetGenericArguments()[0];
propertyInfo.SetValue(instance,
GetListType(genericType, Retrieve(genericType)), null);
}
}
protected virtual object GetListType(Type type, IEnumerable<object> items)
{
return items.Select(item => Convert.ChangeType(item, type)).ToList();
}
The code returns the error: System.InvalidCastException : Object must implement IConvertible. Sadly, I don't know how to proceed from here. Perhaps I am doing this all wrong. I 've thought of using generics or injecting multiple properties by hand, but I'd really like to not have to do that.
Thanks in advance for any help or ideas.
You could create a generic list like this:
public virtual IList Retrieve(Type type)
{
// ...
listType = typeof(List<>).MakeGenericType(new Type[] { type });
IList list = (IList)Activator.CreateInstance(listType);
// ...
return list
}
this list can be casted to IList<T>, because it is one.
You could consider to use IEnumerable and Cast<T>, but then you don't have an instance of a list. I don'^t know how important it is to have one.
I'm trying to check whether an open generic type definition implements some open generic interface. Look at the sample below:
public interface IService<T> { }
public class ServiceImpl<T> : IService<T> { }
private static bool OpenGenericTypeImplementsOpenGenericInterface(
Type derivedType, Type interfaceType)
{
return derivedType.GetInterfaces().Contains(interfaceType);
}
[TestMethod]
public void Verify()
{
Type openGenericImplementation = typeof(ServiceImpl<>);
Type expectedInterfaceType = typeof(IService<>);
bool implDoesImplementInterface = OpenGenericTypeImplementsOpenGenericInterface(
openGenericImplementation, expectedInterfaceType);
// This assert fails. Why?
Assert.IsTrue(implDoesImplementInterface);
}
I found out that the returned type from the Type.GetInterfaces() method does not match the type returned from typeof(IService<>). I can't figure out why that is and how to correctly validate whether some generic type definition inherits or implements some other generic type definition.
What's going on here and how do I solve fix this problem?
The problem is that GetInterfaces returns closed types so you need to open them using GetGenericTypeDefinition:
public static bool ImplementsOpenInterface(Type type, Type openInterfaceType) {
Contract.Requires(type != null);
Contract.Requires(openInterfaceType != null);
Contract.Requires(openInterfaceType.IsGenericTypeDefinition);
Type[] interfaces = type.GetInterfaces();
if (interfaces == null) {
return false;
}
return interfaces
.Where(x => x.IsGenericType)
.Select(x => x.GetGenericTypeDefinition())
.Any(x => x == openInterfaceType);
}
Change your method with this and it will work:
private static bool OpenGenericTypeImplementsOpenGenericInterface(
Type derivedType, Type interfaceType)
{
return derivedType.GetInterface(interfaceType.Name) != null;
}
GetInterfaces() will return a closed Type object with the generic parameter that it implements the interface with.
Instead, use LINQ:
return derivedType.GetInterfaces().Any(i =>
i == interfaceType
|| (i.ContainsGenericParameters && i.GetGenericTypeDefinition() == interfaceType))
This code checks whether any of the interfaces that it implements is a parameterized version of your interface.
I had a need to expand on this to include type inheritance in addition to interfaces. Here's what I came up with:
interface IFace<T> {}
class Impl<T> : IFace<T> {}
class Derived<T> : Impl<T> {}
public static bool InheritsFrom(this Type tDerived, Type tBase)
{
if (tDerived.IsSubtypeOf(tBase)) return true;
var interfaces = tDerived.GetInterfaces()
.Select(i => i.IsGenericType ? i.GetGenericTypeDefinition() : i);
return interfaces.Contains(tBase);
}
public static bool IsSubtypeOf(this Type tDerived, Type tBase)
{
var currentType = tDerived.BaseType;
while (currentType != null)
{
if (currentType.IsGenericType)
currentType = currentType.GetGenericTypeDefinition();
if (currentType == tBase) return true;
currentType = currentType.BaseType;
}
return false;
}
Note that while these methods will work on any two types, they assume that if a generic type is passed, the type is open (that is, it is the generic type definition without defined type parameters).
I use a method similar to the following to get some precomputed metadata related to a Type's properties.
MyData GetProperty<T, U>(Expression<Func<T, U>> member)
{
// Get the property referenced in the lambda expression
MemberExpression expression = member.Body as MemberExpression;
PropertyInfo property = expression.Member as PropertyInfo;
// get the properties in the type T
PropertyInfo[] candidates = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
// Find the match
foreach (PropertyInfo candidate in candidates)
if (candidate == property)
return GetMetaData<T>(candidate);
throw new Exception("Property not found.");
}
// Returns precomputed metadata
MyData GetMetaData<T>(PropertyInfo property) { ... }
As you would expect, it works when used as follows:
var data = PropertyInfo((Employee e) => e.Name);
But not when used in the following generic method:
void MyGenericMethod<T>(int id) where T : IEmployee
{
var data = PropertyInfo((T e) => e.Name);
}
It fails because the declaring type of property in the first method is now IEmployee, so the property in the lambda doesn't match the property in the type. How can I get them to match, without relying on the names of the properties? (There can be multiple properties with the same name if interfaces are implemented explicitly, so p1.Name == p2.Name won't cut it).
What you'd probably need is an InterfaceMapping. You can get that from the actual type by calling GetInterfaceMap(typeof(interface)), i.e.,
InterfaceMapping mapping = typeof(Employee).GetInterfaceMap(typeof(IEmployee));
Now, the mapping will contain the fields InterfaceMethods which will contain the methods you see when reflecting the interface, and TargetMethods which are the class's implementing methods. Note that this maps the the getter methods from the interface to the getter methods from the target class. You'll need to find the proper interface property by mapping the getter method of the various properties of the class to the found getter method.
Type interfaceType = typeof(IEmployee);
Type classType = typeof(Employee);
PropertyInfo nameProperty = interfaceType.GetProperty("Name");
MethodInfo nameGetter = nameProperty.GetGetMethod();
InterfaceMapping mapping = classType.GetInterfaceMap(interfaceType);
MethodInfo targetMethod = null;
for (int i = 0; i < mapping.InterfaceMethods.Length; i++)
{
if (mapping.InterfaceMethods[i] == nameGetter)
{
targetMethod = mapping.TargetMethods[i];
break;
}
}
PropertyInfo targetProperty = null;
foreach (PropertyInfo property in classType.GetProperties(
BindingFlags.Instance | BindingFlags.GetProperty |
BindingFlags.Public | BindingFlags.NonPublic)) // include non-public!
{
if (targetMethod == property.GetGetMethod(true)) // include non-public!
{
targetProperty = property;
break;
}
}
// targetProperty is the actual property
Caution: Note the use of BindingFlags.NonPublic and GetGetMethod(true) here, for accessing private members. If you've got an explicit interface implementation, there isn't really a public property matching the interface's property, instead there is a private property named Some.NameSpace.IEmployee.Name that is mapped (which is, of course, your explicit implementation).
When you've found the right property, you can just call
ParameterExpression p = Expression.Parameter("e", typeof(T));
Expression<Func<T, U>> lambda = Expression.Lambda<Func<T, U>>(
Expression.Property(p, targetProperty), p);
and you've got yourself a lambda expression that uses the class's properties rather than the interface's properties.
Does BindingFlags.FlattenHierarchy work? If not, you could always iterate through typeof(T).GetInterfaces and call GetProperties on each of them.
You'll need to get the member name from the lambda expression, and use reflection to get that member off of the type you've been given:
public static PropertyInfo PropInfo<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var memberName = GetExpressionMemberName(memberGetter);
return typeof(TContainer).GetProperty(memberName);
}
public static string GetExpressionMemberName<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var expressionType = memberGetter.Body.NodeType;
switch (expressionType)
{
case ExpressionType.MemberAccess:
{
var memberExpr = (MemberExpression) memberGetter.Body;
return memberExpr.Member.Name;
}
case ExpressionType.Convert:
{
var convertExpr = (UnaryExpression) memberGetter.Body;
var memberExpr = (MemberExpression) convertExpr.Operand;
return memberExpr.Member.Name;
}
default:
throw new InvalidOperationException("Expression {0} does not represent a simple member access.");
}
}
Here's proof that it works:
void Main()
{
Console.WriteLine(
MyGenericMethod<Employee>()
.GetGetMethod()
.Invoke(
new Employee {Name = "Bill"},
new object[] {}));
}
public class Employee : IEmployee {
public string Name {get;set;}
string IEmployee.Name { get { throw new Exception(); } }
}
public interface IEmployee {string Name {get;}}
public PropertyInfo MyGenericMethod<T>() where T : IEmployee
{
return PropInfo((T e) => e.Name);
}
Console output:
Bill