2009-08-06 17 views
5

Utilizzando LINQ-to-SQL, vorrei creare automaticamente record figlio quando si inserisce l'entità padre. Fondamentalmente, imitando il modo in cui un trigger di inserimento SQL funzionerebbe, ma in-code in modo che si possa eseguire qualche ulteriore elaborazione.Mimando SQL Insert Trigger con LINQ-to-SQL

Il genitore ha un'associazione con il figlio, ma sembra che non possa semplicemente aggiungere nuovi record figlio durante lo SubmitChanges() del DataContext.

Per esempio,

public partial class Parent 
{ 
    partial void OnValidate(System.Data.Linq.ChangeAction action) 
    { 
     if(action == System.Data.Linq.ChangeAction.Insert) 
     { 
      Child c = new Child(); 
      ... set properties ... 
      this.Childs.Add(c); 
     } 
    } 
} 

Questo sarebbe l'ideale, ma purtroppo la recente creazione Child record non viene inserito nel database. Ha senso, dal momento che DataContext ha una lista di oggetti/dichiarazioni e probabilmente non gli piacciono i nuovi oggetti aggiunti nel mezzo di esso.

Analogamente, l'intercettazione della funzione partial void InsertParent(Parent instance) nel DataContext e il tentativo di aggiungere il record Child produce lo stesso risultato: nessun errore, ma nulla aggiunto al database.

C'è un modo per ottenere questo tipo di comportamento senza aggiungere codice al livello di presentazione?

Aggiornamento: Sia i OnValidate() e InsertParent() funzioni sono chiamate dalla funzione del DataContext SubmitChanges(). Sospetto che questa sia la difficoltà intrinseca di ciò che sto cercando di fare: DataContext non consentirà l'inserimento di oggetti aggiuntivi (ad esempio tramite InsertOnSubmit()) mentre è in corso l'invio delle modifiche esistenti al database.

Idealmente mi piacerebbe mantenere tutto sotto una Transazione in modo che, se si verificano errori durante l'inserimento/aggiornamento, nulla è effettivamente cambiato nel database. Da qui i miei tentativi di imitare la funzionalità SQL Trigger, che consente di inserire automaticamente i record figlio attraverso una singola chiamata alla funzione SubmitChanges() di DataContext.

risposta

6

Se si desidera che accada appena prima che venga salvato; è possibile ignorare SubmitChanges e chiamare GetChangeSet() per ottenere le modifiche in sospeso. Cercate le cose che vi interessano (ad esempio, delta.Inserts.OfType<Customer>(), e apportare le modifiche necessarie.

Poi chiamata base.SubmitChanges(...).

Here's a related example, la manipolazione eliminazioni.

+0

Marc - grazie mille. Questo è esattamente quello che stavo cercando e funziona perfettamente! – BrandonB

+0

Marc, questo (e il tuo link correlato) è un ottimo suggerimento ed è stato molto utile anche a me, grazie! – Funka

2

Il metodo Add imposta solo un collegamento tra i due oggetti: non contrassegna l'elemento aggiunto per l'inserimento nel database. Per questo, è necessario chiamare InsertOnSubmit sull'istanza Table<Child> contenuta nel DataContext. Il problema, naturalmente, è che non esiste un modo innato per accedere a DataContext dal metodo che descrivi.

Si ha accesso ad esso implementando InsertParent nel DataContext, quindi mi piacerebbe andare su quella rotta (e utilizzare InsertOnSubmit invece di Add, ovviamente).

CURA ho pensato che il metodo parziale InsertParent sarebbe stato chiamato dalla DataContext ad un certo punto, ma nel guardare il mio codice che metodo sembra essere definita, ma mai a cui fa riferimento la classe generata. Quindi a che serve, mi chiedo?

+0

I miei pensieri esattamente - gli oggetti "figli" devono essere aggiunti al DataContext per mezzo di "InsertOnSubmit" per poter essere memorizzato quando SubmitChanges() viene chiamato. –

+0

Ben: verrà chiamato un metodo parziale, se effettivamente implementato. Finché è solo appena definito (ma non implementato), non succede nulla. –

+0

Giusto, ma deve ancora esserci un call point. Nel mio esempio DataContext, non riesco a trovare alcuna chiamata a questi metodi parziali dal codice generato. –

0

In linq to sql si esegue un "trigger" eseguendo una classe parziale sul file dbml e quindi inserendo un metodo parziale. Ecco un esempio che non farebbe nulla perché chiama la cancellazione incorporata.

partial void DeleteMyTable(MyTable instance) 
{ 
    //custom code here 
    ExecuteDynamicDelete(instance); 
    //or here :-) 
}