7

dato un tavolo "utente" e una tabella "Login" in MS SQL 2008:Come si aggiunge una colonna calcolata al modello EF4?

CREATE TABLE [dbo].[User_User](
    [UserID] [int] IDENTITY(1000,1) NOT NULL, 
    [UserName] [varchar](63) NOT NULL, 
    [UserPassword] [varchar](63) NOT NULL 
) 
CREATE TABLE [dbo].[Util_Login](
    [LoginID] [int] IDENTITY(1000,1) NOT NULL, 
    [User_UserID] [int] NOT NULL, -- FK REFERENCES [dbo].[User_User] ([UserID]) 
    [LoginDate] [datetime] NOT NULL, 
) 

Come si regola il mio User_User oggetto entità modello quadro per includere una colonna "UserLastLogin" che restituisce un MAX (LoginDate)?

so che posso creare un modello EF4 intorno ad una vista SQL:

CREATE VIEW [v_User_User] 
AS 
SELECT 
     [User_User].*, 
     (
       SELECT MAX(LoginDate) 
       FROM [Util_Login] 
       WHERE User_UserID = UserID 
     ) AS UserLastLogin 
FROM [User_User] 

Ma c'è un modo che posso solo modificare il modello User_User per includere il columnn calcolato?

EDIT: Sto cercando un modo per andare a prendere un utente o un elenco <utente> tra cui la data di Max (Util.LastLogin) in una singola query db.

risposta

5

Molto buona domanda, e sì, c'è un modo perfetto per raggiungere questo obiettivo in EF4:

proprietà personalizzate sono un modo per fornire le proprietà calcolate a entità. La buona notizia è che le proprietà personalizzate non devono necessariamente essere calcolate da altre proprietà esistenti sulla stessa entità, dal codice che stiamo per vedere, possono essere calcolate da qualsiasi cosa ci piaccia!

Ecco i passaggi:
prima creare una classe parziale e definire una proprietà personalizzata su di esso (per semplicità, ho assunto User_User tabella è stata mappata alla classe utente e Util_Login a Util)

public partial class User { 
    public DateTime LastLoginDate { get; set; } 
} 

quindi, come si può vedere qui, piuttosto che la creazione di una proprietà LastLoginDate nel modello, che sarebbe necessario per mappare nuovo al deposito dei dati, abbiamo creato la proprietà nella classe parziale e poi abbiamo HA ve l'opzione per popolare durante oggetto materializzazione o su richiesta se non si crede che ogni oggetto entità dovrà fornire tali informazioni.

Nel tuo caso il ricalcolo dei LastLoginDate proprietà personalizzata per ogni utente essere materializzato è utile in quanto credo che questo valore sarà accessibile per tutti (o almeno la maggior parte) delle entità essere materializzati. Altrimenti, dovresti considerare di calcolare la proprietà solo se necessario e non durante la materializzazione dell'oggetto.

Per questo, utilizzeremo lo ObjectContext.ObjectMaterialized Event che viene generato ogni volta che i dati vengono restituiti da una query poiché ObjectContext sta creando gli oggetti entità da tali dati. L'evento ObjectMaterialized è una cosa di Entity Framework 4. Quindi tutto ciò che dobbiamo fare è creare un gestore di eventi e iscriverlo all'evento ObjectMaterialized.

Il posto migliore per mettere questo codice (sottoscrivendo l'evento) è all'interno del OnContextCreated Metodo.Questo metodo viene chiamato dal costruttore dell'oggetto contesto e dal sovraccarico del costruttore , che è un metodo parziale senza implementazione, semplicemente una firma del metodo creata dal generatore di codice EF.

Ok, ora è necessario creare una classe parziale per il tuo ObjectContext. (Suppongo che il nome è UsersAndLoginsEntities) e sottoscrivere il gestore di eventi (ho chiamato Context_ObjectMaterialized) per ObjectMaterialized evento.

public partial class UsersAndLoginsEntities { 
    partial void OnContextCreated() { 
     this.ObjectMaterialized += Context_ObjectMaterialized; 
    } 
} 

L'ultimo passo (il vero lavoro) sarebbe quello di implementare questo gestore realmente popolare la proprietà personalizzata per noi, che in questo caso è molto semplice:

void Context_ObjectMaterialized(object sender, ObjectMaterializedEventArgs args) 
{ 
    if (args.Entity is User) {   
     User user = (User)args.Entity; 
     user.LastLoginDate = this.Utils 
       .Where(u => u.UserID == user.UserID) 
       .Max(u => u.LoginDate); 
    } 
} 


Speranza questo aiuta.

+0

Grazie per la risposta prolissa! Un problema che vedo è che richiederà una query sql per impostare LastLoginDate per ciascun utente. per esempio. Se ho un elenco di 50 utenti, ciò significa 1 query per popolare l'elenco e 50 query aggiuntive per popolare LastLoginDate per ciascun utente. O mi sbaglio per l'evento ObjectMaterialized? – uhleeka

+1

Ciò è corretto, l'evento viene attivato per ciascun oggetto utente nell'elenco.Se ciò non è auspicabile, l'altra opzione sarebbe quella di caricare con impazienza la proprietà di navigazione di Util e quindi eseguire un Max() su di esso sul lato client. –

+0

Sì, ho guardato con ansia caricando un po '... non sarebbe un carico impaziente di Util restituire tutte le righe Util associate all'utente? Quindi, essenzialmente, un Max() lato client dovrebbe buttare via tutto tranne la prima riga. – uhleeka

1

Dopo molte discussioni, ho finito con la seguente soluzione:

In primo luogo, creare una vista che contiene tutti i campi utente più un campo data LastLogin (dal mio post originale).

Dopo aver aggiunto l'utente (lo chiamano User_Model) e la vista dell'utente (chiamarlo UserView_Model) per il mio modello EF, ho creato una classe wrapper (chiamarlo User_Wrapper) attorno al User_Model e ha aggiunto un ulteriore Proprietà DateTime per LastLogin.

Ho modificato la classe User_Wrapper per recuperare da UserView_Model, quindi popolato il modulo utente sottostante riflettendo su tutte le proprietà condivise tra User_Model e UserView_Model. Infine, ho impostato la proprietà User_Wrapper.LastLogin in base alla User_View recuperata.

Tutte le altre funzioni (Crea, Aggiorna, Elimina ...) funzionano su User_Model. Solo il recupero utilizza UserView_Model.


Cosa ha fatto tutto questo? Ora ho solo una chiamata al database per popolare un singolo User_Wrapper o un Elenco <User_Wrapper>.

Gli svantaggi? Immagino che, poiché il mio UserView_Model non ha alcuna relazione associata, non sarei in grado di eseguire alcun caricamento ansioso usando EF ObjectContext. Fortunatamente, nella mia situazione, non trovo che sia un problema.

C'è un modo migliore?

1

Ho appena avuto una situazione in cui avevo bisogno di contare le proprietà per due entità correlate senza caricare le raccolte. Una cosa che ho scoperto è che è necessario avere MultipleActiveResultSets = True nella stringa di connessione per evitare che venga lanciata un'eccezione sull'handler di eventi ObjectMaterialized quando si interrogano altre entitàcollections.

Problemi correlati