等同于NHibernate实体的实施,无氧问题

本文关键字:问题 NHibernate 实体 等同于 | 更新日期: 2023-09-27 17:50:24

在NHibernate 3.0 Cookbook中,有一个基本实体类型的示例实现。平等是这样实现的:

public abstract class Entity<TId>
{
  public virtual TId Id { get; protected set; }
  public override bool Equals(object obj)
  {
    return Equals(obj as Entity<TId>);
  }
  private static bool IsTransient(Entity<TId> obj)
  {
     return obj != null && Equals(obj.Id, default(TId));
  }  
  private Type GetUnproxiedType()
  {
     return GetType();
  }  
  public virtual bool Equals(Entity<TId> other)
  {
    if (other == null) return false;            
    if (ReferenceEquals(this, other)) return true;
    if (!IsTransient(this) && !IsTransient(this) && Equals(Id, other.Id))
    {
      var otherType = other.GetUnproxiedType();
      var thisType = GetUnproxiedType();
      return thisType.IsAssignableFrom(otherType) ||
         otherType.IsAssignableFrom(thisType);
    }
    return false;
  }    
}

GetUnboxiedType((方法的原因是:有一个抽象基类Product,一个从Product继承的具体类Book,以及一个由NHibernate用于延迟加载的动态代理类ProductProxy。如果表示Book的ProductProxy和具体Book具有相同的Id,则应将它们视为相等。然而,我真的不明白为什么在这种情况下对ProductProxy实例调用GetType((应该返回Product,以及它有什么帮助。有什么想法吗?

等同于NHibernate实体的实施,无氧问题

实际上,我已经写信给这本书的作者介绍了这段代码。事实证明,这是由于代理包装的工作方式。以下是他的回应:

"如果你不了解代理框架是如何工作的,这个想法可能看起来很神奇

当NHibernate出于延迟加载的目的返回代理时,它会返回从实际类型继承的代理实例。我们可以在不强制从数据库加载的情况下访问一些成员。其中包括代理的Id属性或字段GetType(),以及在某些情况下的Equals()GetHashCode()。访问任何其他成员将强制从数据库加载。

当这种情况发生时,代理会创建一个内部实例。因此,例如,Customer(CustomerProxy102987098721340978(的延迟加载实例在加载时,将使用数据库中的所有数据在内部创建一个新的Customer实例。代理然后做这样的事情:

public overrides string Name 
{ 
    get { 
       return _loadedInstance.Name; 
    } 
    set { _loadedInstance.Name = value; } 
}

顺便说一句,正是这种覆盖要求允许延迟加载的实体上的所有内容都是虚拟的。

因此,对代理上Name属性的所有调用都会中继到具有实际数据的内部Customer实例。

GetUnproxiedType()利用了这一点。在代理上对GetType()的简单调用将返回typeof(CustomerProxy02139487509812340)。对GetUnproxiedType()的调用将中继到内部客户实例,内部客户实例将返回typeof(Customer)。">

使用当前的(v5.x(NHibernate代理工厂(静态或动态,自v5.1以来可用的静态(,这种模式实际上已经被打破。v5内置的代理工厂不拦截私有方法。

我认为v4已经是这样了。

为了使该模式与当前内置的代理工厂一起工作,GetUnproxiedType应该是virtual(所以顺便说一句,不是private,而是protected(。

否则,请使用NHibernateUtil.GetClass,这是针对此目的的,不依赖于脆性技巧。它的文档警告说,它将通过副作用初始化代理,但无论如何,GetUnproxiedType技巧也必须这样才能工作
当然,使用NHibernateUtil.GetClass意味着在域模型基类中直接依赖于NHibernate。但是,在我看来,依赖于特定于外部(从领域的角度(库实现的实现技巧并没有更好。

此外,一些更改可能会导致GetUnproxiedType技巧在未来更加失败,比如一些减少导致代理在可以避免的情况下初始化的情况的想法。(请参阅此处的示例。(

如果你真的想要一个不依赖于直接NHibernate引用的GetUnproxiedType方法;"安全";解决方案是在每个具体实体类中对其进行抽象和重写,以生成typeof(YourEntityClass)。但在实践中,它会很麻烦,而且容易出错(创建新实体时复制粘贴错误,忘记更改该方法…(,而抽象部分在某些具体实体类通过继承进一步专业化的情况下没有帮助。

另一个技巧是,根据GetType获得的类型,检查它属于哪个程序集(代理类型将不属于某个程序集(,以便搜索层次结构中属于域模型程序集的第一个类型
请注意,如果代理是基类的代理,而不是具体类的代理,并且您的helper方法设置为private,则它将生成基类类型,而不初始化代理。就性能而言,这更好。虽然虚拟GetUnproxiedType简单地返回GetType将返回具有当前代理工厂的具体类类型,但它也将初始化代理。

我们使用NH2,这个例子对我们不起作用。它说,具有相同id的两个实体是不平等的,其中一个是代理(组织(,另一个不是(组织(。当我们有一个层次结构时:

class Organization
class AOrganization : Organization
class COrganization : Organization
{
  public virtual COrganization GetConcrete()
  {
    return null;
  }
}
class DOrganization : COrganization
{
  public virtual COrganization GetConcrete()
  {
    return this;
  }
}
AOrganization aOrganization;
COrganization cOrganization;
contract = new CContract(aOrganization, cOrganization as COrganization); //(COrganization)(cOrganization.GetConcrete()),

因此,CContract有一个类型为"组织"的领域。带设置

public class Contract: Entity <short>
{
    public virtual COrganization COrganization
    {
        get { return cOrganization; }
        protected internal set
        {
            if (cOrganization != null && value != cOrganization) // != calls ==, which calls Equals, which calls GetUnproxiedType()
                    throw new Exception("Changing organization is not allowed.");
            }
            cOrganization = value;
        }
    }
    private COrganization cOrganization;
}

我们构建了新的合同,它的构造函数设置了指向某个组织的组织字段。然后我们调用了UnitOfWork.Commit,NH试图再次设置COorganization字段(使用相同的id(,GetUnboxiedType工作不正确,新值和旧值被识别为不相等,并抛出异常。。。

这是出现错误的地方:

            var otherType = other.GetUnproxiedType();
            var thisType = GetUnproxiedType();
            return thisType.IsAssignableFrom(otherType) ||
            otherType.IsAssignableFrom(thisType);

在调试器中:otherType==COOrganizationProxy-GetUnboxiedType失败thisType==D组织

CoorganizationProxy和DOrganization都继承了Coorganization。所以它们不是彼此的IsAssignableFrom。。。

为什么这个例子对你有用?

也许是因为我们有NH 2.0或2.1?

还是因为简单的"cOrganization as COOrganization">而不是的"(COOrganization((cOrganization.GetConcrete(((">

或者因为我们实现了==,!=和Equals不仅在实体中,而且在组织中?

public abstract class Organization : Entity<int>
{
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
    public static bool operator ==(Organization object1, Organization object2)
    {
        return AreEqual(object1, object2);
    }
    public static bool operator !=(Organization object1, Organization object2)
    {
        return AreNotEqual(object1, object2);
    }
}
public abstract class Entity<TId>
{
    public virtual TId Id { get; /*protected*/ set; }
    public override bool Equals(object obj)
    {
        return Equals(obj as Entity<TId>);
    }
    private static bool IsTransient(Entity<TId> obj)
    {
        return obj != null &&
        Equals(obj.Id, default(TId));
    }
    private Type GetUnproxiedType()
    {
        return GetType();
    }
    public virtual bool Equals(Entity<TId> other)
    {
        if (other == null)
            return false;
        if (ReferenceEquals(this, other))
            return true;
        if (!IsTransient(this) &&
        !IsTransient(other) &&
        Equals(Id, other.Id))
        {
            var otherType = other.GetUnproxiedType();
            var thisType = GetUnproxiedType();
            return thisType.IsAssignableFrom(otherType) ||
            otherType.IsAssignableFrom(thisType);
        }
        return false;
    }
    public override int GetHashCode()
    {
        if (Equals(Id, default(TId)))
            return base.GetHashCode();
        return Id.GetHashCode();
    }
    /// This method added by me
    /// For == overloading
    protected static bool AreEqual<TEntity>(TEntity entity1, TEntity entity2)
    {
        if ((object)entity1 == null)
        {
            return ((object)entity2 == null);
        }
        else
        {
            return entity1.Equals(entity2);
        }
    }
    /// This method added by me
    /// For != overloading
    protected static bool AreNotEqual<TEntity>(TEntity entity1, TEntity entity2)
    {
        return !AreEqual(entity1, entity2);
    }
}