L'Entity Framework e l'ObjectDataSource

di Andrea Zani, in .NET,

Anche se con l'SP1 del Framework 3.5 è stato introdotto il nuovo EntityDataSource per collegare direttamente questi nuovi oggetti a web control come il ListView o il GridView, l'utilizzo dell'ObjectDataSource rimane sempre la soluzione migliore: come dice Daniele, i datasource come l'SqlDataSource e compagnia bella sono solo veramente utili per le demo.

Di base non ci sono problemi. Solo di base però. Quando si cerca di fare qualcosa di più complesso saltano sempre fuori casini vari. Messo nella pagina un ListView collegato ad un bel ObjectDataSource che utilizza una classe apposita che ritorna le entità (nel mio caso con l'ausilio dell'Entity Framework) non ci sono problemi: persino altre entità memorizzate in proprietà vengono visualizzate correttamente. Come nell'esempio già visto:

gridview

I problemi nascono quando entrano in gioco gli update e gli insert. Come scritto sopra, di base, cioè con proprietà con tipi non complessi, non ci sono problemi. Definendo nell'InsertMethod dell'ObjectDataSource una nostra funzione come la seguente, tutto funziona:

[System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Insert)]
public static void InsertAuthor(Author author)
{
using (TestEntitiesModel.TestEntitiesCode context = new TestEntitiesModel.TestEntitiesCode())
{
context.addAuthors(author);
context.SaveChanges();
}
}

Che bello! Eh sì, ma l'entità qui trattata ha anche come proprietà la collection di Book collegati all'autore secondo il seguente schema:

entity framework

Per visualizzare il tutto in modalità edit si incotrano le prime rogne ma risolvibile. Ma cosa viene passato al metodo InsertAuthor prima mostrato? Tutto, tranne, ovviamente, eventuli aggiunte ai book. Qui l'esempio è un po' forzato, ma un caso reale potrebbe essere la creazione di un nuovo utente e il collegamento ad esso di una o più role per autorizzazione varie (visualizzate in modalità edit come lista con checkbox).

Come inviare il tutto in modo corretto a quella funzione o quasi?

Io ho solo trovato un espediente, ma spero ci siano soluzioni più semplici che finora mi sono sfuggite. Utilizzando l'evento On_ItemInserting dell'ListView possiamo forza l'aggiunta di tutte le informazioni utili:

protected void lw_authors_ItemInserting(object sender, ListViewInsertEventArgs e)
{
// In Values troviamo tutti i valori letti dal objectdatasource (BoundColumn, Bind, ecc...)
// Inserisce un campo aggiuntivo perla data presente nel db ma non del ListView
e.Values.Add("CreationData", DateTime.Now);
var books = new System.Data.Objects.DataClasses.EntityCollection();
// Metodi che richiedono le entità da collegare
books.Add( AZ.GetBookFromId(1) );
books.Add( AZ.GetBookFromId(2) );
e.Values.Add("Books", auths);
}

Innanzitutto, l'objectdatasource espone la connection come EntityConnection, quindi perché possa essere riconosciuta dobbiamo inserire tutti gli item selezionati in questa collection. Sembra tutto ok, in questo modo all'evento di inserimento dovrebbe essere passato il tutto correttamente. Dovrebbe! Richiamato questo codice si ha come output un errore chiarissimo in merito proprio al momento dell'Add:

The object could not be added to the EntityCollection or EntityReference. An object that is attached to an ObjectContext cannot be added to an EntityCollection or EntityReference that is not associated with a source object.

Allora ecco l'escamotage:

protected void lw_users_ItemInserting(object sender, ListViewInsertEventArgs e)
{
    // ...
    var books = new System.Data.Objects.DataClasses.EntityCollection<Book>();
    books.Add(new Book { Id = 1 });
    books.Add(new Book { Id = 2 });
    e.Values.Add("Books", auths);
}

Creo due nuove classi Book dove inserisco gli Id dei valori selezionati nell'interfaccia grafica (qui inseriti da codice per rendere il tutto più semplice). Ora il codice per l'inserimento dovrebbe accettare anche questi dati aggiuntivi, ma ecco il secondo problema della giornata: quelle entità verrebbero trattate come nuovi oggetti e inseriti nuovamente nel database. Ecco dunque l'ultima modifica al codice, questa volta nella funzione di inserimento:

[System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Insert)]
public static void InsertAuthor(Author author)
{
 using (TestEntitiesModel.TestEntitiesCode context = new TestEntitiesModel.TestEntitiesCode())
 {
  var coll = user.Books.Select(r => r.Id).ToArray<int>();
  user.Books.Clear();
  foreach (var item in coll)
    user.Books.Add( context.Books.Where( r=>r.Id==item ).FirstOrDefault() );
  // Oppure
  // foreach (var item in coll)
  //  user.Books.Add( context.GetObjectbyKey( item ) );
  context.addAuthors(author);
  context.SaveChanges();
 }
}

E tutto funziona correttamente. Ma spero ci siano soluzioni più immediate e semplici. Vero? Ditemi di sì! :(

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese