OfType<>;与NHibernate代理类玩得不好

本文关键字:代理 NHibernate lt gt OfType | 更新日期: 2023-09-27 17:58:06

我有类似于以下的类:

public abstract class Base { }
public class Concrete : Base { }
public class Cement : Base { }

以及以下代码

var bases = someEntity.Bases; // returns two Concrete and once Cement
// bases now contains:
// - ProxyBase : Base
// - Concrete : Base
// - Cement : Base
var concretes = bases.OfType<Concrete>(); 
// concretes only contains one Concrete (i.e. the ProxyBase is ignored)

如何在不污染我的课堂的情况下获得两个Concrete的期望结果?

如果可能的话,我也不想强迫映射不使用延迟加载。

OfType<>;与NHibernate代理类玩得不好

这无疑是动态代理的一个痛点。您可以将Self属性添加到基类中,这样您就可以获得对未装箱对象的引用:

public virtual Base Self
{
    get { return this; }
}

然后,您可以通过检查类型来获得特定类型的具体类:

var concretes = bases.Where(b => b.Self is Concrete);

您还可以创建一个扩展方法,使OfType的行为符合预期:

public static IEnumerable<T> OfType<T>(this IEnumerable<Base> bases) where T : Base
{
    return Enumerable.OfType<T>(bases.Select(b => b.Self));
}

您必须先取消它的氧气,才能获得具体的类型。

public static T Unproxy<T>(this T obj, ISession session)
{
    if (!NHibernateUtil.IsInitialized(obj))
    {
        NHibernateUtil.Initialize(obj);
    }
    if (obj is INHibernateProxy)
    {    
        return (T) session.GetSessionImplementation().PersistenceContext.Unproxy(obj);
    }
    return obj;
}

然后

foreach(var base in bases)
      Unproxy<Concrete>(base);

所以我找到了一个临时解决方案,但这是脆弱的(如果ToString被覆盖,那么它就会崩溃,其他ORM可能会以不同的方式工作),而且绝对不能满足

var concreteTypeName = typeof(Concrete);
// yuck
var concretes = bases.Where(x => String.Equals(x.ToString(), concreteTypeName));

如果您可以使用INHibernateProxy,请尝试以下操作:

var concretes = bases.Where(x => {
        var proxy = (INHibernateProxy)x;
        return proxy.HibernateLazyInitializer.PersistentClass == typeof(Concrete);
    });

或者正如Kirk Woll所说:

var concretes = bases.Where(x => x.GetType().BaseType == typeof(Concrete));

在测试正确的类型时,代理是一个难题。这个答案涵盖了所有不同的解决方案。

  1. 一种解决方案是使用no-proxy延迟加载,这应该可以做到!(说明)
  2. 在我看来,如果你不能使用no-proxy(出于任何原因),Jamie Ide的答案是最好的解决方案

祝你好运!