2011-04-16 6 views
10

En NHibernate 3.0 Cookbook, hay una implementación de muestra para un tipo de entidad base. Los iguales se implementa como esto:Igual implementación de NHibernate Entities, pregunta no operativa

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; 
    }  
} 

La razón para el método GetUnproxiedType() es la siguiente: Hay una clase base de productos abstracto, un hormigón libro de clase que hereda de producto y una ProductProxy clase proxy dinámico utilizado por NHibernate para la carga perezosa. Si un ProductProxy que representa un Libro y un Libro concreto tienen los mismos Id, deben tratarse como iguales. Sin embargo, realmente no veo por qué llamar a GetType() en una instancia de ProductProxy debería devolver el producto en este caso y cómo ayuda. ¿Algunas ideas?

+0

¿Puede adjuntar la implementación del método 'GetType()'? sin verlo, supongo que devuelve la clase subyacente "concreta" (por ejemplo, para ProductProxy, me imagino que debería devolver 'Producto' –

+0

El productProxy es una clase proxy dinámica creada por NHibernate, así que no tengo ni idea de la puesta en práctica .. pensé que tal vez alguien con una idea de cómo NHibernate crea proxies serían capaces de ayudar .. también GetType() no es virtual, de modo parece ser que su aplicación como "nuevo" en la clase de proxy no tendría ningún efecto en todos .. –

+0

Tengo el mismo libro y no soy un gran admirador de la implementación de Equals que se proporciona. Se basa demasiado en la persistencia de los objetos. El libro indica que si cualquiera de las entidades es transitoria (no guardada en el db) entonces este igual siempre devuelve falso. En primer lugar, creo que esto rompe la regla de ignorancia de persistencia y, en segundo lugar, qué sucede si quiero verificar la igualdad antes de guardar en la base de datos. Implemento parte de esto para la eficiencia (ch ecking the ids) pero aún implemento un "logical" Igual para todos mis objetos. – brainimus

Respuesta

6

De hecho, me adelanté y escribí al autor del libro sobre este código. Resulta que esto se debe a la forma en que funciona el proxy wrapping. He aquí su respuesta:

"Si no sabe cómo funcionan los marcos de proxy, la idea puede parecer mágico

Cuando NHibernate devuelve un proxy para los efectos de la carga diferida, devuelve una instancia del proxy heredada. del tipo real. Hay unos pocos miembros podemos acceder sin forzar una carga desde la base de datos. entre ellas se encuentran propiedad ID de proxy o de campo, GetType(), y en algunas circunstancias Equals() y GetHashCode(). el acceso a cualquier otro miembro obligará a una carga de la base de datos.

Cuando eso sucede, el proxy crea una instancia interna. Así, por ejemplo, una carga perezosa instancia de Customer (CustomerProxy102987098721340978), cuando se cargue, creará internamente una nueva instancia Customer con todos los datos de la base de datos. El proxy entonces hace algo como esto:

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

Por cierto, este es primordial que requiere que todo sea virtual en entidades que permiten cargado ligeramente.

Por lo tanto, todas las llamadas a la propiedad Name en el proxy se retransmiten a la instancia interna Customer que tiene los datos reales.

GetUnproxiedType() se aprovecha de esto. Una simple llamada a GetType() en el proxy devolverá typeof(CustomerProxy02139487509812340). Una llamada al GetUnproxiedType() se retransmitirá a la instancia del cliente interno, y la instancia del cliente interno devolverá typeof(Customer). "

+0

¿Esto significa que la simple comparación de dos entidades representadas provocará que se carguen desde la base de datos, aunque no se usen campos DB en la comparación, excepto el Id, que ya conocemos? –

+1

@Victor, sí lo hace. Puede probarlo cargando los objetos con Session.Load(). Probé un poco y parece hacerlo tan pronto como escribe cualquier sobrecarga de Equals(), incluso si solo devuelve verdadero/falso. –

2

Usamos NH 2 y este ejemplo no funcionó para nosotros. (NO FUE desproxydo el tipo y el proxy izquierdo) tipo, ver abajo) se dice que 2 entidades con el mismo ID no son iguales, cuando uno de ellos es proxy (de COrganization) y otro no lo es (DOrganization) cuando tuvimos una jerarquía:..

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()), 

Así CContract tiene un campo de tipo COrganization. Con un regulador de

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; 
} 

Construimos nuevo contrato, su constructor establece el campo COrganization apuntando a alguna organización. Luego llamamos a UnitOfWork.Commit, NH intentó establecer el campo de organización nuevamente (con la misma identificación), GetUnproxiedType funcionó incorrectamente, los valores nuevos y antiguos fueron reconocidos como no iguales, y se lanzó una excepción ...

Aquí es el lugar en el que mostró el error hasta:

  var otherType = other.GetUnproxiedType(); 
      var thisType = GetUnproxiedType(); 

      return thisType.IsAssignableFrom(otherType) || 
      otherType.IsAssignableFrom(thisType); 

En depurador: othertype == COrganizationProxy - GetUnproxiedType fallidos ... thisType == DOrganization

COrganizationProxy y DOrganization uno de ellos heredando COrganization . Entonces no son IsAssignableFrom para el otro ...

¿Por qué este ejemplo funciona para usted?

Tal vez porque tenemos NH 2.0 o 2.1?

O debido a la sencilla "cOrganization como COrganization" en lugar de "(COrganization) (cOrganization.GetConcrete())"?

¿O porque tenemos la implementación de ==,! = E Igual no solo en Entidad, sino también en Organización?

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); 
    } 
} 
+1

Extrañamente, cuando cambié "private Type GetUnproxiedType()" del libro a "público virtual Type GetUnproxiedType()", todo comenzó a funcionar (NH 2.0 o 2.1). ¿Por qué tenemos el método "privado" en el libro? – NHusser

+0

¿podría ser que está aprobando solo métodos públicos? Tampoco he probado este código todavía, por lo que no puedo confirmar si funciona en NH 3.0 (que es a lo que apunta el libro). –

+0

Depende de la fábrica proxy que estamos utilizando. http://groups.google.com/group/nhusers/browse_thread/thread/54777dd6249e7639 – NHusser

Cuestiones relacionadas