【问题标题】:How do I convert an object from Reflection to a generic collection?如何将对象从反射转换为通用集合?
【发布时间】:2010-12-15 06:38:33
【问题描述】:

我正在尝试编写一个比较方法来使用反射比较某些 POCO 中的属性,以确保它们已正确保存到数据库中。例如,假设我有这个 POCO:

public class NoahsArk
{
    public string Owner { get; set; }
    public ICollection<Animal> Animals { get; set; }
}

我想做的是这样的:

[Test]
public class Saves_Correctly_To_Database()
{
    var noahsArk = new NoahsArk { // some setup code here };
    db.Save(noahsArk);
    var dbNoahsArk = db.Get<NoahsArk>(noahsArk.Id);

    Assert.That(Compare(noahsArk, dbNoahsArk), Is.True);
}

我使用的 ORM 是 NHibernate。到目前为止,我的比较方法如下所示:

public static bool EqualsProperties<T>(this T x, T y)
{
    var xType = x.GetType();

    foreach (var property in xType.GetProperties())
    {
        if (property.GetValue(x, null).Implements(typeof(ICollection<>)))
        {
            var xValue = property.GetValue(x, null) as ICollection<T>;
            var yValue = property.GetValue(y, null) as ICollection<T>;
        }

Object.Implements() 是我编写的一个扩展方法,用于确定一个类型是否实现了一个接口。如您所见,该方法是不完整的。我遇到的问题是,当我使用property.GetValue(x, null) 时,它返回一个object,我不知道如何将其转换为特定的通用ICollection 类型。我需要能够做到这一点,以便我可以使用 LINQ 执行 x.Contains(y) 来比较两个集合是否相等。关于如何做到这一点的任何想法?

附:我尝试使用Compare .NET Objects,但它在NHibernate 深处的某个地方给了我一个空引用异常。它不能正确处理 NHibernate 如何代理 ICollection 以进行延迟加载。更糟糕的是,NHibernate 修改了 POCO 以支持延迟加载,但这都是在运行时完成的。在源代码中,看起来您只是在使用常规的 ICollection,但 NHibernate 在运行时将其更改为 NHibernate.Collections.Generic.PersistentSet,这就是导致比较器失败的原因。

【问题讨论】:

    标签: nhibernate reflection fluent-nhibernate comparison poco


    【解决方案1】:

    您的问题有点令人困惑,因为您在 EqualsProperties 方法的声明中不需要类型参数 T。你只需要

    public static bool EqualsProperties(this object x, object y)
    

    然后继续使用相同的参数 T 将 x 和 y 的属性转换为 ICollection&lt;T&gt;;然而,这些集合中的对象显然可能具有不同于 x 和 y 的 类型。

    现在回答您的问题:您无需转换为正确的泛型类型即可使用 LINQ Contains 方法。你可以这样做:

    xValue = property.GetValue(x, null);
    yValue = property.GetValue(y, null);
    if (typeof(IEnumerable).IsInstanceOf(x))
    {
       IEnumerable<object> xEnumerable = (x as IEnumerable).Cast<object>();
       IEnumerable<object> yEnumerable = (y as IEnumerable).Cast<object>();
       // use any LINQ method you like now
    }
    

    您还应该确保使用带有相等比较器的 LINQ 重载,因为您的域对象显然不会覆盖 Equals 方法本身。否则,您将不会编写此单元测试代码来比较它们。

    【讨论】:

    • 感谢您的回答。我在声明中使用类型参数 T 背后的想法是,这是作为一个隐式的第一次检查,你必须传递两个相同类型的对象,否则编译器会说它不能隐式地将其中一个对象转换为类型T.
    【解决方案2】:

    Sharp architecture framework 使用属性来装饰属性,这些属性应该被带入 equals 方法中。参见DomainSignatureAttribute类和EntityWithTypedId&lt;&gt;.Equals方法的源码。

    【讨论】:

      猜你喜欢
      • 2017-07-18
      • 1970-01-01
      • 2019-08-17
      • 2018-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-31
      相关资源
      最近更新 更多