2009-05-10 18 views
25

Me han lanzado y he comenzado a aprender Fluidez NHibernate (ninguna experiencia anterior de NHibernate). En mi proyecto, estoy programando interfaces para reducir el acoplamiento, etc. Eso significa que prácticamente "todo" se refiere a la interfaz en lugar del tipo concreto (IMessage en lugar de Message). La idea detrás de esto es ayudar a que sea más comprobable al poder burlarse de las dependencias.Programación de interfaces al mapear con Fluidez NHibernate

Sin embargo, (Fluido) NHibernate no ama cuando trato de asignar a interfaces en lugar de clases concretas. La cuestión es simple - de acuerdo con el Fluido Wiki, es inteligente para definir el campo de ID de mi clase como por ejemplo

int Id { get; private set; } 

para obtener una clave primaria típica generada automáticamente. Sin embargo, eso sólo funciona con clases concretas - que no se puede especificar un nivel de acceso en una interfaz, donde la misma línea tiene que ser

int Id { get; set; } 

y supongo que niega hacer el colocador privada en la clase concreta (la la idea es que solo NHibernate debe establecer el ID asignado por el DB).

Por ahora, supongo que haré público al colocador y trataré de evitar la tentación de escribirlo ... Pero, ¿alguien tiene una idea de cuál sería la forma "adecuada" y de mejores prácticas para crear un campo de clave primaria adecuado en el que NHibernate solo puede escribir mientras solo se está programando en las interfaces.

ACTUALIZADO

Por lo que entiendo después de las dos respuestas por debajo de mookid y James Gregory, que bien puede ser en el camino equivocado - no debería ser una razón para mí tener una interfaz por entidad como tengo ahora Eso está muy bien. Supongo que mi pregunta es: ¿no hay motivo para programar al 100% contra una interfaz para cualquier entidad? Y si hay incluso una sola situación donde esto podría justificarse, ¿es posible hacer esto con (Fluido) NHibernate?

Lo pido porque no sé, no para ser crítico. Gracias por las respuestas. :)

+3

Es curioso porque algunos desarrolladores que respeto han asumido accidentalmente el antipatrón de interfaz de entidad. Es más un error que hacen los profesionales, que novatos, ya que los novatos apenas saben por qué las interfaces son importantes en primer lugar. –

+1

Si no está creando interfaces para sus entidades, ¿cómo prueba el comportamiento de sus entidades? Más específicamente, ¿cómo te burlas de las dependencias de esa entidad? –

+2

Cuando la gente dice que programar interfaces no implementaciones, no significan la construcción del lenguaje de interfaz. Significan tratar de encasillar el código que está usando tanto como sea posible. Es decir. Cuando utilice un Enumerador en un diccionario, no suponga que Current no lanzará antes de que se invoque movenext, no está definido y puede cambiar. – stonemetal

Respuesta

8

ACTUALIZACIÓN: utilizando unión-subclase no es compatible a través de la interfaz fluida que proporciona fluent-nhibernate. Tendrá que usar un archivo de mapeo hbm regular y agregarlo.

Yo también estoy tratando de hacer esto con fluidez NHibernate. No creo que deba ser un problema el mapeo de interfaces. Desea utilizar una estrategia de herencia, específicamente el table-per-concrete-class strategy.

Esencialmente, usted crea una definición de mapeo para la clase base (en este caso su interfaz) y especifica cómo NHibernate debe tratar con los implementadores mediante el uso de una subclase-unión.

Así, por ejemplo, esto debería permitirle hacer asociaciones polimórficas:

<class name="IAccountManager" 
       abstract="true" 
       table="IAccountManager"> 

     <id name="Id"> 
       <generator class="hilo"/> 
     </id> 

     <union-subclass 
       table="DefaultAccountManager" 
       name="DefaultAccountManager"> 
       <property name="FirstName"/> 
     </union-subclass> 

     <union-subclass 
       table="AnotherAccountManagerImplementation" 
       name="AnotherAccountManagerImplementation"> 
       <property name="FirstName"/> 
     </union-subclass> 
     ... 
</class> 

Nota cómo el ID es el mismo para todos los ejecutores concretos. NHibernate requirió esto. Además, la tabla IAccountManager no existe en realidad.

También puedes probar y aprovechar el polimorfismo implícito de NHibernate (documentado debajo de la estrategia de tabla por clase de concreto), pero tiene muchas limitaciones.

+6

La subclase de unión ahora es compatible con Fluent NHibernate 1.1: Llamar a UseUnionSubclassForInheritanceMapping() desde la base ClassMap – cbp

10

puede ajustar su interfaz para que contenga solamente un captador:

public interface ISomeEntity 
{ 
    int Id { get; } 
} 

Su clase concreta todavía puede implementar un colocador así, y dado que se está programando a sus interfaces que nunca va a llamar a la incubadora " por accidente".

Si no desea permitir el establecimiento de la identificación incluso cuando contiene una referencia a una instancia concreta, puede abstenerse de implementar un setter, y luego dejar que NHibernate acceda al campo en lugar de la propiedad; es correcto, NHibernate puede usar hábil truco de reflexión para establecer su campo de identificación directamente en lugar de invocar la propiedad. Entonces es posible correlacionar el ID de la siguiente manera:

Id(e => e.Id).Access.AsCamelCaseField(); 

en cuyo caso su propiedad Id debe estar respaldado por un campo id correspondiente. Hay más convenciones de nombres, p. si prefiere caracteres de subrayado como prefijo de campo privado.

+0

¡Gracias por la respuesta! Tu ISomeEntity es como lo tenía, pero NHibernate se queja de que no hay setter si lo mapeo en la interfaz. Si hago un mapa de la clase concreta, funciona, pero parece que me huele un poco ... Así que supongo que buscaré la segunda opción y accederé al campo. Menos limpio que me gustaría, pero supongo que la realidad limita lo que puedo hacer. :) –

+0

Ok, traté de hacer el Id (e => e.Id) .Access.AsCamelCaseField(); bit, pero eso requiere que el campo de Id. De respaldo también esté en la interfaz. Veo que puede que no haya una manera fácil de lograr lo que busco, pero también veo por la respuesta de James Gregory que lo que busco podría no ser Lo correcto (tm). ¡Gracias! –

12

Me doy cuenta de que esto es una diversión, y no una respuesta a su pregunta (aunque creo que Mookid lo tiene cubierto).

Realmente debe evaluar si las interfaces en las entidades de su dominio en realidad están proporcionando algo de valor; es raro encontrar una situación en la que realmente necesite hacer esto.

Por ejemplo: ¿Cómo se puede confiar menos en IMessage que confiar en Message, cuando ambos (casi) indudablemente comparten firmas idénticas? No debería necesitar burlarse de una entidad, porque es raro que tenga el comportamiento suficiente como para requerir burlas.

+0

Guau, respuesta de la nave nodriza. :) Gracias, y veo. Ciertamente no puedo justificar el uso de interfaces por mi cuenta, acabo de leer suficientes blogs, etc., que afirma que siempre debería estar programando para interfaces, nunca para implementarlas en concreto. Voy a abandonar eso gracias a tu (y mookid) visión, pero sería genial ver más sobre esto en un mundo loco de TDD/simulacro/IoC/ORM. ;) –

+1

Se burla para verificar el comportamiento y lo inyecta para evitar el acoplamiento en las implementaciones. Las entidades rara vez son algo más que estructuras de datos, y como tales no tienen ningún comportamiento (y por lo tanto no hay variabilidad en la implementación) para requerir abstracciones (interfaces). Dicho esto, este diseño aún debe ser compatible con Fluent NHibernate, ya que es compatible con NH-core, por lo que me aseguraré de crear un problema para él. –

+0

Gracias James, por la información y el problema. :) –

10

Tengo exactamente el mismo problema. Desafortunadamente, tengo una razón válida para usar interfaces de entidades; el modelo de entidad se implementará de diferentes maneras y con diferentes asignaciones por cliente.

Todo el modelo debe ser de sólo lectura, por lo que las interfaces son del estilo:

public interface IAccount 
{ 
    long AccountId { get; } 
    IHouse House { get; } 
} 

public interface IHouse 
{ 
    long HouseId { get; } 
    HouseStatus Status { get; } 
    IList<IAccount> Accounts { get; } 
} 

implementaciones concretas a continuación, poner en práctica estos con fijadores internos:

public class Account: IAccount 
{ 
    public virtual long AccountId { get; internal set; } 
    public virtual IHouse House { get; internal set; } 
} 

public class House: IHouse 
{ 
    public virtual long HouseId { get; internal set; } 
    public virtual HouseStatus Status { get; internal set; } 
    public virtual IList<IAccount> Accounts { get; internal set; } 
} 

He bajado la ruta de mapeo a las clases concretas. Todo está bien hasta que se crean relaciones que devuelven interfaces y se deben convertir a implementaciones concretas.

HasMany(x => x.Accounts) 

puede convertirse en

HasMany<Account>(x => x.Accounts) 

Pero no hay un equivalente 'fundido' para

References(x => x.House) 

Mapeo de las interfaces (la solución más ordenado) tiros el problema mencionado anteriormente en que la Id debe existir en la clase superior para establecer y requiere un setter en la interfaz.

public sealed class AccountMap : ClassMap<IAccount> 
{ 
    public PokerPlayerMap() 
    { 
     Id(x => x.AccountId, "account_id"); 

     DiscriminateSubClassesOnColumn("Type").SubClass<Account>(s => 
     { 
      References(x => x.House);  
     }); 
    } 
} 

Por el momento, mi única solución es agregar arregladores a todos los campos de Id de la interfaz. Es una lástima que el Id no pueda existir dentro de una subclase o tenga su tipo moldeado desde la interfaz.

+2

¿No puede usar 'References (x => x.House). Class ();'? – fostandy

4

Parece que no tengo la reputación suficiente para comentar las respuestas de otras personas, pero como tal voy a tener que hacer de esto una respuesta por sí misma.

Referencias ahora tiene una sobrecarga genérica para permitir el lanzamiento que el Gecko estaba buscando en su respuesta.

Cuestiones relacionadas