Domanda

Ho un seguente mapping:

<set name="People" lazy="true" table="ProjectPeople">
  <key column="ProjectId" />
  <composite-element class="PersonRole">
    <many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
    <many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true"  />
  </composite-element>
</set>

Ora, non voglio davvero avere una classe separata per il ruolo nel dominio, ho solo bisogno del nome del ruolo. Tuttavia, in DB i ruoli devono comunque essere normalizzati in una tabella separata Role (Id, Name).

Come posso mapparlo in modo che le persone utilizzino la seguente classe PersonRole?

public class PersonRole {
    public virtual Person Person { get; set; }
    public virtual string Role { get; set; }
}

Aggiorna : aggiunta generosità, sembra una domanda utile non solo a me.

È stato utile?

Soluzione

Personalmente vorrei creare una classe di ruolo come Yassir

Ma se vuoi usare la struttura che hai al momento, crea una vista che contenga la chiave foriegn della tua tabella personale e la descrizione del ruolo.

Modifica la tabella Set mapping in modo che punti alla tua nuova vista Quindi modifica il tuo mapping dei ruoli in modo che sia una proprietà anziché il mapping molti a uno.

Comunque, adottando questo approccio, penso che ciò significherebbe che non sarai in grado di aggiornare il tuo ruolo in quanto reinserisce una vista

Modifica: per aggiornare il ruolo è possibile aggiungere <sql-insert>,<sql-update> and <sql-delete> al file di mappatura in modo che tutto a cascata funzioni

Altri suggerimenti

In realtà non otterrai la risposta che speri, semplicemente perché non è possibile. (N) Hibernate è un framework di mappatura relazionale di oggetti e supporta tre tipi di strategie di mappatura :

  • tabella per gerarchia di classi
  • tabella per sottoclasse
  • tabella per classe concreta

Ti permette anche di deviare da questo usando formula o sql-insert ecc., ma come hai scoperto, questi alla fine causano solo più dolore, non sono incoraggiati dalla comunità di Hibernate e sono cattivi per la manutenibilità del tuo codice.

Soluzione?

In realtà, è molto semplice. Non vuoi usare una classe per Role. Presumo tu intenda che non vuoi esporre una classe di tipo Role e che non vuoi digitare prObject.Role.Name continuamente. Solo prObject.Role, che dovrebbe restituire una stringa. Hai diverse opzioni:

  1. Usa una classe interna in, diciamo, PersonRole, questa classe può essere interna o privata. Aggiungi una proprietà Ruolo che imposta e aggiorna un campo membro;
  2. Utilizza una classe interna. Aggiungi una proprietà Ruolo che imposta e aggiorna un campo membro;

Esaminiamo l'opzione 2:

// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role 
{
    // typical mapping, need not be internal/protected when class is internal
    // cannot be private, because then virtual is not possible
    internal virtual int Id { get; private set; }
    internal virtual string Name { get; set; }
}

// the composite element
public class PersonRole
{
    // mapped properties public
    public virtual Person Person { get; set; }

    // mapped properties hidden
    internal virtual Role dbRole { get; set; }

    // not mapped, but convenience property in your DAL
    // for clarity, it is actually better to rename to something like RoleName
    public string Role     /* need not be virtual, but can be */
    { 
        get
        {
            return this.dbRole.Name;
        }
        set
        {
            this.dbRole.Name = value;    /* this works and triggers the cascade */
        }
    }
}

E la mappatura può apparire come previsto. Risultato: non hai violato la regola di una tabella per classe ( EDIT: asker afferma che vuole esplicitamente violare quella regola e Hib la supporta, che è corretta ), ma hai nascosto gli oggetti da modifiche e accessi utilizzando tecniche tipiche orientate agli oggetti. Tutte le funzionalità NH (a cascata, ecc.) Funzionano ancora come previsto.

(N) Hibernate riguarda tutto questo tipo di decisioni: come creare un livello di astrazione ben ponderato e sicuro nel database senza sacrificare la chiarezza, la brevità o la manutenibilità o violare le regole OO o ORM.


Aggiornamento (dopo la chiusura di q.)

Altri approcci eccellenti che uso molto quando ho a che fare con questo tipo di problema sono:

  • Crea normalmente i tuoi mapping (ad esempio, una classe per tabella, so che non ti piace, ma è per il meglio) e usa i metodi di estensione:

     // trivial general example
     public static string GetFullName(this Person p)
     {
         return String.Format("{0} {1}", p.FirstName, p.LastName);
     }
    
     // gettor / settor for role.name
     public static string GetRoleName(this PersonRole pr)
     {
         return pr.Role == null ? "" : pr.Role.Name;
     }
     public static SetRoleName(this PersonRole pr, string name)
     {
         pr.Role = (pr.Role ?? new Role());
         pr.Role.Name = name;
     }
    
  • Crea normalmente i tuoi mapping ma usa partial class es, che ti consente di " decorare " la tua classe nel modo che preferisci. Il vantaggio: se usi la mappatura generata delle tue tabelle, ti rigeneri tutte le volte che vuoi. Naturalmente, le classi parziali dovrebbero andare in file separati, quindi considerando il tuo desiderio di diminuire & Quot; bloat & Quot; questo probabilmente non è attualmente un buon scenario.

     public partial class PersonRole
     {
         public string Role {...}
     }
    
  • Forse più semplice: basta sovraccaricare ToString() per String.Format, il che lo rende adatto per l'uso in Role.Name e amici, ma ovviamente non lo rende assegnabile. Per impostazione predefinita, ogni classe di entità o POCO dovrebbe comunque avere un <=> sovraccarico.

Sebbene sia possibile farlo direttamente con NHibernate, il q. è stato chiuso prima che avessi il tempo di guardarlo (nessuno ha colpa, non avevo tempo). Aggiornerò se trovo il tempo per farlo attraverso la mappatura di Hibernate HBM, anche se non sono d'accordo con l'approccio. Non è utile lottare con concetti avanzati di Hib quando il risultato finale è meno chiaro per altri programmatori e meno chiaro in generale (dove è andata quella tabella? Perché non c'è un'astrazione IDao per quella tabella? Vedi anche Best practice di NHibernate e S # arp ). Tuttavia, l'esercizio è comunque interessante.

Considerando i commenti su " best practice " ;: in situazioni tipiche, non dovrebbe essere solo " una classe per tabella " ;, ma anche un IDaoXXX, un DaoConcreteXXX e un GetDaoXXX per ogni tabella, in cui si utilizza la gerarchia di classi / interfacce per distinguere tra sola lettura e lettura / scrittura delle tabelle. Quello èun minimo di quattro classi / righe di codice per tabella. Generalmente, questo viene generato automaticamente, ma fornisce un livello di accesso molto chiaro (dao) al livello dati (dal). Il livello dati è mantenuto il più spartano possibile. Niente di questi & Quot; best practice & Quot; impedirti di utilizzare metodi di estensione o metodi parziali per spostare <=> in <=>.

Queste sono le migliori pratiche generali . Non è sempre possibile o fattibile o addirittura necessario in certe sedute speciali o tipiche.

Non penso che sia possibile mappare molti-a-uno a un tipo primitivo se fossi in te aggiungerei una classe Role al modello

Questa è la più grande svolta dell'intera cosa purista OO. Sicuramente l'obiettivo è avere un'applicazione funzionante. Non una versione di alcuni corpi di una gerarchia di classi perfetta. Quindi cosa succede se devi codificare & Quot; prObject.Role.Name & Quot; anziché " prObject.Role " ;. In che modo ti aiuta a creare un programma migliore e più affidabile?

Dal punto di vista purista del design delle applicazioni, ciò che si desidera è semplicemente sbagliato. Una persona può avere diversi ruoli, di solito un ruolo può essere assegnato a più persone. Perché affrontare tutti questi problemi per imporre una gerarchia di classi non realistica per un ruolo per persona quando il modello di dati non ritardati è composto da molti ruoli per persona?

Se hai davvero un " solo un ruolo per persona " regola quindi dovrebbe essere riflessa nel modello di dati sottostante.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top