O/R映射中具有泛型集合的反射

本文关键字:泛型 集合 反射 映射 | 更新日期: 2023-09-27 18:16:58

上下文。我在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>,因为我不知道用户可能在代码中定义什么子类。该代码应该适用于完全通用的表模式。

很抱歉问了这么长的问题,我很乐意提供更多信息,也很乐意为您提供任何建议/建议。

O/R映射中具有泛型集合的反射

可能有更好的方法可以做到这一点,但这里是我过去使用的模式:

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在他的回答中提供的例子一样。