2009-12-31 2 views
9

Sto testando NHibernate come soluzione alle esigenze ORM della mia azienda. Per fare ciò, ho prodotto un piccolo modello di test basato su una scuola, che fornisce alcuni utili casi limite per la gestione di NHibernate.Mappatura di una struttura immutabile come componente in NHibernate

Sto riscontrando problemi nel trovare come mappare una struttura personalizzata come componente di un'entità senza utilizzare l'interfaccia IUserType. Vorrei sottolineare che è un requisito importante che le classi di dominio siano in un assembly separato dal nostro codice NHibernate e che l'assembly del dominio non abbia un riferimento a NHibernate.

La struttura personalizzata è Time, utilizzata per rappresentare un'ora del giorno in ore e minuti. È una struttura immutabile molto semplice e fornita solo per illustrare il problema di una struttura personalizzata. Il costruttore prende un singolo argomento che è ore e minuti, come numero intero nel 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; } 
} 

Questa struttura viene utilizzata come componente della classe Lesson per memorizzare l'ora del giorno di iniziare la lezione:

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

Questa classe mappe direttamente a questa tabella:

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

Sto cercando un modo per mappare questa struttura come componente della classe Lesson, in modo che NHibernate legga un valore di proprietà sulla struttura (come qualsiasi altro componente) t o ottenere un valore per la colonna, ma inizializzerà una nuova istanza della struttura passando il valore della colonna al costruttore durante la lettura del valore dalla colonna nell'entità.

Se avete suggerimenti, sarebbe super. Se vuoi dirmi che questo non può essere realizzato senza usare IUserType, anche questa è una buona risposta.

risposta

16

Per quanto ne so, ci sono tre piani di attacco.

  • È possibile associare il componente direttamente alle proprietà del tipo personalizzato. In questo esempio, sono NHibernate impostare la proprietà HoursAndMinutes, modificare il codice in setter tale struttura per aggiornare le proprietà Hours e Minutes appropriato, e hanno il costruttore chiamare this.HoursAndMinutes = hoursAndMinutes; così lo stesso codice per aggiornare le proprietà Hours e Minutes viene eseguito indipendentemente viene utilizzato il costruttore o viene utilizzato il setter sulla proprietà HoursAndMinutes. La scriveresti in questo modo se non usassi un ORM e sapessi che avrebbe usato questa proprietà? Probabilmente no. Ma non è la fine del mondo e un commento spiegherebbe tutto.

  • Si scrive un'implementazione IUserType o ICompositeUserType. In realtà, esistono esattamente per questo scenario e offrono la flessibilità necessaria per creare un'istanza della struttura nell'implementazione NullSafeGet() e estrarre i dati come desiderato dall'implementazione NullSafeSet(). Inseriscilo in un altro assieme, ad esempio MyModel.NHibernateCrap.dll, se lo desideri. Non è necessario che il modello/dominio sia a conoscenza dell'esistenza dell'implementazione IUserType o di NHibernate, che è tutto per il file di mapping da specificare.

  • Si utilizza la soluzione alternativa basata sul codice descritta da Miki Watts nella sua risposta. Cioè, il tuo componente nella mappatura di NHibernate mappa i campi o le proprietà private nel tuo tipo di modello che eseguono alcuni magici incantesimi a mano per convertirli in proprietà pubbliche che l'applicazione utilizza e viceversa. (È simile alla prima opzione che fornisco, l'unica differenza è che nel suo scenario il campo è una soluzione che consente al database precedente di filtrare nell'implementazione della classe, ma non ad altre parti dell'applicazione o del modello. , o se il database legacy è solo un fatto di vita, allora credo che questo sia perfettamente ragionevole.)

rispondere direttamente e realisticamente la tua domanda, NHibernate non ha intenzione di chiamare un costruttore che ha i parametri, periodo - è semplicemente come funziona, aggiorna un oggetto e poi inizia a impostarlo e rifletterci sopra, a meno che non inizi a fare cose strane con i proxy o dica di usare l'implementazione IUserType. Non esiste alcun meccanismo per dire qualcosa come <constructor><arg>HoursAndMinutes</arg></constructor> in un file di mappatura o qualcosa del genere. Smettila di preoccuparti e ama la bomba.

Dal IUserType è il meccanismo fornito da NHibernate per fare tali cose, non capisco davvero perché non si vorrebbe usarlo.

Buona fortuna!

+0

Buone informazioni; +1 per te. Il mio requisito di non utilizzare IUserType è semplicemente quello di vedere quali alternative sono disponibili, che avete gentilmente fornito. –

+0

Nicholas - se abbiamo strutture che contengono tipi di Entità (a differenza dei tipi di valore), possiamo mapparle con IUserType? Se é cosi, come; se no, qual è l'alternativa? – fostandy

2

Sto lavorando con un database precedente basato su Priority ERP. Nel database, il tempo è rappresentato da un numero intero di minuti, a partire da un'epoca. Quindi, ad esempio, il numero 0 rappresenta 01/01/1988 00:00 e il numero 1440 rappresenta 02/01/1988 00:00 e così via.

La soluzione che ho trovato per questo aspetto:

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

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

in cui le funzioni DateTimeHelper fanno l'attuale conversione dal numero intero di minuti per una struttura DateTime reale.

+0

Grazie per la risposta, ma questo è un pezzo puramente teorica di lavoro per esplorare i limiti degli strumenti ORM. È stato scelto il tempo in un formato non standard per illustrare la mappatura di strutture immutabili. I workaround, sebbene siano divertenti da leggere, non rispondono direttamente alla domanda. –

0

Facendo set protette e un costruttore di default protetto su di esso sembrava funzionare per me

come questo:

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

Come funziona? Quando creo un costruttore vuoto per una struttura, ottengo l'errore di compilazione "la struct non può contenere un costruttore senza parametri". –