2013-06-18 11 views
9

Ho un modello che ottengo da una richiesta POST. Poiché la mia vista definisce il suo tipo POCO, anche l'oggetto creato dai dati inviati è un POCO. Essendo un POCO, non ha diverse proprietà virtuali sovrascritte. Pertanto, tali proprietà virtuali restituiscono null. Questo, a sua volta, significa che devo fare query separate basate sulle chiavi esterne per navigare attraverso le sue proprietà (se voglio fare qualcosa di più complesso del semplice salvataggio).Ho un POCO, posso ottenere un proxy da DbContext?

Posso ottenere il proxy che ha tutte le funzionalità sovrascritte, visto il POCO del mio modello?

(Avevo pensato che questo è ciò che è stato per db.Entry().Entity, ma mi restituisce comunque l'oggetto POCO, non il proxy. Sto controllando il tipo di esecuzione dell'oggetto con il mouse-over durante le pause breakpoint.)

+0

possibile duplicato di [Converti oggetto POCO in oggetto Proxy in EntityFramework] (http://stackoverflow.com/questions/8174200/convert-poco-object-to-proxy-object-in-entityframework) – Dennis

risposta

7

Qualcosa sulla falsariga di questo codice farà quello che ti serve. Ho usato automapper per copiare i valori dall'entità passata alla versione con proxy.

Il codice controlla se l'entità passata è un proxy o meno e la gestisce di conseguenza.

public class Repository<T> where T : class 
{ 
    private readonly Context context; 
    private bool mapCreated = false; 
    public Repository(Context context) 
    { 
     this.context = context; 
    } 

    protected virtual T InsertOrUpdate(T e, int id) 
    { 
     T instance = context.Set<T>().Create(); 
     if (e.GetType().Equals(instance.GetType())) 
      instance = e; 
     else 
     { 
      if (!mapCreated) 
      { 
       Mapper.CreateMap(e.GetType(), instance.GetType()); 
       mapCreated = true; 
      } 
      instance = Mapper.Map(e, instance); 
     } 

     if (id == default(int)) 
      context.Set<T>().Add(instance); 
     else 
      context.Entry<T>(instance).State = EntityState.Modified; 

     return instance; 
    } 
} 

UPDATE versione come descritto da @Colin nei commenti che non ha bisogno automapper

public class Repository<T> where T : class 
{ 
    private readonly Context context; 
    public Repository(Context context) 
    { 
     this.context = context; 
    } 

    protected virtual T InsertOrUpdate(T e, int id) 
    { 
     T instance = context.Set<T>().Create(); 
     if (e.GetType().Equals(instance.GetType())) 
     { 
      instance = e; 
     } 
     else 
     { 
      DbEntityEntry<T> entry = context.Entry(instance); 
      entry.CurrentValues.SetValues(e); 
     } 

     context.Entry<T>(instance).State = 
      id == default(int) 
       ? EntityState.Added 
       : EntityState.Modified; 

     return instance; 
    } 
} 
+1

Grazie mille ! Questo è esattamente ciò di cui avevo bisogno per ottenere in modo elegante il proxy appropriato con valori corretti per vari tipi di modelli. –

+1

@TheodorosChatzigiannakis Ho aggiornato il codice per utilizzare la versione generica di 'Entry '. – qujck

+0

Sì, ho notato. Ho già personalizzato il tuo codice iniziale per quello che avevo tra le mani (che era una libreria di gestori generici per varie procedure standard con EF). La tua idea mi ha davvero aiutato ad andare avanti. Grazie per la tua risposta di alta qualità! –

0

Se si vuole fare questo tramite il controller MVC, si potrebbe usare qualcosa di simile questo come azione:

[HttpPost] 
    public ActionResult Update(int? id, FormCollection form) 
    { 
     // assumes repository will handle 
     // retrieving the entity and 
     // including and navigational properties 
     var entity = repository.Get(id); 
     if (entity == null) 
     { 
      throw new InvalidOperationException(string.Format("Not found: {0}", id)); 
     } 
     if (TryUpdateModel(entity)) 
     { 
      try 
      { 
       // 
       // do other stuff, add'l validation, etc 
       repository.Update(entity); 
      } 
      catch (Exception ex) 
      { 
       // 
       // exception cleansing/handling 
       // add'l model errors 
       return View(entity); 
      } 
      return View("Success", entity); 
     }    

     return View(entity); 
    } 
+0

Grazie per la tua risposta! Questo è un approccio valido ed è, infatti, simile a quello che stavo già usando. Tuttavia, ho ritenuto un po 'inutile ri-interrogare il database solo per aggirare i limiti di EF. Ecco perché stavo esplorando altre possibili opzioni per recuperare il proxy. –

1

db.Ent . Ry() Entity restituirà sempre un POCO, e non tornerà l'oggetto proxy che gestisce l'implementazione di proprietà di navigazione virtuale:

var o = db.Entry(myPoco).Entity; // always returns a POCO 

Sarà normalmente ottenere un oggetto proxy invece di un POCO quando si chiama Find() o Where() rispetto al contesto del database. Tuttavia,, nel contesto in cui un oggetto viene prima aggiunto al database, questi metodi restituiscono (in modo imprevisto?) Il POCO anziché l'oggetto proxy. In realtà hanno a lasciare il contesto e aprirne uno nuovo per ottenere il proxy:

 // create a new POCO object, and connect to it to another object already in the DB 
     MyPoco myPoco = new MyPoco(); 
     myPoco.MyOtherPocoId = myPoco2.MyOtherPocoId; // make reference to existing object 

     using (var db = new MyContext()) 
     { 
      // Add myPoco to database. 
      db.MyPocos.Add(myPoco); 
      db.SaveChanges(); 

      // One would think you get a proxy object here, but you don't: just a POCO 
      var test10 = db.MyPocos.Find(myPoco.Id);      // returns a MyPoco       
      var test11 = db.MyPocos.Where(x => x.Id == myPoco.Id).First(); // returns a MyPoco 
      var test12 = db.Entry(myPoco).Entity;       // returns a MyPoco 

      // ...so, you can't access the referenced properties through virtual navigation properties: 
      MyOtherPoco otherPoco1 = myPoco.Poco2; // returns NULL 
     } 

     // leave the context and build a new one 

     using (var db = new MyContext()) 
     { 
      // Now, the same Find() and Where() methods return a proxy object 
      var test20 = db.MyPocos.Find(myPoco.Id); // returns a proxy object 
      var test21 = db.MyPocos.Where(x => x.Id == myPoco.Id).First(); // returns a proxy object 

      // ...which means the virtual properties can be accessed as expected: 
      MyOtherPoco otherPoco = myPoco.Poco2; // works as expected 

      // Note that db.Entry().Entity still returns a POCO: 
      var test22 = db.Entry(myPoco).Entity; // returns a MyPoco 
     } 

Ci può essere qualche incantesimo magico per rendere il contesto in cui viene aggiunto l'oggetto indietro dare un oggetto proxy, ma io porto trovarlo.

+0

Grazie! Questo assomiglia a quello che avevo già. È un peccato che EF non abbia nulla da fornire automaticamente un proxy pertinente. Ma credo che richiedere un proxy vuoto e mapparlo manualmente (o forse) con uno strumento come Automapper, come suggerito in un'altra risposta) è il più vicino possibile a questa funzionalità. –

Problemi correlati