Technology Experience
.NET World

Entity Framework 4, campi NULL/NOT NULL e chiamate ai RIA Services

Questa sera voglio parlarvi di un “dietro le quinte” di Entity Framework, ovvero una particolare situazione che mi è capitata durante le settimane scorse. Per la lettura di questo post assumo che sappiate:

  • cosa sia un database
  • cosa sia Entity Framework
  • cosa sia un’applicazione Silverlight o giù di lì
  • cosa siano i RIA Services

Dunque, cerco di arrivare al sodo in poche linee. Supponiamo di aver disegnato il seguente db:

db_diagram

Questa immagine arriva così com’è dal database diagram di SQL Server Management Studio. Forse è un database di cui avevo già parlato in passato in altri post. Abbiamo una tabella “Teams” contenente un po’ di squadre di calcio, ed abbiamo una tabella “Players” che mantiene l’elenco dei giocatori squadra per squadra. Il campo “IdTeam” della tabella “Players” serve proprio per mantenere questa relazione.

Ora, immaginiamo due scenari perseguibili:

  • la tabella “Players” contiene solo giocatori che effettivamente appartengono ad una squadra
  • la tabella “Players” contiene anche giocatori che NON appartengono ad alcuna squadra

Voi come realizzereste questa cosa? Il modo più semplice è quello di sfruttare in modo opportuno il campo “IdTeam” citato prima. Se “IdTeam” contiene NULL, quel record contiene un giocatore libero (non appartenente a nessuno), altrimenti contiene un ID che fa riferimento ad un Team esistente. Dal punto di vista del database, non ci sono particolari accrocchi da fare: è sufficiente impostare NULL / NOT NULL sul campo “IdTeam” ed il gioco è fatto (ed eventualmente rimuovere il valore predefinito dal campo stesso).

Come si comporta invece Entity Framework in questi due scenari?
Beh, devo dire che si comporta piuttosto bene. Ecco come appare il designer in entrambi i casi:

idTeam_NOT_NULL In questo primo caso, la relazione che lega le due entity è uno a molti.

Se nel designer selezioniamo IdTeam, vediamo che la proprietà Nullable è False.

idTeam_NULL In questo secondo caso, la relazione è 0..1.

Se nel designer selezioniamo IdTeam, vediamo che la proprietà Nullable è (None).

 

Una piccola precisazione. Ci sono almeno due piccole imperfezioni nella funzionalità di Update model from Database del designer di EF:

  1. non si accorge se cancellate un campo da una tabella (per adesso non ci riguarda)
  2. non si accorge se un certo campo diventa NULL / NOT NULL (questo invece ci riguarda eccome!)

Morale: se andate nel Management Studio e cambiate la proprietà Allow Nulls del campo “IdTeam” della tabella “Players”, e poi andate in VS2010 per refreshare il data model di EF, questi rimarrà invariato. Avete due possibilità: o cancellate la tabella e la riaggiungete (fattibile se le cose sono davvero semplici), oppure cambiate semplicemente la proprietà Nullable del campo (quest’ultima strada non l’ho provata).

Comunque sia, alla fine vi ritrovate il data model che rispecchia esattamente la struttura del vostro database.

E con le classi POCO, come siamo messi?
Da quando ho cominciato a lavorare con Silverlight 4 & EF4, ho subito utilizzato la possibilità di persistere oggetti generati tramite il template di classi POCO. Come si comporta questo template nei due casi esaminati sopra? Beh, anche qui la cosa è abbastanza intelligente!

Se il campo “IdTeam” non ammette NULL, allora è la proprietà è un semplice int.
Se il campo “IdTeam” ammette NULL, allora la proprietà IdTeam della nostra classe è una Nullable<int>, ed appare come segue:

public virtual Nullable<int> IdTeam
{
    get { return _idTeam; }
    set
    {
        try
        {
            _settingFK = true;
            if (_idTeam != value)
            {
                if (Team != null && Team.Id != value)
                {
                    Team = null;
                }
                _idTeam = value;
            }
        }
        finally
        {
            _settingFK = false;
        }
    }
}
private Nullable<int> _idTeam;

 

Il codice qui sopra proviene dalla definizione della classe Player.

Caricare dati attraverso i RIA Services

Altro balzo in avanti. Supponiamo adesso di aver chiuso tutto il giro, e di aver implementato una piccola applicazione Silverlight che da qualche parte mostri una ListBox con l’elenco delle squadre: quando l’utente clicca su una squadra, viene eseguito un command che carica i giocatori di quella squadra, e li mostra in un’altra ListBox. Ora – non ci interessa sapere che il tutto è stato fatto con M-V-VM e tutto il resto.

Quello che conta è che da qualche parte il nostro codice sarà una cosa tipo questa:

void loadPlayersCommandExecute(object value)
{
    if (value == null) return;
 
    Team t = value as Team;
 
    TeamsDomainContext dc = new TeamsDomainContext();
    var query = dc.GetPlayersQuery().Where(pl => pl.IdTeam.Equals(t.Id));
    dc.Load<Player>(query, callbackPlayers, null);
}

 

Questo è l’execute del command che carica i giocatori. Arriva in input un object, che è il SelectedItem della ListBox delle squadre.

Domanda per voi: se abbiamo impostato che “IdTeam” ammette NULL (che alla fin fine è lo scenario che ho applicato), questa riga di codice a runtime funziona? Per compilare compila, ma in esecuzione si schianta. E si schianta sull’ultima linea, al momento della chiamata a Load().

Perchè? Notare la clausola Where: la proprietà IdTeam di pl è Nullable<int>, mentre t.Id ritorna int.

In questo caso ho chiamato il metodo Equals su un reference type (Nullable<int>, o int? che dir si voglia), passando come parametro un value type (int). E tutto ciò non va affatto bene. L’exception contiene il seguente messaggio:

Expression of type ‘System.Int32’ cannot be used for parameter of type ‘System.Object’ of method ‘Boolean Equals(System.Object)’

Come risolvere la cosa. Basta sostituire la chiamata a Equals con il più banale ==. Ecco come diventa la query:

var query = dc.GetPlayersQuery().Where(pl => (pl.IdTeam == t.Id));

Ed il gioco è fatto!

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.