2015-10-14 12 views
6

Attualmente sto facendo in questo modo:modo migliore per aggiornare i campi modificabili solo con Entity Framework

Per esempio:

public update(Person model) 
{ 
    // Here model is model return from form on post 
    var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault(); 
    db.Entry(oldobj).CurrentValues.SetValues(model); 
} 

Funziona, ma per esempio,

ho 50 colonne in il mio tavolo ma ho visualizzato solo 25 campi nel mio modulo (ho bisogno di aggiornare parzialmente il mio tavolo, con il rimanente 25 colonne mantengono lo stesso valore precedente)

So che può essere raggiunto "mappando colum ns uno per uno "o creando" campi nascosti per le restanti 25 colonne ".

Basta chiedersi c'è un modo elegante per farlo con meno sforzo e prestazioni ottimali?

+0

Crea modello di vista .. aggiungi solo i campi che vuoi .. Imposta valori – JamieD77

+0

Anche io creo View Model; lo stesso problema esiste ancora durante l'aggiornamento alla mia tabella "Persona". Per favore correggimi se mi manca qualcosa –

+0

il tuo viewmodel ha solo i 25 campi che stai visualizzando? – JamieD77

risposta

5

Questa è una buona domanda. Per impostazione predefinita, ho scoperto che finché il rilevamento delle modifiche è abilitato (è di default a meno che non lo si disattivi), Entity Framework farà un buon lavoro di applicare al database solo ciò che gli si chiede di modificare.

Quindi, se si modifica solo 1 campo rispetto all'oggetto e quindi si chiama SaveChanges(), EF aggiornerà solo quel campo 1 quando si chiama SaveChanges().

Il problema qui è che quando si mappa un modello di vista in un oggetto entità, tutti i valori vengono sovrascritti. Qui è il mio modo di gestire questo:

In questo esempio, si dispone di una sola entità chiamata Persona:

Person 
====== 
Id - int 
FirstName - varchar 
Surname - varchar 
Dob - smalldatetime 

Ora diciamo che vogliamo creare un modello di vista che aggiornerà solo Dob, e lasciare tutto altri campi esattamente come sono, ecco come lo faccio.

In primo luogo, creare un modello di visualizzazione:

public class PersonDobVm 
{ 
    public int Id { get; set; } 
    public DateTime Dob { get; set; } 

    public void MapToModel(Person p) 
    { 
     p.Dob = Dob; 
    } 
} 

Ora scrivere il codice o meno come segue (dovrete modificarlo per abbinare il vostro contesto nome, ecc):

DataContext db = new DataContext(); 
Person p = db.People.FirstOrDefault(); 

// you would have this posted in, but we are creating it here just for illustration 
var vm = new PersonDobVm 
{ 
    Id = p.Id, // the Id you want to update 
    Dob = new DateTime(2015, 1, 1) // the new DOB for that row 
}; 

vm.MapToModel(p); 
db.SaveChanges(); 

Il MapToModel il metodo potrebbe essere ancora più complicato e fare tutti i tipi di controlli aggiuntivi prima di assegnare i campi del modello di visualizzazione all'oggetto entità.

In ogni caso, il risultato quando SaveChanges è chiamato è il seguente SQL:

exec sp_executesql N'UPDATE [dbo].[Person] 
SET [Dob] = @0 
WHERE ([Id] = @1) 
',N'@0 datetime2(7),@1 int',@0='2015-01-01 00:00:00',@1=1 

Così si può vedere chiaramente, Entity Framework non si è tentato di aggiornare qualsiasi altro campo - solo il campo Dob.

So che nell'esempio si desidera evitare di codificare ogni compito manualmente, ma penso che questo sia il modo migliore. Puoi infilare tutto nel tuo VM in modo da non sporcare il tuo codice principale, e in questo modo puoi soddisfare esigenze specifiche (ad esempio tipi compositi, convalida dei dati, ecc.). L'altra opzione è usare AutoMapper, ma non penso che siano sicuri. Se usi un AutoMapper e scrivi "Dob" come "Doob" nella tua VM, non mapperebbe "Doob" a "Dob", né ti direbbe! Fallirebbe silenziosamente, l'utente penserebbe che tutto fosse ok, ma il cambiamento non sarebbe stato salvato.

Considerando che se hai digitato "Dob" come "Doob" nella tua VM, il compilatore ti avviserà che MapToModel() fa riferimento a "Dob" ma hai solo una proprietà nella tua VM chiamata "Doob".

Spero che questo ti aiuti.

+0

Grazie per il tuo suggerimento, ma la mappatura uno a uno è piuttosto caotica quando hai un numero molto grande di coulle. –

0

Ho risolto il problema utilizzando FormCollection per elencare l'elemento utilizzato nel modulo e modificare solo quelle colonne nel database.

Ho fornito il mio esempio di codice di seguito; Ottimo se può aiutare qualcun altro

// Here 
// collection = FormCollection from Post 
// model = View Model for Person 

var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault(); 
if (result != null) 
{ 
    List<string> formcollist = new List<string>(); 
    foreach (var key in collection.ToArray<string>()) 
    { 
     // Here apply your filter code to remove system properties if any 
     formcollist.Add(key); 
    } 
    foreach (var prop in result.GetType().GetProperties()) 
    { 
      if(formcollist.Contains(prop.Name)) 
      { 
        prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null)); 
      } 
    } 
    db.SaveChanges(); 
} 
+2

Anche se questo può funzionare e ti consente di risparmiare qualche codice, ecco una domanda a cui devi pensare: quanto funzionerà questo codice quando qualcuno cambia il nome di una proprietà nel tuo modello di visualizzazione? Immagina che qualcuno cambi "Dob" in "DateOfBirth" durante un refactoring. Come funzionerà il tuo codice allora? Non hai affatto la sicurezza del compilatore. Quello che succederà è che l'utente inserirà un valore in "DateOfBirth", ma non verrà applicato al campo Dob. L'utente dice di aver inserito un Dob, ma il tuo DB dirà di non averlo. –

4

Giuro per EntityFramework.Extended. Nuget Link

Esso consente di scrivere:

db.Person 
    .Where(x => x.ID == model.ID) 
    .Update(p => new Person() 
    { 
    Name = newName, 
    EditCount = p.EditCount+1 
    }); 

che è molto chiaramente tradotto in SQL.

+0

Penso che .Update non sia disponibile in Entity Framework –

+4

EntityFramework.Extended. È un pacchetto di nuget. – Visser

+0

È bello, ma ho trovato che non è di grande aiuto quando si usa EF in combinazione con Web API 2, perché il metodo '.Update()' deve istanziare il tipo (altrimenti si ottiene 'MemberInitException'). Se voglio farlo, ho bisogno di un mezzo per scorrere le proprietà sul mio oggetto in entrata per istanziare il nuovo richiesto da detto metodo :( – LeeCambl

Problemi correlati