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
Related
I have a couple interfaces (IMapFrom and IMapTo) that allow me to simplify my AutoMapper configurations. Each of the interfaces has a default implementation for the MapTo and MapFrom methods. I have a separate MappingProfile class that uses reflection to find all of the implementing classes, and invokes their map creation.
The aforementioned classes look like so:
public interface IMapFrom<T>
{
void MapFrom(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
public interface IMapTo<T>
{
void MapTo(Profile profile) => profile.CreateMap(GetType(), typeof(T));
}
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces().Any(i =>
i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IMapFrom<>) ||
i.GetGenericTypeDefinition() == typeof(IMapTo<>))))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var mapTo = type.GetMethod("MapTo");
var mapFrom = type.GetMethod("MapFrom");
mapTo?.Invoke(instance, new object[] {this});
mapFrom?.Invoke(instance, new object[] {this});
}
}
}
If the class implementing the interfaces overrides the default interface implementations, the MappingProfile class works as desired. However, if the classes simply rely on the default implementations, mapTo and mapFrom in the ApplyMappingsFromAssembly method are both null.
For example, this class would not have its mappings applied successfully:
public class CreateJobCommand :
UpdateJobInputModel,
IMapFrom<UpdateJobInputModel>,
IMapTo<Job>,
IRequest<int>
{
}
How can I get the default implementations if they're not reimplemented in the inheriting class?
Per Kevin Gosse's comment to my question, I looked into using GetInterface().GetMethod() as seen in the Microsoft documentation.
If I take that approach, the now functional, resulting code looks like:
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces().Any(i =>
i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IMapFrom<>) ||
i.GetGenericTypeDefinition() == typeof(IMapTo<>))))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var mapTo = type.GetMethod("MapTo") ??
instance!.GetType()
.GetInterface("IMapTo`1")?
.GetMethod("MapTo");
var mapFrom = type.GetMethod("MapFrom") ??
instance!.GetType()
.GetInterface("IMapFrom`1")?
.GetMethod("MapFrom");
mapTo?.Invoke(instance, new object[] {this});
mapFrom?.Invoke(instance, new object[] {this});
}
}
}
Autofac allows you to register generic types with the RegisterGeneric method.
I would like to register ImmutableArray<T>.
The semantics I want are the same as for IEnumerable<T> - namely Autofac should create an instance of an ImmutableArray<T> with all registrations of T.
If this was non generic it would be simple:
builder.Register<ImmutableArray<string>>(c => c.Resolve<IEnumerable<string>>().ToImmutableArray());
However I can't see any way to that with generic types.
There is no easy way to register a generic method with a lambda expression. You need to use a IRegistrationSource with generic type manipulation to do that.
Something like that :
public class ImmutableArrayRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents => false;
private static MethodInfo toImmutableArrayMethod =
typeof(ImmutableArray)
.GetMethods()
.Where(m => m.Name == nameof(ImmutableArray.ToImmutableArray)
&& m.GetParameters().Any(p => p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
.FirstOrDefault();
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
IServiceWithType typedService = service as IServiceWithType;
if (typedService != null && typedService.ServiceType.IsClosedTypeOf(typeof(ImmutableArray<>)))
{
Type elementType = typedService.ServiceType.GetGenericArguments()[0];
IComponentRegistration r = RegistrationBuilder.ForDelegate(typedService.ServiceType, (c, p) =>
{
Object elements = c.Resolve(typeof(IEnumerable<>).MakeGenericType(elementType));
Object immutableArray = toImmutableArrayMethod.MakeGenericMethod(elementType)
.Invoke(null, new Object[] { elements });
return immutableArray;
}).As(service)
.CreateRegistration();
yield return r;
}
yield break;
}
}
and then register your registration source :
builder.RegisterSource<ImmutableArrayRegistrationSource>();
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>
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).
It's there a way to invoke a method and the return type is strong typed ?
There is an example of code
public static IQueryable<T> FilterVersion<T>(this Table<T> t, IVersionIndexFilter version)
where T : class
{
try
{
// Define the new type of my table
Type versionTableType = Type.GetType(typeof(T).AssemblyQualifiedName.Replace(typeof(T).FullName, string.Concat(typeof(T).FullName, VersionIndexTableExtensionName)));
// Get the method info generic with a custom helper
var getTableType = MethodInfoHelper.GetGenericMethod(typeof(DataContext), "GetTable", new Type[] { versionTableType }, new Type[] { }, typeof(Table<>).MakeGenericType(typeof(Table<>).GetGenericArguments()[0]), BindingFlags.Public | BindingFlags.Instance);
// Get the object with a invoke but invoke return object, i need he return a Table<vesrionTableType>
var joinTable = getTableType.Invoke(t.Context, null);
// Put my new object in anonymous class
var result = new { FromT = t, InnerT = joinTable };
// Write the type of the property and is {System.Object InnerT}
Console.Write(result.GetType().GetProperty("InnerT"));
}
I need my Console.Write(result.GetType().GetProperty("InnerT")); return a Table<versionTableType>
It's there a way i cand do that ? Any suggestion ?
There is my GetGenericMethod
public static MethodInfo GetGenericMethod(Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType, BindingFlags flags)
{
if (genericArgTypes == null)
{
genericArgTypes = new Type[] { };
}
MethodInfo genericMethod = (from m in t.GetMethods(flags)
where m.Name == name
&& m.GetGenericArguments().Length == genericArgTypes.Length
&& m.GetParameters().Select(pi => pi.ParameterType.IsGenericType ? pi.ParameterType.GetGenericTypeDefinition() : pi.ParameterType).SequenceEqual(argTypes) &&
(returnType == null || (m.ReturnType.IsGenericType ? m.ReturnType.GetGenericTypeDefinition() : m.ReturnType) == returnType)
select m).FirstOrDefault();
if (genericMethod != null)
{
return genericMethod.MakeGenericMethod(genericArgTypes);
}
return null;
}
I get my method info generic correctly. The probleme is when i assign to another property the result of my invoke is an object. I need to strong type my result
EDIT : It's a good idea the static method CreateResult but not enough ... there is what i try to do after with my value
NewExpression exResult = Expression.New(result.GetType().GetConstructor(new Type[] { t.GetType(), typeof(Table<>).MakeGenericType(versionTableType) }), new List<Expression>() { outer, inner }, result.GetType().GetProperty("FromT"), result.GetType().GetProperty("InnerT"));
There is the error i got after try the technique
Argument type 'System.Data.Linq.Table`1[Nms.Media.Business.Manager.Data.Linq.ImageLibraryItemBinaryLocaleVersionIndex]' does not match the corresponding member type 'System.Object'
Thanks
EDIT: Okay, since your question edit it looks like you're already calling MakeGenericMethod. Good.
Now, once you've managed to invoke the method, there's then the matter of making an appropriate anonymous type. I would suggest you create a generic type and use Type.MakeGenericType on the generic type definition, then construct an instance of that. Alternatively, you could have another generic method which is strongly typed, and invoke that with reflection in a similar way. That method could use an anonymous type:
public object CreateResult<TTable, TJoin>(TTable t, TJoin joinTable)
{
return new { FromT = t, InnerT = joinTable };
}
You could call your generic MethodInfo helper to make it easier to invoke this.
I Auto resolve my solutions, inspired from Jon Skeet answer.
I create a class generic
public class ConvertTable<TOuter, TInner>
where TInner : class
where TOuter : class
{
public TOuter FromT { get; set; }
public TInner InnerT { get; set; }
public ConvertTable(TOuter fromT, TInner innerT)
{
FromT = fromT;
InnerT = innerT;
}
}