Creating instances of generic base class - c#

I am working with the Dapper.FluentMap library, and trying to set up auto-registration of my mapping classes. To do that, I need to call FluentMapConfiguration.AddMap<T>(EntityBase<T>) for each of the mapping classes.
I can do it like this:
public class TypeAMap : EntityMap<TypeA> {}
public class TypeBMap : EntityMap<TypeB> {}
public class TypeCMap : EntityMap<TypeC> {}
public void Register(FluentMapConfiguration configuration)
{
configuration.AddMap(new TypeAMap());
configuration.AddMap(new TypeBMap());
configuration.AddMap(new TypeCMap());
// I have a hundred of these, you can see where I'm going...
}
Obviously a problem in the making when you forget to register a map and wonder why your data isn't loading properly. So on to some reflection to auto-register:
public void Register(FluentMapConfiguration configuration)
{
var maps = GetType().Assembly.GetExportedTypes().Where(t =>
!t.IsAbstract &&
!t.IsInterface &&
t.BaseType is { IsGenericType: true } &&
t.BaseType.GetGenericTypeDefinition() == typeof(EntityMap<>)
).ToArray();
foreach (var map in maps)
{
var baseType = typeof(EntityMap<>);
var typeArguments = map.BaseType.GetGenericArguments();
var genericType = baseType.MakeGenericType(typeArguments);
var instance = Activator.CreateInstance(genericType);
configuration.AddMap((dynamic) instance);
}
}
but when it gets to the call to Activator.CreateInstance, it fails, with a MissingMethodException, Cannot create abstract class. It looks like it's trying to create an instance of EntityBase<TypeA> rather than TypeAMap, and since EntityBase<T> is an abstract class, I'm getting the error. So how can I construct my instances correctly?

The Where call filters out the types in the assembly and includes only types that are not abstract, not interfaces, and are direct subclasses of EntityMap<anything>. So maps contains the types TypeAMap, TypeBMap, TypeCMap etc.
Then for each of those, your code gets its base class and tries to instantiate that instead. See the annotated code in the for loop:
// suppose "map" is typeof(TypeAMap)
var baseType = typeof(EntityMap<>);
// typeArguments would be an array containing typeof(TypeA) only.
var typeArguments = map.BaseType.GetGenericArguments();
// genericType would be typeof(EntityMap<TypeA>)
var genericType = baseType.MakeGenericType(typeArguments);
// now you try to instantiate a EntityMap<TypeA>, and fails, because EntityMap<TypeA> is abstract
var instance = Activator.CreateInstance(genericType);
Since you want to do the reflection version of new TypeAMap(), new TypeBMap() etc, and we know that maps contain those types that you want to instantiate, you can just do:
foreach (var map in maps)
{
var instance = Activator.CreateInstance(map);
configuration.AddMap((dynamic) instance);
}
You don't need to care about the generic type parameters for EntityMap at all. The dynamic binder will figure that out when it tries to infer the type parameter for AddMap.

Related

Find all gameojects with components of base class

I have the base class derived from MonoBehaviour
public abstract class AbstractMyGameBeh : MonoBehaviour{//....}
And many other classes, realising this base class.
public class MyGameBeh: AbstractMyGameBeh {//....}
public class BMyGameBeh: AbstractMyGameBeh {//....}
public class CMyGameBeh: AbstractMyGameBeh {//....}
So now I need to find all gameobjects of this classes.
Ofcource, I can do like this, but if I make more classes derived from AbstractMyGameBeh, I'll be have to fix this code part again.
GameObject.FindObjectsOfType<AMyGameBeh>()
GameObject.FindObjectsOfType<BMyGameBeh>()
GameObject.FindObjectsOfType<CMyGameBeh>()
C# 6.0 allows the use of
typeof
if(objOfTypeMyGameBeh.GetType() == typeof(AbstractMyGameBeh))
{
//It's the type you want
}
else
{
//it's not the type you want
}
or as
if(objOfTypeMyGameBeh as AbstractMyGameBeh != null)
{
//It's the type you want, works aswell with inheritance
}
else
{
//it's not the type you want
}
That should work and you can put it in a loop pretty easily to check every object.
As pointed out in the comments, if you're not storing the value you can use is keyword
I've had a similar Problem recently and this solution might be applicable to your problem:
public static IEnumerable<Type> GetTypes()
{
var sourceAssembly = Assembly.GetCallingAssembly();
var assemblies = new List<Assembly>();
assemblies.AddRange(sourceAssembly.GetReferencedAssemblies().Select(an => Assembly.Load(an)));
assemblies.Add(sourceAssembly);
var subclassTypes = new HashSet<Type>();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(AbstractMyGameBeh)));
foreach (var type in types) subclassTypes.Add(type);
}
return subclassTypes;
}
This should work even if your AbstractMyGameBeh is in a different Assembly as long as you call it from an assembly where all child classes are available.
It will search all assemblies (the calling one and all that are referenced by it) for heir classes of your abstract class.
Now This leaves you with a set of Types. So you'd still need to use reflection to call GameObject.FindObjectsOfType<T>(). Should be something like this...
foreach(var subclassType in subclassTypes)
{
MethodInfo method = GetType("GameObject").GetMethod("FindObjectsOfType")
.MakeGenericMethod(new Type[] { subclassType });
method.Invoke(gameObject, new object[] { });
}

How to get underlying type of generic interface?

public class TestBase
{
// implementation
}
public class Test : ICollection<TestBase>
{
// implementation
}
Somewhere else I have a property of the Test type:
public Test Test {get;set;}
How can I get the underlying type of the ICollection that the Test inherits from?
What you are probably looking for is GetGenericArguments().
var type = typeof(Test);
var collInterface = type.GetInterfaces()[0];
var generic = collInterface.GetGenericArguments()[0];
Let x be an instance of a class that implements interfaces. You can get its iterface types as follows:
var type = x.GetType();
var interfaces = type.GetInterfaces();
Edit: I had your question wrong: Follow up with "GetGenericArguments()" as suggested.
the GetInterfaces method returns all implemented interfaces in current type and its GetGenericArguments method returns list of generic arguments witch you can get its base type by BaseType property.
this example returns class hierarchy of the generic argument of base interface
implemented in current class:
var myType = this.GetType().GetInterfaces().First().GetGenericArguments().First();
while (myType != null)
{
Console.WriteLine(myType.Name);
myType = myType.BaseType;
}
Just a different approach of getting the used type in a base interface:
private readonly IObjectProvider<Interface> _repositoryInterfaces =
Interfaces().That().ImplementInterface(typeof(IRepository<>)).As("Repository Interfaces");
[Fact]
public void RepositoryInterfaces_ShouldBeInTheCorrectNamespace()
{
// Arrange
var repos = Interfaces().That().Are(_repositoryInterfaces).GetObjects(Architecture);
// Act
// Assert
foreach (var repo in repos)
{
var entityType = repo.GetImplementsInterfaceDependencies().Single().TargetGenericArguments.Single().Type;
repo.ResidesInNamespace($"Domain.AggregateModels.{entityType.Name}Aggregate").Should().BeTrue();
}
}
my interfaces look like this:
public interface IExampleRepository : IRepository<ExampleEntity>
{ //more methods here }
Type t = typeof(Test);
MethodInfo mi = t.GetMethod("MyMethod");
Type returnvalue = mi.ReturnType;
Type answer = Nullable.GetUnderlyingType(returnvalue);
I hope it helps.

Activator.CreateInstance with dynamic Type

I think I have a lack of understand, what exactly is happening:
The user can enter a Path to an Assembly and an Object type and I try to create a instance of it.
My approach:
Assembly a = Assembly.LoadFile(txtAssemblyPath.Text);
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
var obj = Activator.CreateInstance<myType>();
var obj2 =(myType) Activator.CreateInstance(myType);
The problem is in the Creation of the Object itself. It seems like the myType is not threated as a Type. On this example: Creating generic variables from a type - How? Or use Activator.CreateInstance() with properties { } instead of parameters ( )?
They just get an object, so I guess this is not the same Case. What I dont understand at all: CreateInstance(Type) works, but CreateInstance with the Type doesn't, but T and Type should be the same: System.Type.
Thanks in advance for clarifying.
Matthias
There are a using difference... When you write:
var obj = Activator.CreateInstance<myType>();
You used your Class like a Type, and it's the good way to do it.
You used a generic type who have a reference to a class type.
But there:
var obj2 =(myType) Activator.CreateInstance(myType);
You used you class like an instance (object). And you can't do that, a class is a schema.
If you want to call the second method, you have to write:
var obj2 =(myType) Activator.CreateInstance(typeof(myType));
This code will create an instance of the class Type, and this instance will describe your class myType.
I hope to be clear.
A class is a schema, you can create an object with this schema, it will be an instance (a memory-object of your class).
When you use generic type of method such as Activator.CreateInstance<T>(); you have to provide Strong Type of T. That means you have to pass known type name instead of T. for example:
var activatorPerson = (Person)Activator.CreateInstance<Person>();
That's why there is a non generic form of Activator.CreateInstance(typeGoesHere) function which can be used in cases that we do not have a Strong Type at moment of creation of an object. So we can pass type as a parameter to that function. we can provide type variable in lots of ways. In your case you find the proper type in your assembly as following:
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
also you have to notice that explicit casting as you typed in your code is not valid:
obj2 =(myType) Activator.CreateInstance(myType);
because you have to provide Strong Type name for explicit casting.
when we do not have access to strong type names at run time, we have to use non generic versions of methods.
Here is the pure dynamic way.
This is the factory class and dynamic instance create method:
public class RepositoryFactory
{
public static dynamic CreateDynamic<TEntity>() where TEntity : BaseEntity
{
dynamic repositoryInstance = null;
var subRepositories = AssemblyHelper.GetSubclassesOf(typeof(BaseRepository<TEntity>), true);
var entityTypeName = typeof(TEntity).Name;
var subRepository = subRepositories.FirstOrDefault(x => x.Name == entityTypeName + "Repository");
if (subRepository != null)
{
var repositoryType = subRepository.UnderlyingSystemType;
repositoryInstance = Activator.CreateInstance(repositoryType);
}
return repositoryInstance;
}
}
This is the helper class for mapping types between Entity and Repository.
public static class AssemblyHelper
{
public static List<Type> GetSubclassesOf(Type type, bool ignoreSystem)
{
List<Type> lReturn = new List<Type>();
foreach (var a in System.Threading.Thread.GetDomain().GetAssemblies())
{
if (ignoreSystem && a.FullName.StartsWith("System."))
{
continue;
}
foreach (var t in a.GetTypes())
{
if (t.IsSubclassOf(type) || (type.IsInterface && t.GetInterfaces().FirstOrDefault(e => e.FullName == type.FullName) != null))
{
lReturn.Add(t);
}
}
}
return lReturn;
}
}
This is the Manager class for use case:
public class PageManager
{
private readonly ContentPageRepository _pageRepository;
public PageManager()
{
_pageRepository = RepositoryFactory.CreateDynamic<ContentPage>();
}
public void GetPagesByContentType(ContentType type)
{
var pages = _pageRepository.GetByPredicate(x => x.Status != EntityStatus.Deleted && x.Node.Type == type);
foreach (var page in pages)
{
//Deal with it :)
}
}
}

Get class from Type with Reflection and call a generic constructor with Type in C#

I am working with Dapper and I want to iterate through my model classes and set type mapping for any class having a field decorated with a ColumnAttribute.
public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
{
public static readonly string ColumnAttributeName = "ColumnAttribute";
public ColumnAttributeTypeMapper()
: base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(typeof (T), SelectProperty),
new DefaultTypeMap(typeof (T))
})
{
}
// implementation of SelectProperty and so on...
// If required, full implementation is on https://gist.github.com/senjacob/8539127
}
In my model class library, I'm iterating through all possible types; now I need to call the generic ColumnAttributeTypeMapper<T> constructor with the class of the type.
using System.Web;
using Dapper;
[assembly : PreApplicationStartMethod(typeof(Model.Initiator), "RegisterTypeMaps")]
namespace Model
{
class Initiator
{
public static void RegisterTypeMaps()
{
var mappedTypes = Assembly.GetAssembly(typeof (Initiator)).GetTypes().Where(
f =>
f.GetProperties().Any(
p =>
p.GetCustomAttributes(false).Any(
a => a.GetType().Name == ColumnAttributeTypeMapper<dynamic>.ColumnAttributeName)));
// I want to skip registering each class manually :P
// SqlMapper.SetTypeMap(typeof(Model1), new ColumnAttributeTypeMapper<Model1>());
// SqlMapper.SetTypeMap(typeof(Model2), new ColumnAttributeTypeMapper<Model2>());
foreach (var mappedType in mappedTypes)
{
SqlMapper.SetTypeMap(mappedType, new ColumnAttributeTypeMapper<mappedType>());
}
}
}
}
How can I pass the class from type instead of type 'mappedType' to new ColumnAttributeTypeMapper<classof(mappedType)?>()
I found this as a similar question, but I need to call the generic constructor instead of a generic method with the Type.
If it can not be done, could you please explain the reason?
Answer
This is how the mapping worked as suggested by Tom.
var mapper = typeof(ColumnAttributeTypeMapper<>);
foreach (var mappedType in mappedTypes)
{
var genericType = mapper.MakeGenericType(new[] { mappedType });
SqlMapper.SetTypeMap(mappedType, Activator.CreateInstance(genericType) as SqlMapper.ITypeMap);
}
You will need the method Type.MakeGenericType; usage is as follows:
var columnType = typeof(ColumnAttributeTypeMapper<>);
var genericColumn = columnType.MakeGenericType(new[] {typeof(mappedType)});
var instance = Activator.CreateInstance(genericColumn);
I'm writing this without intellisense and only having skimmed your code, so please let me know whether I've made any mistakes and I'll correct them.

Property Type as Generic parameter

I'm trying to figure out how I can make a Generics call take a variable for the Type. In the call below it take a type "DAL.Account" and works fine.
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
I want to change that so that I can pass a variable in place of the "DAL.Account". Something like this but I know that won't work as you can't pass property as a Type.
ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray())
Below is the shell pieces of code I think explains what I'm trying to do. Generics is not my strong suit so I'm looking for some help. Is there anyway that I can make this happen?
//Stores a "Type" that indicates what Object is a Criteria for.
public class AccountCriteria : IGeneratedCriteria
{
...
public Type EntityType
{
get {return typeof(DAL.Account);}
}
}
//I have added a function to the DataContext called "GetTable"
// And then used it as an example in a Console App to test its functionality.
public class ADRPDataContext : NHibernateDataContext
{
...
public CodeSmith.Data.NHibernate.ITable<T> GetTable<T>() where T : EntityBase
{
var tb = new CodeSmith.Data.NHibernate.Table<T>(this);
return tb;
}
}
// console application that uses DataContext.GetTable
class Program
{
static void Main(string[] args)
{
using (var ctx = new ADRPDataContext())
{
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
}
}
}
//ExistsCommand class that uses the EntityType property of the Critera to generate the data.
public class ExistsCommand
{
private IGeneratedCriteria Criteria { get; set; }
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
//This was my first attempt but doesn't work becuase you can't pass a property in for a Type.
//But I can figure out how to write this so that it will work.
Result = ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
You are looking to instantiate a generic type. Some info can be found here
This is a simple example demonstrating how to instantiate a List with a capacity of 3. Here is a method that you can call to create a generic when you don't know the type:
public static Object CreateGenericListOfType(Type typeGenericWillBe)
{
//alternative to the followin:
//List<String> myList = new List<String>(3);
//build parameters for the generic's constructor (obviously this code wouldn't work if you had different constructors for each potential type)
object[] constructorArgs = new Object[1];
constructorArgs[0] = 3;
//instantiate the generic. Same as calling the one line example (commented out) above. Results in a List<String> with 3 list items
Type genericListType = typeof(List<>);
Type[] typeArgs = { typeGenericWillBe };
Type myNewGeneric = genericListType.MakeGenericType(typeArgs);
object GenericOfType = Activator.CreateInstance(myNewGeneric, constructorArgs);
return GenericOfType;
}
And here is some sample code that will show you the example method works:
List<String> Strings = (List<String>)InstantiateGenericTypeWithReflection.CreateGenericListOfType(typeof(String));
//demonstrate the object is actually a List<String> and we can do stuff like use linq extensions (isn't a good use of linq but serves as example)
Strings.Add("frist");
Strings.Add("2nd");
Strings.Add("tird");
Console.WriteLine("item index 2 value: " + Strings.Where(strings => strings == "2").First());
In your example, replace your GetTable<Criteria.EntityType>() with CreateGenericTableOfType(Criteria.EntityType). This will return a generic table of whatever type you pass in. You will of course need to implement the method properly (handle constructor args, change List to Table etc).
I think you need to change the way you're doing this slightly, and instead use generics instead of the EntityType property. Perhaps something along the lines of the following:
// Create an abstract class to be used as the base for classes that are supported by
// ExistsCommand and any other classes where you need a similar pattern
public abstract class ExtendedCriteria<T> : IGeneratedCriteria
{
public ExistsCommand GetExistsCommand()
{
return new ExistsCommand<T>(this);
}
}
// Make the non-generic ExistsCommand abstract
public abstract class ExistsCommand
{
protected abstract void DataPortal_Execute();
}
// Create a generic sub-class of ExistsCommand with the type parameter used in the GetTable call
// where you were previously trying to use the EntityType property
public class ExistsCommand<T> : ExistsCommand
{
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
Result = ctx.GetTable<T>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
// Derive the AccountCriteria from ExtendedCriteria<T> with T the entity type
public class AccountCriteria : ExtendedCriteria<DAL.Account>
{
...
}

Categories

Resources