Using Entity Framework with abstract classes
Have a very interesting situation and can't seem to find anything concrete on the webs surrounding reflection, inheritance and generics.
To start off with:
I have a base abstract classed, called:
public abstract class ItemBase
{
// default properties
}
ItemBase then becomes my super-开发者_StackOverflow中文版class to an abstract entity called Item:
public abstract class Item
{
// some extra properties
}
The idea behind this is to construct entities that will inherit from Item with additional properties set:
public partial class City : Item {}
Now, in a test method I'm trying to use reflection to invoke a method on the passed entity and return whatever data, in what ever form, back from the method.
Assuming, for testing purposes we have a method in State, that returns a collection of all major cities:
public List<City> GetAllMajorCities() {}
In my stubs I might have a test to get all cities:
List<Item> cities = this.InvokeGenericMethod<Item>("State", "GetAllMajorCities", args);
Then the InvokeGenericMethod(string, string, object[] args) looks something like:
public IEnumerable<T> InvokeGenericMethod<Item>(string className, string methodName, object[] args)
{
Type classType = className.GetType(); // to get the class to create an instance of
object classObj = Activator.CreateInstance(classType);
object ret = classType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, classObj, args);
}
In theory, from the example given, the value of "ret" should be a type of IEnumerable, however, the returning type, if added to a watch to investivage, return List, if I check the ret value as:
if (ret is IEnumerable<T>)
it ignores the check.
If I change
public List<City> GetAllMajorCities() {}
to
public List<ItemBase> GetAllMajorCities() {}
It seems to adhere to the inheritance and allow me to cast it to IEnumberable.
I'm completely stumped here.
Any ideas, recommendations would be greatly appreciated.
Thanks,
Eric
Is it possible that you are using Framework 3.5? Generic co- and contravariance was introduced in .net 4.0. Thus, the following is not possible in Framework 3.5:
class ItemBase { }
class Item : ItemBase { }
class City : Item { }
class Program
{
static void Main(string[] args)
{
IEnumerable<City> cities = new List<City>();
IEnumerable<Item> items = cities; // Compile error here. IEnumerable<Item> is not a supertype of IEnumerable<City>.
}
}
Note that the following (which is what you are trying to achieve) won't even work in .net 4.0:
List<Item> cities = new List<City>();
Why doesn't that work? Because it must be possible to add arbitrary Item
s to a List<Item>
. But, obviously, cities.Add(new Person());
cannot work (since the "real" (= static) type of cities
is List<City>
). Thus, List<Item>
can never be a supertype of List<City>
.
You cannot add items to an IEnumerable, which is why there is a subtype relationship between IEnumerable<City>
and IEnumerable<Item>
(but only in .net 4.0, as mentioned above).
You could try something like this:
Type type = ret.GetType();
Type listType = typeof(IEnumerable<T>);
if (type == listType || type.IsSubclassOf(listType))
{
//
}
First of all, this is wrong:
Type classType = className.GetType();
This construct will yield the type of className
which is String
but not the type whose name is stored in className
.
A better, more type-safe way would be:
public IEnumerable<TItem> InvokeGenericMethod<TItem, TContainer>(...)
where TItem : ItemBase
where TContainer : class, new()
{
TContainer container = new TContainer();
...
}
Update: In case the TContainer
type is not known statically at the place where InvokeGenericMethod
is called, have a look on of the the static Type.GetType
methods to resolve the actual name from its string name. In the simplest case the Type.GetType(String)
method access an assembly qualified name of your type, e.g. "MyNamespace.City, MyAssembly"
.
精彩评论