2009-12-31 8 views
9

Estoy probando NHibernate para que sea la solución a las necesidades de ORM de mi empresa. Para hacer esto, he producido un pequeño modelo de prueba basado en una escuela, proporcionando algunos casos útiles para NHibernate.Asignación de una estructura inmutable como componente en NHibernate

estoy teniendo problemas para encontrar la manera de asignar una estructura personalizada como un componente de una entidad sin utilizar la interfazIUserType. Debo enfatizar que es un requisito importante que las clases de dominio estén en un ensamblaje separado de nuestro código NHibernate, y que el ensamblado de dominio no tenga una referencia a NHibernate.

La estructura personalizada es Time, que se utiliza para representar una hora del día en horas y minutos. Es una estructura inmutable muy simple y solo se proporciona para ilustrar el problema de una estructura personalizada. El constructor toma un solo argumento que es horas y minutos, como un entero en el formato hhmm.

public struct Time 
{ 
    public Time(int hoursAndMinutes) 
    { 
     // Initialize Structure // 
    } 

    public int Hours { get; private set; } 

    public int Minutes { get; private set; } 

    public int HoursAndMinutes { get; private set; } 
} 

Esta estructura se utiliza como un componente de la clase Lesson para almacenar la hora del día de iniciar la lección:

public class Lesson 
{ 
    public int ID { get; private set; } 
    public Teacher Teacher { get; internal set; } 
    public DayOfWeek Day { get; set; } 
    public Time StartTime { get; set; } // <-- Custom Type 
    public int Periods { get; set; } 
} 

Esta clase correlaciona directamente con esta tabla:

CREATE TABLE Lessons 
(
    ID INT, 
    Subject NVARCHAR(128) 
    TeacherID INT, 
    Day VARCHAR(9), 
    StartTime INT, // <-- Maps to custom type. 
    Periods INT 
) 

Estoy buscando una forma de asignar esta estructura como un componente de la clase Lesson, de modo que NHibernate lea un valor de propiedad en la estructura (como cualquier otro componente) t o obtener un valor para la columna, pero inicializará una nueva instancia de la estructura al pasar el valor de la columna al constructor al leer el valor de la columna en la entidad.

Si tiene alguna sugerencia, sería genial. Si quiere decirme que esto no se puede lograr sin usar IUserType, también es una buena respuesta.

Respuesta

16

Que yo sepa, hay tres planes de ataque.

  • Puede asignar el componente directamente a las propiedades del tipo personalizado. En este ejemplo, se han NHibernate establecer la propiedad HoursAndMinutes, cambie el código en el colocador de esa propiedad para actualizar los Hours y Minutes propiedades apropiadamente, y tienen el constructor sólo llamar this.HoursAndMinutes = hoursAndMinutes; por lo que el mismo código para actualizar los Hours y Minutes propiedades es ejecutado independientemente de si se usa el constructor o se usa el colocador en la propiedad HoursAndMinutes. ¿Lo escribirías de esa manera si no estuvieras usando un ORM y supieras que iba a funcionar con esa propiedad? Probablemente no. Pero no es el fin del mundo y un comentario explicaría todo.

  • Escribes una implementación de IUserType o ICompositeUserType. En realidad, existen precisamente para este escenario y le dan la flexibilidad para instanciar la estructura como lo desee en la implementación NullSafeGet() y extraer los datos como lo desee en la implementación NullSafeSet(). Póngalo en otro ensamblaje, digamos MyModel.NHibernateCrap.dll, si lo desea. No es necesario que su modelo/dominio tenga en cuenta que existe la implementación IUserType o NHibernate, eso es todo para que el archivo de mapeo especifique.

  • Utiliza la solución basada en código descrita por Miki Watts en su respuesta. Es decir, su componente en el mapeo de NHibernate se asigna a campos o propiedades privadas en su tipo de modelo que hacen gestos de mano mágicos para convertirlos en propiedades públicas que utiliza la aplicación, y viceversa. (Es similar a la primera opción que proporciono, la única diferencia es que en su escenario el campo es una solución alternativa que permite que la base de datos heredada se filtre a la implementación de la clase, pero no a otras partes de la aplicación o modelo. o si la base de datos heredada es sólo un hecho de la vida, entonces creo que esto es perfectamente razonable.)

para responder directamente y de manera realista su pregunta, NHibernate no va a llamar a un constructor que tiene parámetros, período - así es como funciona, pone al día un objeto y luego comienza a configurarlo y reflejarlo, a menos que empiece a hacer cosas extrañas con proxies o diga que use su implementación IUserType. No hay ningún mecanismo para decir algo como <constructor><arg>HoursAndMinutes</arg></constructor> en un archivo de mapeo ni nada de eso. Deja de preocuparte y ama la bomba.

Dado que IUserType es el mecanismo proporcionado por NHibernate para hacer tales cosas, realmente no entiendo por qué uno no querría usarlo.

¡Buena suerte!

+0

Buena información; +1 para ti. Mi requisito para no usar IUserType es simplemente ver qué alternativas hay disponibles, que usted ha proporcionado gentilmente. –

+0

Nicholas: si tenemos estructuras que contienen tipos de entidades (a diferencia de los tipos de valores), ¿podemos asignarlos con IUserType? Si es así, cómo; si no, ¿cuál es la alternativa? – fostandy

2

Estoy trabajando con una base de datos heredada que se basa en Priority ERP. En la base de datos, el tiempo se representa como un número entero de minutos, comenzando desde una época. Entonces, por ejemplo, el número 0 representa 01/01/1988 00:00, y el número 1440 representa 02/01/1988 00:00 y así sucesivamente.

La solución que se encontró para esto se parece a esto:

[Field("CURDATE")] 
    private int transactionDate = DateTimeHelper.ConvertToInternalValue(DateTime.Today); 

    public DateTime TransactionDate 
    { 
     get { return DateTimeHelper.ConvertToDateValue(transactionDate); } 
     set { transactionDate = DateTimeHelper.ConvertToInternalValue(value); } 
    } 

donde las funciones en DateTimeHelper hacen el real la conversión del número entero de minutos a una estructura DateTime real.

+0

Gracias por la respuesta, pero esto es una pieza puramente teórica de trabajo para explorar los límites de herramientas ORM. Se eligió el tiempo en un formato no estándar para ilustrar el mapeo de estructuras inmutables. Las soluciones alternativas, aunque divertidas de leer, no responden directamente a la pregunta. –

0

Haciendo conjuntos protegidos y un constructor por defecto protegida en él parecía funcionar para mí

así:

public struct Time 
{ 
    protected Time(){} 
    public Time(int hoursAndMinutes) 
    { 
     // Initialize Structure // 
    } 

    public int Hours { get; protected set; } 

    public int Minutes { get; protected set; } 

    public int HoursAndMinutes { get; protected set; } 
} 
+0

¿Cómo funciona esto? Cuando creo un constructor vacío para una estructura, obtengo el error de compilación "struct no puede contener un constructor sin parámetros". –

Cuestiones relacionadas