Il modo migliore per modellare relazioni molti-a-uno in NHibernate quando si ha a che fare con un DB legacy?

StackOverflow https://stackoverflow.com/questions/10915

  •  08-06-2019
  •  | 
  •  

Domanda

Avvertimento: sono molto nuovo in NHibernate.So che questa domanda sembra semplice - e sono sicuro che ci sia una risposta semplice, ma è da un po' che sto girando le ruote su questo argomento.Ho a che fare con un database legacy che in realtà non può essere modificato strutturalmente.Ho una tabella dei dettagli che elenca i piani di pagamento accettati da un cliente.Ogni piano di pagamento ha un ID che rimanda a una tabella di riferimento per ottenere i termini, le condizioni, ecc. del piano.Nel mio modello a oggetti, ho una classe AcceptedPlan e una classe Plan.Inizialmente, ho utilizzato una relazione molti-a-uno dalla tabella dei dettagli alla tabella di riferimento per modellare questa relazione in NHibernate.Ho anche creato una relazione uno-a-molti che va nella direzione opposta dalla classe Plan alla classe AcceptedPlan.Andava bene mentre stavo semplicemente leggendo i dati.Potevo andare al mio oggetto Piano, che era una proprietà della mia classe AcceptedPlan per leggere i dettagli del piano.Il mio problema è sorto quando ho dovuto iniziare a inserire nuove righe nella tabella dei dettagli.Dalla mia lettura, sembra che l'unico modo per creare un nuovo oggetto figlio sia aggiungerlo all'oggetto genitore e quindi salvare la sessione.Ma non voglio dover creare un nuovo oggetto Piano principale ogni volta che desidero creare un nuovo record di dettaglio.Sembra un sovraccarico inutile.Qualcuno sa se sto andando su questo nel modo sbagliato?

È stato utile?

Soluzione

Mi allontanerei dall'avere un oggetto figlio contenente il loro genitore logico, può diventare molto disordinato e molto ricorsivo abbastanza rapidamente quando lo fai.Darei un'occhiata a come intendi utilizzare il modello di dominio prima di fare questo genere di cose.Puoi facilmente avere ancora i riferimenti ID nelle tabelle e lasciarli semplicemente non mappati.

Ecco due esempi di mappature che potrebbero spingerti nella giusta direzione, ho dovuto modificare i nomi delle tabelle ecc. Ma potrebbe essere d'aiuto.Probabilmente suggerirei anche di mappare StatusId su un'enumerazione.

Presta attenzione al modo in cui la borsa mappa efficacemente la tabella dei dettagli in una raccolta.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="CustomerAccountId" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan" table="details">
          <key column="CustomerAccountId" foreign-key="AcceptedOfferFK"/>
          <many-to-many
            class="Namespace.AcceptedOffer, Namespace"
            column="AcceptedOfferFK"
            foreign-key="AcceptedOfferID"
            lazy="false"
           />
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="AcceptedOffer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="AcceptedOfferId" length="4" sql-type="int" not-null="true" unique="true" index="AcceptedOfferPK"/>
            <generator class="native" />
        </id>

        <many-to-one 
          name="Plan"
          class="Namespace.Plan, Namespace"
          lazy="false"
          cascade="save-update"
        >
        <column name="PlanFK" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

        <property name="StatusId" type="Int32">
            <column name="StatusId" length="4" sql-type="int" not-null="true"/>
        </property>

  </class>
</hibernate-mapping>

Altri suggerimenti

Non ho visto il diagramma del tuo database mentre stavo scrivendo.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="customer_id" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan">
            <key column="accepted_offer_id"/>
            <one-to-many class="Namespace.AcceptedOffer, Namespace"/>
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="Accepted_Offer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="accepted_offer_id" length="4" sql-type="int" not-null="true" unique="true" />
            <generator class="native" />
        </id>

        <many-to-one name="Plan" class="Namespace.Plan, Namespace" lazy="false" cascade="save-update">
            <column name="plan_id" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

  </class>
</hibernate-mapping>

Probabilmente dovrebbe funzionare (ho eseguito solo mappature di esempio per le raccolte, dovrai aggiungere altre proprietà).

L'approccio che adotterei per modellare questo è il seguente:

L'oggetto cliente contiene un ICollection <PaymentPlan> PaymentPlans che rappresenta i piani accettati dal cliente.

Il PaymentPlan per il cliente verrebbe mappato utilizzando un sacchetto che utilizza la tabella dei dettagli per stabilire quale ID cliente è mappato a quale PaymentPlans.Utilizzando cascade all-delete-orphan, se il cliente veniva eliminato, verrebbero eliminati sia le voci dei dettagli che i PaymentPlans di proprietà del cliente.

L'oggetto PaymentPlan contiene un oggetto PlanTerms che rappresentava i termini del piano di pagamento.

I PlanTerms verrebbero mappati su un PaymentPlan utilizzando un aggiornamento di salvataggio a cascata di mappatura molti-a-uno che inserirà semplicemente un riferimento all'oggetto PlanTerms pertinente nel PaymentPlan.

Utilizzando questo modello, potresti creare PlanTerms in modo indipendente e quindi quando aggiungi un nuovo PaymentPlan a un cliente, creerai un nuovo oggetto PaymentPlan passando l'oggetto PlanTerms pertinente e quindi aggiungendolo alla raccolta sul cliente pertinente.Alla fine salveresti il ​​cliente e lasceresti che l'ibernazione eseguisse a cascata l'operazione di salvataggio.

Ti ritroveresti con un oggetto Customer, un oggetto PaymentPlan e un oggetto PlanTerms con il Customer (tabella clienti) che possiede istanze di PaymentPlans (la tabella dei dettagli) che aderiscono tutte a PlanTerms specifici (la tabella del piano).

Se necessario, ho alcuni esempi più concreti della sintassi di mappatura, ma probabilmente è meglio lavorarci con il tuo modello e non ho abbastanza informazioni sulle tabelle del database per fornire esempi specifici.

Non so se ciò sia dovuto al fatto che la mia esperienza con NHibernate è limitata, ma potresti creare una classe BaseDetail che abbia solo le proprietà per i Dettagli mentre vengono mappate direttamente alla tabella Dettagli.

Quindi crea una seconda classe che eredita dalla classe BaseDetail che ha l'oggetto Plan Parent aggiuntivo in modo da poter creare una classe BaseDetail quando desideri semplicemente creare una riga Detail e assegnarle il PlanId, ma se devi popolare un Detail completo record con l'oggetto del piano Parent è possibile utilizzare la classe Detail ereditata.

Non so se ha molto senso, ma fatemelo sapere e chiarirò ulteriormente.

Penso che il problema che hai qui è che il tuo oggetto AcceptedOffer contiene un oggetto Plan e quindi il tuo oggetto Plan sembra contenere una raccolta AcceptedOffers che contiene oggetti AcceptedOffer.Stessa cosa con i clienti.Penso che il fatto che gli oggetti siano figli l'uno dell'altro è ciò che causa il tuo problema.

Allo stesso modo, ciò che rende complessa la tua Offerta Accettata è che ha due responsabilità:indica offerte incluse in un piano, indica l'accettazione da parte di un cliente.Ciò viola il principio di responsabilità unica.

Potrebbe essere necessario distinguere tra un'offerta inclusa in un piano e un'offerta accettata dai clienti.Quindi ecco cosa farò:

  1. Crea un oggetto Offerta separato che non abbia uno stato, ad esempio non ha un cliente e non ha uno stato: ha solo un OfferId e il Piano a cui appartiene come attributi.
  2. Modifica il tuo oggetto Piano per avere una raccolta Offerte (non è necessario che abbia un'offerta accettata nel suo contesto).
  3. Infine, modifica il tuo oggetto AcceptedOffer in modo che contenga un'offerta, il cliente e uno stato.Il cliente rimane lo stesso.

Penso che questo districherà sufficientemente le mappature di NHibernate e i problemi di salvataggio degli oggetti.:)

Un suggerimento che potrebbe (o meno) essere utile in NHibernate:puoi mappare i tuoi oggetti rispetto a Views come se View fosse una tabella.Basta specificare il nome della vista come nome di tabella;fintanto che tutti i campi NOT NULL sono inclusi nella vista e la mappatura funzionerà correttamente.

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