Dynamically get a DbSet - c#

I want to get a DbSet with the class name that I have stored in a variable.
I have tried with this code:
string name = "ParosLineas";
var dbset = (System.Data.Entity.DbSet)efContext.GetType().GetProperty(name).GetValue(efContext);
dbset.AddRange(dates.[name]);
efContext.SaveChanges();
but I get this error:
System.InvalidCastException: 'Unable to cast object of type 'System.Data.Entity.DbSet1[Report.Models.ParosLinea]' to type 'System.Data.Entity.DbSet'
this is my ParosLine declaration:

The problem is that the generic DbSet<TEntity> class does not inherit (hence cannot be cast to) the non generic DbSet.
I see two options.
If you know the namespace name of the entity class, you can use Type.GetType to get the corresponding Type which in turn can be used to call the non generic DbContext.Set method which returns a non generic DbSet object, e.g.
string nameSpace = "MyEntities";
string name = "ParosLineas";
var type = Type.GetType($"{namespace}.{name}");
var dbSet = efContext.Set(type);
Another way is to use reflection to get the DbSet<T> property as you are doung, but cast the result to IQueryable. Then you can use the IQueryable.ElementType to call the non generic Set method as above:
string name = "ParosLineas";
var type = ((IQueryable)efContext.GetType().GetProperty(name).GetValue(efContext))
.ElementType;
var dbSet = db.Set(type);
The first method is preferable. First, because it uses less calls, and second, because does not require DbSet<T> property in the context and does not assume that DbSet<T> property name is the same as the entity class name.

Related

Cast object list to model

I am trying to create a generic method to get a list of records from different tables. Based on my internet research I thought this would work but have been unable to make it work. A lot of code is omitted for clarity:
The method:
public IEnumerable<object> GetEntityUpdates(string entity)
{
string query = "select * from " + entity;
IEnumerable<object> entityList;
entityList = db.Database.SqlQuery<object>(query).ToList();
return entityList;
}
This is then the calling code where I am attempting the cast which fails no matter how I spin it:
var theList = da.GetEntityUpdates("EntityName");
IEnumerable<EntityName> entityList = theList.Cast<EntityName>();
EntityName is a database model which corresponds to the select above.
InvalidCastException is then thrown.
"Unable to cast object of type 'System.Object' to type 'EntityName'."
Change your GetEntityUpdates to use generics. Entity Framework also has a Set method for DbContext that lets you access entities of a certain type. More info here: https://msdn.microsoft.com/en-us/library/gg696521(v=vs.113).aspx
public IEnumerable<T> GetEntityUpdates<T>() where T : class
{
return db.Set<T>().ToList();
}
Then to call it you just pass in your entity class as a type parameter:
var foo = da.GetEntityUpdates<EntityName1>();
var bar = da.GetEntityUpdates<EntityName2>();
The method you're using to extract information, SqlQuery<object>, relies on reflection in order to build your objects, not sure what framework is being used but would guess making your method generic would solve the problem
public IEnumerable<T> GetEntityUpdates<T>(string entity)
{
string query = "select * from " + entity;
return db.Database.SqlQuery<T>(query).ToList();
}

passing type of property to generic type

I have a generic class GenericClass<T>
for some reason I need to pass the generic type from another type like that:
say I have some classes from NormalClass1 to NormalClassN all of them have property say prop1 with different types
I need to do this
var type1 = typeof(NormalClass1).GetProperty("prop1").GetType();
and send type1 to new instance of GenericClass like that:
var instance = new GenericClass<type1>();
but an error has occurred that says that
Cannot implicitly convert type 'GenericClass<type1>' to 'GenericClass<T>'
how can I pass this type to GenericClass
There are multiple problems with your code.
First of all:
var type1 = typeof(NormalClass1).GetProperty("prop1").GetType();
will return the type PropertyInfo, not the type of the property. What you want is:
var type1 = typeof(NormalClass1).GetProperty("prop1").PropertyType;
Second of all you seem to have a conceptual issue with Generics, Types and TypeParameters.
Basically there is a difference between a Type variable (Type x = typeof(NormalClass1<>) and a generic Type parameter (the T in NormalClass<T>).
T is not much more than a placeholder for a Type. You can use typeof(T) to get the actual Type of T. Using typeof(x) on the other hand would result in a compulation error since x is a variable and not a Type. You would use x.GetType() instead.
You cannot create a generic type via a runtime type variable directly.
What you can do instead is creating the generic type via reflection.
The following example should illustrate how to do that
var genericTypeParameter = typeof(NormalClass1).GetProperty("prop1").PropertyType;
var genericBaseType = typeof(GenericClass<>);
var genericType = genericBaseType.MakeGenericType(genericTypeParameter);
var instance = Activator.CreateInstance(genericType);
As you can see, var instance would substitute to object instance. That has to be that way, since you can check the type of compile time. Best Practice would probably be to create a non generic base class for you generic class. You can than use the base class type and have at least a small amount of type checking at runtime, even if you have no chance to test the generic type parameter.
This would be what it would look like:
var instance = (GenericClassBase)Activator.CreateInstance(genericType);
You can only do it with Reflection:
var generic = typeof (GenericClass<T>).MakeGenericType(type1);
var instance = Activator.CreateInstance(generic);

Compile-Time Generic Type Mapping without Reflection

In C#, is there any way to map one generic type to another generic type during compile time? I'd like to avoid using Reflection for this question. For example, let's say I would like to have TypeA map to TypeB, and have something similar to the following code work:
private void List<U> GetItemList<T>() where T : class <== U is the destination type obtained by the compile-time mapping from T to U
{
Type U = GetMappedType(typeof(T)) <=== this needs to happen during compile-time
List<U> returnList = Session.QueryOver<U>().List();
return returnList;
}
private Type GetMappedType(Type sourceType)
{
if (sourceType == typeof(TypeA))
return typeof(TypeB);
}
I realize that since I'm using a method call to map the type, that it will not do the mapping during compile time, but is there another way that accomplishes what I'm trying to achieve, only during compile time? I know that the code above is not correct, but I hope you can see what I'm trying to go for.
In short, I'd like to know if there's a way to map one type to another and have the C# compiler know the type mapping so that the destination type can be used as a Generic Type Parameter to any method that takes a Generic Type Parameter. I'd like to avoid using Reflection.
As a side-question, if I do use Reflection for this, will it make the implementation very resource-heavy?
Yes dynamic would be the answer. I had the same issue recently where I had to switch repositories based on some values configured in the database.
var tableNameWithoutSchema = tableName.Substring(tableName.IndexOf(".", StringComparison.Ordinal) + 1);
var tableType = string.Format("Library.Namespace.{0}, Library.Name", tableNameWithoutSchema);
var instance = UnitofWork.CreateRepository(tableType, uoW);
CreateRepository returns a dynamic type
public static dynamic CreateRepository(string targetType, DbContext context)
{
Type genericType = typeof(Repository<>).MakeGenericType(Type.GetType(targetType));
var instance = Activator.CreateInstance(genericType, new object[] { context });
return instance;
}
context was needed as I had to pass the context to Generic repository through constructor.
In my case this approach had some problems though.
May be this helps you.

C# - Use runtime-defined type as parameter in generic method

This is what i'm trying to do:
PropertyInfo[] propertyInfos = GetProperties(typeofEntity);
Type t = propertyInfos[0].GetType();
IList<t.GetType()> magicalList;
Let us say that t happens to be of type Int32, i then want the list to be
IList<Int32>
This doesn't work, as it's just the same as doing
IList<Type>
I don't want to write a dozen casts to manually find the Type.
Any Ideas?
Thanks
EDIT---------------
I'm doing this because i wanto to pass an object no an NHibernate query, and automatically create the criterias corresponding to the values of the object's properties.
Ex:
Person{
public string Name
public Phone Phone
}
Phone{
public int Number
}
I want to be able to create a person with a phone, and pass it in an nhibernate query, using DetachedFor<>. I then want to automatically create criterias for the properties of 'complex' properties of Person, such as Phone.Number.
You can only use generics with a known type at compile-time.
In your code, the expression magicalList[0] would have no compile-time type.
You can either use a non-generic IList or do everything with reflection.
Try:
var genericType = typeof (List<>).MakeGenericType(t.GetType());
var magicalList = Activator.CreateInstance(genericType);
There is the method System.Type.MakeGenericType wich help to create generic type passing the arguments. In your case you have the main type :
var oGenericType = typeof (IList<>);
var oSpecificType = oGenericType.MakeGenericType(typeof(int));

How to instantiate a generic class by using its type name?

In my project (.NET 3.5) I got many DAOs like this: (One for every entity)
public class ProductDAO : AbstractDAO<Product>
{...}
I need to create a function that will receive the name of the DAO or the name of its entity (whatever you think it's best) and run the DAOs "getAll()" function. Like this code does for just one entity:
ProductDAO dao = new ProductDAO();
dao.getAll();
I'm new to C#, how can I do that with reflection?
Someting like this:
String entityName = "Product";
AbstractDAO<?> dao = new AbstractDAO<entityName>()
dao.getAll();
Edit
One detail that I forgot, this is how getAll() returns:
IList<Product> products = productDao.getAll();
So I would also need to use reflection on the list. How?
Solution
Type daoType = typeof(AbstractDAO<>).Assembly.GetType("Entities.ProductDAO");
Object dao = Activator.CreateInstance(daoType);
object list = dao.GetType().GetMethod("getAll").Invoke(dao, null);
If you are using generics and don't want to implement a specific DAO for each entity type, you can use this:
Type entityType = typeof(Product); // you can look up the type name by string if you like as well, using `Type.GetType()`
Type abstractDAOType = typeof(AbstractDAO<>).MakeGenericType(entityType);
dynamic dao = Activator.CreateInstance(abstractDAOType);
dao.getAll();
Otherwise, just do a Type.GetType() with the computed name of the DAO (assuming that you follow a certain convention for the names).
Try:
Type d1 = typeof(AbstractDAO<>);
Type[] typeArgs = {Type.GetType("ProductDAO")};
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed);
o.GetType().GetMethod("getAll").Invoke();

Categories

Resources