上下文。我在C#中开发了一个O/R映射,在那里我需要在集合中"急切地加载"相关的表。我的数据库是一个专有系统,但在模拟SQL中,这就是我所做的:
select * from primarytable
(我得到ID为1、2、3、4的对象(
select * from relatedtable where primaryid in (1, 2, 3, 4)
这个系统中的查询很昂贵,所以我只能为每个表提供一个查询,这导致了我的问题。为了提供上下文,有一个通用类
class Repository<T>
O/R映射逻辑和数据库耦合所在的位置。这是每个给出的模型的子类
class MyModel : Model
(其中每个映射的数据库列由C#属性表示(
class MyModelRepository : Repository<MyModel>
(负责持久化MyModel实例(
因为我事先不知道我需要访问哪些列/属性,所以Repository<T>
中有一些反映。到目前为止,这一直有效,但现在我遇到了麻烦。
问题。我需要实现的方法是:
// in Repository<T>
public void LoadMultiRelation(IEnumerable<T> resources, Type modelType, Type repoType)
其中modelType是相关模型的类型(子类化model(,repoType是该模型的存储库的类型(存储库的子类(。子资源由父Model类上的IEnumerable<T>
属性表示。
无论我怎么做,我都会遇到类型的转换错误。无法将IEnumerable<Model>
转换为IEnumerable<MyModel>
。任务是用类似的反射完成的
propertyOnParent.SetValue(theParent, collectionOfChildren)
此时childrenCollection的静态类型是IEnumerable<Model>
,因为我不知道用户可能在代码中定义什么子类。该代码应该适用于完全通用的表模式。
很抱歉问了这么长的问题,我很乐意提供更多信息,也很乐意为您提供任何建议/建议。
可能有更好的方法可以做到这一点,但这里是我过去使用的模式:
public static class Utility
{
public static void SetProperty(PropertyInfo property, object target, IEnumerable value)
{
var valueType = property.PropertyType.GetGenericArguments()[0];
var genericConvertMethod = ConvertMethod.MakeGenericMethod(valueType);
object correctlyTypedValue = genericConvertMethod.Invoke(null, new [] {value});
property.SetValue(target, correctlyTypedValue);
}
private static readonly MethodInfo ConvertMethod = typeof(Utility).GetMethod("Convert",BindingFlags.Static|BindingFlags.NonPublic);
private static IEnumerable<T> Convert<T>(IEnumerable source)
{
// ToList is optional - depends on the behavior you want
return source.OfType<T>().ToList();
}
}
类型转换规则在反射下与普通代码中的规则相同。它们有时看起来不同的原因是,当您使用反射设置值时,设置代码总是使用实例的"真实"类型,而不是引用的类型。所以,虽然你不能写这样的代码:
IEnumerable<Base> a = new List<Derived>();
IEnumerable<Derived> b = a; // Cannot implicitly convert type!
你可以这样写代码:
IEnumerable<Base> a = new List<Derived>();
PropertyInfo prop = /* get a PropertyInfo which points to a static property of type IEnumerable<Derived> */;
prop.SetValue(null, a);
因为属性设置器将a
视为List<Derived>
。这可能会造成一种错觉,即反射代码有时遵循与普通类型转换不同的一组规则。
您无法将IEnumerable<MyClass>
转换为IEnumerable<MyChildClass>
,因为在该接口的声明中:
public interface IEnumerable<out T> : IEnumerable
类型参数CCD_ 17是协变的。这意味着,您可以将IEnumerable<MyChildClass>
转换为IEnumerable<MyClass>
,但不能反过来
要从IEnumerable<MyClass>
获得IEnumerable<MyChildClass>
,可以使用OfType<TResult>
Linq扩展方法:
class MyClass { }
class MyChildClass : MyClass { }
//...
IEnumerable<MyClass> myClassList;
//...
IEnumerable<MyChildClass> myChildClassList = myClassList.OfType<MyChildClass>();
//...
所以,你需要做一些类似上面的反思,就像Steve Ruble在他的回答中提供的例子一样。