How to get and use class/entity type by reflection? - c#

I have these entities that are being called at runtime and I need to be able to return an IQueryable<EntityObject> based on the entity type being called that particular time by string. Let's say the entity is types of food and the class name is Food, so...
return Repository.Read<Food>(); //this is what I am trying to accomplish
However, I don't know that it is Food until runtime and is such only given as a string, so I use reflection:
Type t = Type.GetType(lookupEntityName); //where lookupEntityName = "Food"
How can I use this Type t to replace 'Food' in the original line of code from above:
return Repository.Read<HERE>(); // use 't' to repalce "HERE"

Supposing that your method only contains a return statement (because you have only posted that), you could do something like this (warning: this wasn't tested):
public static IQueryable<T> Read<T>()
{
return Repository.Read<T>();
}
And you could use it like this:
IQueryable<Food> food = Read<Food>();

You have to use the MethodInfo.MakeGenericMethod Method to create your generic method at runtime.
var openMethod = typeof(Repository).GetMethod(nameof(Repository.Read), ...);
var closedMethod = openMethod.MakeGenericMethod(t);
return closedMethod.Invoke(...);

If you need to call a generic method, you must get the MethodInfo of that function and create a generic MethodInfo for the appropiate type.
This is a helper function to do this:
public MethodInfo GetGenericMethod(string MethodName, Type SourceType, Type TargetType)
{
var mInfo = SourceType.GetMethod(MethodName);
return mInfo.MakeGenericMethod(TargetType);
}
And now you can do this:
Type t = Type.GetType(lookupEntityName);
Type tSrc = typeof(Repository);
var result = GetGenericMethod("Read", tSrc, t).Invoke(Repository);
If Read is an static method then pass null to the invoke.

Related

X is a variable but used like a type when trying to cast

I am passing a string of the name of the entity type I want to query and getting the type based on the string. I want to get the DbSet back and return an IQueryable. The problem is where I am doing (DbSet<tableEntity>) and getting the following error:
tableEntity is a variable but used like a type
when trying to cast. Is there a way to resolve this?
public object GetList(string tableEntity)
{
Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");
var dbObject = (DbSet<tableEntity>)typeof(DbContext).GetMethod("Set", Type.EmptyTypes)
.MakeGenericMethod(tableEntity)
.Invoke(databaseContext, null);
return dbObject.AsQueryable();
}
EDIT
Just to add I don't have access to the type that's we I am passing the name through a string.
So it turns out that the entity type is literally not known, or knowable, at compile time. It has to be a string.
The only place you're using the type at compile time is in the cast to (DbSet<tableEntity>). Well, you may not need that. All you need from that type is to call AsQueryable(), and AsQueryable() is an extension method for IEnumerable, with generic and non-generic versions. IF we call it through non-generic IEnumerable, that's non-generic AsQueryable(), returning non-generic IQueryable. But we're returning object anyway, so hey. For the result of this thing to be useful, something somewhere must be doing a fair amount of reflection on it anyway, so the declared type is likely to be of little consequence.
See if this works:
public object GetList(string typeName)
{
Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");
var dbObject = (System.Collections.IEnumerable)
typeof(DbContext).GetMethod("Set", Type.EmptyTypes)
.MakeGenericMethod(tableEntity)
.Invoke(databaseContext, null);
return dbObject.AsQueryable();
}
If it turns out you need generic IQueryable<TEntityType>, we'll have to use reflection to get MethodInfo for AsQueryable<TEntityType> for the unknown (at compile time) entity type, and call MakeGenericMethod(tableEntity) on that.
First try:
In the language, type parameters to generics must be actual types, not instances of the Type class. That's because they're resolved at compile time.
But that's no problem; to pass a type parameter to a generic method, simply write a generic method with a type parameter.
You can't do this:
var stuff = GetList("MyTableEntityClass");
But this is just as good:
var stuff = GetList<MyTableEntityClass>();
...
public object GetList<TTableEntity>()
{
var dbObject = (DbSet<TTableEntity>)typeof(DbContext)
.GetMethod("Set", Type.EmptyTypes)
.MakeGenericMethod(typeof(TTableEntity))
.Invoke(databaseContext, null);
return dbObject.AsQueryable();
}
Reflection is different; that's why we pass typeof(TTableEntity) to MakeGenericMethod().
And once we're using an actual type that the compiler can check, we can do better with our return type, too:
public IQueryable<TTableEntity> GetList<TTableEntity>()
{
var dbObject = (DbSet<TTableEntity>)typeof(DbContext)
.GetMethod("Set", Type.EmptyTypes)
.MakeGenericMethod(typeof(TTableEntity))
.Invoke(databaseContext, null);
return dbObject.AsQueryable();
}
Since, as Ed mentioned, you don't use the table entity type at compile time, why not just use the non-generic databaseContext.Set (tableEntity).AsQueryable ()? But if you've set your heart on Set<>, try this:
public object GetList(string tableEntity)
{
Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");
return GetType ()
.GetMethod ("GetListHelper")
.MakeGenericMethod (tableEntity)
.Invoke (this) ;
}
public object GetListHelper<T> () where T : class
{
var dbObject = databaseContext.Set<T> (null) ;
return dbObject.AsQueryable();
}

passing a type to IDBConnection Get < T > ()

Is it possible to pass a type during runtime to IDBConnection.Get<T> extension?
For example, I have classes like Countries, States, Cities, and an object variable instance containing either of those. And I want to pass the class type to Get<T> method
Pseudocode :
var myobject = _somedata; //it could be of type countries, states, cities, etc...
var q = sqlConn.Get<typeof(myobject)>()
Is there anyway to achieve this ?
You can, but you don't want to. The resulting code is nasty.
Instead, try writing your own extension method that uses the type of an argument.
public static T GetLike<T>(this IDbConnection conn, T likeType)
{
return conn.Get<T>();
}
Then you can simply call
var q = sqlConn.GetLike(myobject);
And the anonymous type will be inferred by the compiler.

Returning desired type from Activator.CreateInstance() instead of object

I'm trying to create an instance of specified Type whatever user wants to have. For a quick illustration of my purpose please see the code below:
static void Main(string[] args)
{
object o = GetInstance(typeof(int));
Console.WriteLine("Created type: {0}", o.GetType().FullName);
}
public static object GetInstance(Type t)
{
Console.WriteLine("Creating instance of {0}", t.FullName);
return Activator.CreateInstance(t);
}
The problem is Activator.CreateInstance() returns object by default. There is also an overload of this method like T Activator.CreateInstance<T>() which is parameterless and returns the type you specify as T.
However, the problem is T should be hard-coded while calling this method and thus should be a fixed value. I am trying to create an instance of desired class and return it as its type.
Right now if you use this method you should write something like:
int i = GetInstance(typeof(int)) as int
I'm trying to reduce this to:
int i = GetInstance(typeof(int))
Is there a way that I can do casting inside the GetInstance and get rid of that as int repetition? By this way, my return type (and also the type I cast the object to) will be unknown at compile time.
Seemed impossible by design to me but I'd really appreciate if you figure it out.
EDIT: Where I'm stuck is e.g. while you're casting, you can do return (T) result if you are in a generic method, but you can't do Type t = ...; return (t) result this doesn't work. You cannot cast to a type which is passed to you as a parameter which is not known at compile time.
Follow a known pattern
This is not a new problem. It is a problem facing any API that allows type-specific return values. For example, a JSON parsing library like Newtonsoft (which is, to wit, the single most popular .NET package downloaded by .NET programmers in 2019) must be able to parse a string and return a type-specific object, which may or may not be known at compile time. It might make sense to follow their example.
Newtonsoft exposes three ways to specify the type when deserializing. You could do as you are currently doing:
//Cast required
var result = JsonConvert.DeserializeObject(text, typeof(MyType)) as MyType;
You can use a generic method:
//No cast required, but you have to hardcode a type as a type parameter
var result = JsonConvert.DeserializeObject<MyType>(text);
Or you can use an instance as a template, which is great for anonymous types, although you can use it with non-anonymous classes as well. This one works via generic type inference:
//No cast required and no need to specify type; the type is inferred from the argument
var result = JsonConvert.DeserializeAnonymousType(text, new MyType());
Here's how you'd do it:
So for you to make this work, your code might look like this:
public object GetInstance(Type type)
{
return Activator.CreateInstance(type);
}
int i = GetInstance(typeof(int)) as int;
public T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
int i = GetInstance<int>();
public T GetInstance<T>(T template)
{
return Activator.CreateInstance<T>();
}
int i = GetInstance(0);
If you do it this way, it's hard to imagine any programmer would have trouble using your library, as the approach should already be familiar to them.
Actually you could write GetInstance like this:
static T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
And use it:
int j = GetInstance<int>();
This might help you to create instance of desired type:
public class ConcreteFactory<T> : AbstractFactory<T>
{
public override T CreateInstance(string typeName,params object[] parameters)
{
var path = Assembly.GetExecutingAssembly().CodeBase;
var assembly = Assembly.LoadFrom(path);
var type = assembly.GetTypes().SingleOrDefault(t => t.Name == typeName);
return (T)Activator.CreateInstance(type, parameters);
}
}
Key here is generic type T can be used to cast the created instance, this can be used as a template to create instance of any type with parameterized constructor

How can I send Type variable to the method which is generic?

I have a method like this,
public List<T> Test<T>()
{
// do something.
}
I don't know what is T and dont have. But I have type of T as TYPE.
for example:
class Person
{
}
var type = typeof(Person);
I don't have Person. Person is keeping at the type object.
How can I use the test method ?
var list = Test<type>(); // It gives an error like this. I must use the type object.
You can use the MakeGenericMethod method from MethodInfo:
MethodInfo info = this.GetType().GetMethod("Test").MakeGenericMethod(type);
object result = info.Invoke(this, null);
This is assuming you call the method inside the same type that defines Test. If you call it from somewhere else, use typeof(ClassThatDefinesTest) instead of this.GetType(), and the instance of this class instead of this.
If typeof(T) is all you really needed, then you can refactor it to:
public void Test(Type t)
{
// do something.
}
And call it like:
Test(type);
If this won't work for you, I recommend Botz3000's solution using MakeGenericMethod.
You could expose both Test<T>() and Test(Type) and then have one call the other, (either with MakeGenericMethod or typeof(T)) depending on whether you need the static type or simply the runtime type. That way your callers don't need to know which of the two you need.
You have pass the Class name to it. Please refer to the following MSDN,
http://msdn.microsoft.com/en-us/library/twcad0zb(v=vs.100).aspx
As said #Botz3000 you can use MakeGenericMethod() method. But another solution could be use the dynamic keyword and the CreateInstance() method from Activator class:
public void Test(Type type)
{
dynamic myVariable = Activator.CreateInstance(type);
// do something, like use myVariable and call a method: myVariable.MyMethod(); ...
}
You call it like this:
Test<Person>();

Typecast to a type from just the string representation of the type name

sTypeName = ... //do some string stuff here to get the name of the type
/*
The Assembly.CreateInstance function returns a type
of System.object. I want to type cast it to
the type whose name is sTypeName.
assembly.CreateInstance(sTypeName)
So, in effect I want to do something like:
*/
assembly.CreateInstance(sTypeName) as Type.GetType(sTypeName);
How do I do that? And, what do I take on the left side of the assignment expression, assuming this is C# 2.0. I don't have the var keyword.
Usually you let all classes, you want to instantiate this dynamically, implement a common interface, lets say IMyInterface. You can create an instance from the classname string like this:
Assembly asm = Assembly.GetExecutingAssembly();
string classname = "MyNamespace.MyClass";
Type classtype = asm.GetType(classname);
// Constructor without parameters
IMyInterface instance = (IMyInterface)Activator.CreateInstance(classtype);
// With parameters (eg. first: string, second: int):
IMyInterface instance = (IMyInterface)Activator.CreateInstance(classtype,
new object[]{
(object)"param1",
(object)5
});
Even if you dont have a common interface, but know the name of the method (as string) you can invoke your methods like this (very similar for properties, event and so on):
object instance = Activator.CreateInstance(classtype);
int result = (int)classtype.GetMethod("TwoTimes").Invoke(instance,
new object[] { 15 });
// result = 30
The example class:
namespace MyNamespace
{
public class MyClass
{
public MyClass(string s, int i) { }
public int TwoTimes(int i)
{
return i * 2;
}
}
}
Unfortunately there's no way in .NET to do what you want.
Possible partial solutions are:
If you know the type at compile-time (unlikely, since you're creating it at run-time from a string) then simply cast to that type:
YourType t = (YourType)Activator.CreateInstance(sTypeName);
If you know that all the possible types will implement a specific, common interface then you can cast to that interface instead:
IYourInterface i = (IYourInterface)Activator.CreateInstance(sTypeName);
If you can't do either of the above then, unfortunately, you're stuck with object and reflection.
.
Define a generic method in your class, and then you can cast like this:
public T Cast<T>(object obj)
{
return (T) obj;
}
string sTypename = "SomeClassName";
MethodInfo cast = this.GetType().GetMethod("Cast");
MethodInfo genericCast = cast.MakeGenericMethod(new Type[] { Type.GetType(sTypename) });
Object castedValue = genericCast.Invoke(this, new object[] { instanceToBeCasted });
But then I think, what is the point of such casting if you cannot store the casted value in a variable of the actual type, precisely because you don't know the actual type at the time of writing the code?

Categories

Resources