57 messaggi dal 24 marzo 2008
Ciao Ragazzi,
vi scrivo per avere suggerimenti in merito ad Entity Framework, che ho iniziato a studiare recentemente e con il quale ho provato a realizzare un piccolo database di contatti come progetto personale di studio.

Il mio dubbio maggiore è legato alla gestione di entità collegate, derivanti dalla mappatura delle relazioni provenienti dal db.

Il database e' molto semplice e si compone di sole 3 tabelle:

- Anagrafica (IdPersona, Nominativo, IdTipoContatto)
- Riferimenti (IdRiferimento, IdPersona, IdTipoRiferimento, Dettagli);
- TipoContatto (IdTipoContatto, Descrizione)
- TipoRiferimento (IdTipoRiferimento, Descrizione)

Le relazioni sono

- Uno-a-molti Anagrafiche-Riferimenti (IdPersona);
- Uno-a-molti TipoContatto-Anagrafiche (IdTipoContatto);
- Uno-a-molti TipoRiferimento-Riferimenti (IdTipoRiferimento);

Per inserire un nuovo contatto utilizzo il codice che segue (i dati relativi alla tipologia di contatto e riferimento vengono presi da combobox)


using (dbContattiEntities db = new dbContattiEntities())
{
   int tipoCont = (int)cmbTipoContatto.SelectedValue;
   int tipoRif = (int)cmbTipoRiferimento.SelectedValue;

   //Creazione nuova anagrafica
   Anagrafica anag = new Anagrafica()
   {
      Nominativo = txtNominativo.Text.Trim(),
      TipoContatto = db.TipoContatto.First(p => p.IdTipoContatto == tipoCont)
   };

   //Creazione nuovo riferimento
   Riferimenti rif = new Riferimenti()
   {
      Anagrafica = anag,
      TipoRiferimento = db.TipoRiferimento.First(p => p.IdTipoRiferimento == tipoRif),
                    Dettagli = txtRiferimento.Text.Trim()
   };

   //Aggiunge le entità al db
   db.AddToAnagrafica(anag);
   db.AddToRiferimenti(rif);

   //Salva le modifiche
   db.SaveChanges();

}



Per recuperare un singolo contatto e tutti i riferimenti correlati utilizzo il metodo Include


using (dbContattiEntities db = new dbContattiEntities())
{
  Anagrafica anag = db.Anagrafica.Include("Riferimenti").First(p => p.IdAnagrafica == _idAnag);

}



Secondo voi questo approccio è corretto oppure è possibile utilizzare metodologie più performanti (in relazione soprattutto al ridurre al minimo gli accessi al db)

Grazie :)

Nulla è reale...tutto è lecito...
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,
quando crei una nuova Anagrafica, puoi semplicemente valorizzare la sua proprietà IdTipoContatto. Non è necessario estrarre l'entità TipoContatto dal database, tanto il suo ID lo conosci già. Quindi prova in questo modo, così risparmi una query:

   //Creazione nuova anagrafica
   Anagrafica anag = new Anagrafica()
   {
      Nominativo = txtNominativo.Text.Trim(),
      IdTipoContatto = tipoCont
   };

Tu stai usando il designer di Visual Studio per creare le tue classi di entità, giusto? In questo caso, assicurati di includere le colonne foreign key quando aggiungi le tabelle al modello, altrimenti la proprietà IdTipoContatto non sarà disponibile. Qui vedi dove bisogna mettere la spunta.
http://blogs.msdn.com/blogfiles/msdnforum/WindowsLiveWriter/ForeignKeyAssociationinEntityFramework4_D350/image_thumb_1.png

dadox77 ha scritto:

Per recuperare un singolo contatto e tutti i riferimenti correlati utilizzo il metodo Include

Certo, va benissimo. Grazie al metodo Include carichi preventivamente anche i riferimenti di ogni Anagrafica. Entity framework produrrà una query SQL comprendente una JOIN tra la tabella Anagrafica e quella Riferimenti. Questa tecnica, definita eager loading, è da preferire quando sai già a priori che dovrai leggere sia le proprietà dell'Anagrafica che quelle dei relativi Riferimenti.

Vedo che i Riferimenti hanno a loro volta un'entità collegata, la TipoRiferimento. Se sai di dover leggere anche la sua proprietà Descrizione, allora usa l'Include ancora una volta.

using (dbContattiEntities db = new dbContattiEntities())
{
  Anagrafica anag = db.Anagrafica.Include("Riferimenti").Include("Riferimenti.TipoRiferimento").First(p => p.IdAnagrafica == _idAnag);
}

Caricando le entità preventivamente ti eviti il problema chiamato select n+1. Ad ogni modo, mentre stai sviluppando l'applicazione, tieni sempre aperto il Sql profiler per renderti conto di quali query (e quante) vengano inviate al database.

Buono studio! :)
ciao
Modificato da BrightSoul il 18 giugno 2012 23.18 -

Enjoy learning and just keep making
57 messaggi dal 24 marzo 2008
Ciao Bright, felice di rileggerti :)



Tu stai usando il designer di Visual Studio per creare le tue classi di entità, giusto? In questo caso, assicurati di includere le colonne foreign key quando aggiungi le tabelle al modello, altrimenti la proprietà IdTipoContatto non sarà disponibile. Qui vedi dove bisogna mettere la spunta.



Si utilizzo VS per creare le entity class, ma da quello che ho potuto vedere la versione 2008 dell'IDE (framework 3.5) sembrerebbe non dare la possibilità di includere direttamente le foreign keys nel modello. Purtroppo mi sono reso conto di non aver specificato la versione di framework in uso quando ho aperto la discussione.



...tieni sempre aperto il Sql profiler per renderti conto di quali query (e quante) vengano inviate al database.



Anche in questo caso sono in difficoltà in quanto utilizzo Sql Server Express e nel management studio non ho a disposizione il sql profiler...come ti dicevo sono in un ambiente di sviluppo "casalingo" :)

In ogni caso ho capito che intendi...si tratterebbe di accedere direttamente al value della foreign key dell'entità che sto creando/modificando, anziche alla sua intera entità collegata, che in quest'ultimo caso comporta l'esecuzione di una query in fase di istanza della stessa per recuperarne i valori.



Grazie al metodo Include carichi preventivamente anche i riferimenti di ogni Anagrafica. Entity framework produrrà una query SQL comprendente una JOIN tra la tabella Anagrafica e quella Riferimenti. Questa tecnica, definita eager loading, è da preferire quando sai già a priori che dovrai leggere sia le proprietà dell'Anagrafica che quelle dei relativi Riferimenti.



Ottimo...perfetto :)
In ogni caso non utilizzando la Include, il codice che ti riporto di seguito solleva un'eccezione di non istanza della classe TipoContatto


            using (DbTestEntities db = new DbTestEntities())
            {
                Anagrafica anag = db.Anagrafica.First(p => p.IdAnagrafica == idAnag);

                string tipoCont = anag.TipoContatto.Descrizione;

            }



Ho letto in proposito che Entity Framework non supporta il lazy loading e che quindi tutte le operazioni che implicano il caricamento di entità correlate devono essere esplicitate (sia in eager loading oppure in deferred con il metodo load)

quindi il codice precedente andrebbe modificato cosi:


            using (DbTestEntities db = new DbTestEntities())
            {
                Anagrafica anag = db.Anagrafica.First(p => p.IdAnagrafica == idAnag);
                anag.TipoContattoReference.Load();

                string tipoCont = anag.TipoContatto.Descrizione;

            }



Tuttavia nel caso precedente avrei 2 query, giusto?

Grazie come sempre di tutti i tuoi preziosi suggerimenti, ci rileggiamo presto :)

Davide

Nulla è reale...tutto è lecito...
11.886 messaggi dal 09 febbraio 2002
Contributi
heilà :) ben trovato

dadox77 ha scritto:

la versione 2008 dell'IDE (framework 3.5) sembrerebbe non dare la possibilità di includere direttamente le foreign keys nel modello

ah, no, infatti con la versione 1.0 di Entity Framework non puoi esporre le chiavi secondarie nel modello, dato che sono già coinvolte in un'associazione.

Dunque sei obbligato a caricare le entità collegate, come stavi già facendo.

dadox77 ha scritto:

nel management studio non ho a disposizione il sql profiler...

ok, in questo caso prova ad usare un profiler gratuito. So dell'esistenza di "Anjlab Sql Profiler":
https://sites.google.com/site/sqlprofiler/
Ma non riesco a capire che cacchio hanno fatto, sembra che gli abbiano cambiato nome e, nonostante ci sia scritto "absolutely free", poi vengo ridirezionato al download di un prodotto trial. Prova lo stesso, magari la trial non ha scadenza.

In alternativa, puoi anche fare a meno di un profiler. Ci sono altri modi per ispezionare le query prodotte da Entity Framework. Uno è quello di castare L'IQueryable restituito da una query linq2entities sul tipo ObjectQuery e usare il suo metodo .ToTraceString.

Oppure esistono anche questi wrappers ma richiedono VS2010.

A proposito... non ti conviene passare a Visual Studio 2010, versione express? Così puoi usare Entity Framework 4, che supporta anche il "nuovo" approccio code-first così non avrai bisogno del designer.
E' uscita anche le RC di Visual Studio 2012 Express, dagli un'occhiata.
http://www.microsoft.com/visualstudio/11/en-us/downloads#express-web

dadox77 ha scritto:

Ho letto in proposito che Entity Framework non supporta il lazy loading

Sì sì, lo supporta, ma dalla versione 4.

dadox77 ha scritto:
Tuttavia nel caso precedente avrei 2 query, giusto?

Già, l'explicit loading causa l'esecuzione di una seconda query. Anche il lazy loading farebbe lo stesso ma in questo caso il vantaggio è che la seconda query avverrebbe in maniera trasparente. Ho detto "vantaggio"? Spesso non lo è perché se non stai attento introduci nella tua applicazione il problema select n+1 e senza accorgertene. Per questo è sempre importante monitorare le query che vanno al db. Alcuni potrebbero preferire una soluzione più radicale, che è quella di disabilitare il lazy loading completamente.

Comunque, ti sarebbe possibile migrare il progetto al framework 4? Potresti avere tutti i vantaggi di cui abbiamo discusso finora.

ciao
Modificato da BrightSoul il 23 giugno 2012 16.51 -

Enjoy learning and just keep making
57 messaggi dal 24 marzo 2008
Ciao Bright :)

in realtà non ho alcun problema a passare a vs 2010 poichè, come ti dicevo, sto studiando entity framework per un aggiornamento personale e non per esigenza lavorative.

A lavoro ancora utilizziamo VS 2003 e 2008 (anche se sto spingendo per aggiornare le applicazioni per portarle tutte almeno su framework 3.5), ma non è escluso che si possa pensare di acquistare VS 2010. Tutto dipenderà dai progetti che entreranno nel corso del prossimo anno.

Grazie di tutto,
Davide

Nulla è reale...tutto è lecito...
11.886 messaggi dal 09 febbraio 2002
Contributi
prego :)

...allora aspettate ad effettuare l'aggiornamento; presto uscirà Visual Studio 2012 e potrete decidere in tutta libertà se iniziare a sviluppare per il framework 4.5 con Entity Framework 5.
Non ci sono ancora date di rilascio ufficiali sulla roadmap 2012 ma penso che sia ragionevole aspettarsi un rilascio "entro la fine dell'anno".

buon weekend!
Modificato da BrightSoul il 23 giugno 2012 19.03 -

Enjoy learning and just keep making
57 messaggi dal 24 marzo 2008
Ho installato visual web developer 2010 per testare le novità che mi hai segnalato (utilissima anche la possibilità di disabilitare il lazy loading a livello di modello per evitare query "a nostra insaputa" :) )

Mi è venuta in mente un altro comportamento che nei "vecchi" dataset ADO.NET era possibile abilitare o no, e cioè l'aggiunta di uno statement di select basato su SCOPE_IDENTITY@@ dopo i comandi di insert-update per aggiornare eventuali campi identity o calcolati.

Ho visto che in entity framework questa cosa è attiva di default (creando una nuova anagrafica specificando solamente Nominativo e IdTipoContatto, il campo IdAnagrafica è valorizzato dopo il SaveChanges())

Che tu sappia è possibile eventualmente gestire questa opzione? Sarebbe comunque una query in più che non sempre è utile fare.

Grazie di nuovo :)

Davide

Nulla è reale...tutto è lecito...
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,
dadox77 ha scritto:

Che tu sappia è possibile eventualmente gestire questa opzione?

uhm, a naso penso che non sia possibile. Faccio questo ragionamento: dopo il SaveChanges() l'entità deve passare dallo stato "Added" ad "Unchanged" ma, affinché l'ObjectContext possa rispettare il pattern Identity Map, deve creargli una EntityKey "definitiva" che la rappresenterà univocamente all'interno del contesto. Penso sia questo il motivo per cui va sempre recuperare il nuovo ID dal database, mediante la funzione scope_identity().

Comunque questa seconda query non ha alcun particolare impatto sulle prestazioni. In fondo, il comando insert e la query select vengono eseguite in un solo round-trip al database.

Se proprio vuoi evitare quella seconda select devi ricorrere al metodo ObjectContext.ExecuteStoreCommand che ti lascia eseguire un comando SQL arbitrario (una insert, nel tuo caso).

Il problema serio può nascere quando hai tanti inserimenti da fare. Qui sì che noteresti dei rallentamenti perché ogni entità verrebbe inserita singolarmente.

Per fare inserimenti massivi dovresti (ahimé) rinunciare ad Entity Framework ed usare altri sistemi, tipo:

ciao
Modificato da BrightSoul il 25 giugno 2012 22.59 -

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.