2015-08-11 28 views
10

Sono alle prese con un problema correlato a NHibernate in cui è possibile utilizzare alcuni input.Come mantenere un sottoinsieme di un oggetto invece dell'intero oggetto?

Introduzione:

Ho un database legacy qualora non siano realmente stati applicati i concetti relazionali.

Nel database ho una tabella OrderLine che contiene i dati per le righe di un ordine.

Inoltre, la tabella contiene anche tutte le colonne con informazioni specifiche su Order. Questo potrebbe ad esempio essere il numero d'ordine di un cliente.

E.x. Se ho 10 righe di ordine, ho 10 righe nella mia tabella OrderLines e ogni riga ha tutti i dati specifici Order, ad es. numero d'ordine o informazioni sul cliente.

Io non voglio avere la struttura di cui sopra nel mio codice in modo da vista è stato creato per Orders in modo che potessi mappa La mia Order in NHibernate che poi ha un insieme/sacchetto di OrderLines che rende molto più senso.

Mapping: (semplificato)

<class name="Order" table="[view_Orders]"> 
    <bag name="OrderLines"> 
</class> 

<class name="OrderLine" table="OrderLines" /> 

Il problema:

La complessità della vista rende impossibile per salvare la vista. Quando si cerca NHibernates genera questa eccezione:

NHibernate.Exceptions.GenericADOException: non poteva inserire: XXX ---> System.Data.SqlClient.SqlException: Visualizza o 'view_Orders' funzione non è aggiornabile perché la modifica interessa più tabelle di base.

mappatura mio NHibernate è costruito come un oggetto Order che ha un "sacco impostare o" di OrderLine oggetti. Idealmente mi piacerebbe che NHibernate mantenga il set di oggetti OrderLine invece dell'intero oggetto.

C'è un modo per raggiungere questo obiettivo? Ho provato a bloccare l'oggetto utilizzando diverse modalità di blocco, ma non mi ha aiutato.

+0

Sembra che quello che stai cercando di fare dovrebbe funzionare. L'eccezione indica che NHibernate sta tentando di eseguire un UPDATE contro l'entità Ordine che è mappata a una vista non aggiornabile. NHibernate lo farà solo se ritiene che l'entità sia sporca, al di fuori delle raccolte OrderLines. Dovresti controllare le tue altre proprietà mappate. E forse prova ad usare un profiler come NHProf per vedere cosa sta cambiando. –

+0

Esattamente. Mi piacerebbe NON aggiornare il mio ordine perché è mappato a una vista non aggiornabile. Invece vorrei solo aggiornare l'oggetto figlio, ad es.la borsa di OrderLines. Ho usato NHProfiler e l'eccezione da sopra è fondamentalmente un'eccezione SQL che viene passata attraverso NHibernate. – JuniorCoder

risposta

1

Nel caso comprenda il problema, la soluzione è sorprendentemente semplice.Abbiamo appena avremmo marcare oggetto radice con dynamic-update="true"

<class name="Order" table="[view_Orders]" dynamic-update="true"> 
    ... 
</class> 

e quindi applicare update="false" ad ogni proprietà o di riferimento che abbiamo in quel Order classe mappata a vista:

... 
<property name="Code"  update="false"/> 
... 
<many-to-one name="Country" update="false /> 

Ma la nostra collezione sarà bisogno della mappatura standard, anche in cascata:

<class name="Order" table="[view_Orders]" dynamic-update="true"> 
    <bag name="OrderLines" 
     lazy="true" 
     inverse="true" 
     batch-size="25" 
     cascade="all-delete-orphan" > 
     ... 
    </bag> 
    ... // other stuff is update="false" 
</class> 

E ora il codice come questo farebbe gestione di OrderLines, pur non eseguendo alcun aggiornamento per l'oggetto radice Order

var session = ... // get ISession 
// load root 
var root = session.Get<Order>(123); 

// if needed change existing line (pretend there is one) 
root.OrderLines[0].Amount = 100; 

// add new 
var newOrder = ... // new order 
root.OrderLines.Add(newOrder); 

session.Save(root); 
session.Flush(); 

e che è. Cascade sull'oggetto radice sta facendo quello che ci serve, mentre l'aggiornamento = "false" non sta aggiornando lo ...

NOTA: Solo nota interessante - c'è anche di classe e la raccolta impostazione mutable="false", ma non funzionerebbe qui ... come la soluzione di cui sopra (è triste, perché sarebbe più elegante , ma non funziona come previsto ...). Vedi:

19.2.2. Strategy: read only

Se la vostra applicazione ha bisogno di leggere ma mai modificare le istanze di una classe persistente, una cache di sola lettura possono essere utilizzati. Questa è la strategia più semplice e migliore. È anche perfettamente sicuro per l'uso in un cluster.

<class name="Eg.Immutable" mutable="false">

+0

Hi Radim, Grazie mille per il vostro aiuto. Ho già provato l'impostazione mutable = "false" senza alcun risultato. Ho provato a utilizzare l'aggiornamento dinamico ma sto ancora ricevendo gli stessi errori. L'errore che ho descritto sopra è relativo a inserimenti di dati e non aggiornamenti che ritengo sia mirato al tuo esempio se sono corretto? – JuniorCoder

+0

Sì, come ho detto 'mutable =" false "' non è la soluzione qui. Ma contrassegnare tutte le proprietà relative alla vista come 'update =" false "' funzionerà. Proprio '' sarà persistente ... Perché ... se dici di aver mappato la vista - dobbiamo parlare SOLO DELL'AGGIORNAMENTO. Quindi, come hai descritto * (se l'ho letto correttamente) * - dobbiamo persistere solo nella raccolta - durante l'aggiornamento. In tal caso, anche gli articoli di quella raccolta possono essere INSERITI. Ma inserire il nuovo record nella visualizzazione genitore ... non ha senso in base al tuo scenario. Questo è quello che ho controllato e funziona .. o? –

+0

Intendo la parte della tua domanda * "... Idealmente mi piacerebbe che NHibernate mantenga il set di oggetti OrderLine invece dell'intero oggetto ...." * così, se abbiamo un record esistente (quello rappresentato da vista, mappata come oggetto genitore) inseriremo solo OrderLines ... e tale soluzione sarà supportata dalla risposta di cui sopra. L'ordine esiste già (non viene aggiornato da NHibernate) tutti gli articoli in OrderLines sono completamente gestibili ... –

3

È possibile utilizzare mutable="false" per evitare l'aggiornamento e cancella in quanto questo article dice:

classi immutabile, mutevole = "false", non possono essere aggiornati o cancellati da l'applicazione. Ciò consente a NHibernate di ottimizzare leggermente le prestazioni.

Per evitare l'inserto è possibile utilizzare la seguente istruzione (Utilizza il proyection invece un inserire comando, non dimenticare l'uso check="none"):

<sql-insert check="none">SELECT 1</sql-insert> 

Ecco un esempio collaudato:

<class name="Order" table="[view_Orders]" mutable="false"> 
    <id name="OrderId" type="System.Guid"> 
    <generator class="guid.comb"/> <!-- Change as you need --> 
    </id> 

    <!-- Other properties --> 
    <!-- <property name="GrandTotal"/> --> 

    <set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan"> 
    <key column="OrderId"/> 
    <one-to-many class="OrderLine"/> 
    </set> 

    <sql-insert check="none">SELECT 1</sql-insert> 
</class> 

<class name="OrderLine" table="OrderLine"> 
    <id name="OrderLineId" type="System.Guid"> 
    <generator class="guid.comb"/> <!-- Change as you need --> 
    </id> 

    <!-- Other properties --> 
    <!-- <property name="OrderId"/> 
    <property name="GrandTotal"/>/> --> 

</class>