Sto utilizzando Entity Framework 5 (DBContext
) e sto cercando di trovare il modo migliore per copiare in profondità un'entità (ovvero copiare l'entità e tutti gli oggetti correlati) e quindi salvare la nuove entità nel database. Come posso fare questo? Ho esaminato l'utilizzo di metodi di estensione come CloneHelper
ma non sono sicuro se si applica a DBContext
.Entity Framework 5 copia/clone di un'entità
risposta
Un modo semplice a buon mercato di clonazione di un soggetto è quello di fare qualcosa di simile:
var originalEntity = Context.MySet.AsNoTracking()
.FirstOrDefault(e => e.Id == 1);
Context.MySet.Add(originalEntity);
Context.SaveChanges();
Il trucco è AsNoTracking() - quando si carica un soggetto come questo, il contesto di non conoscono e quando chiami SaveChanges, lo tratterà come una nuova entità.
Se MySet
ha un riferimento a MyProperty
e si desidera una copia di esso troppo, basta usare un Include
:
var originalEntity = Context.MySet.Include("MyProperty")
.AsNoTracking()
.FirstOrDefault(e => e.Id == 1);
Questo trucco mi ha permesso solo un po 'di tempo :-). Ma con la mia configurazione di DbContext c'era un'eccezione sul non essere in grado di aggiungere automaticamente l'entità. Ho dovuto andare su ObjectContext come questo 'DirectCast (DbContext, IObjectContextAdapter) .ObjectContext.AddObject (entitySetName, entity)' – Patrick
Ho una proiezione dal contesto ef come questo 'dbContext, Select (x => {a = x, ab = x.MyCollection.Where (g => g.Id> 2)}). ToList() 'Se aggiungo' AsNoTracking() 'C'è una perdita di dati dalla query. – Eldho
Questo ha funzionato perfettamente con me utilizzando EF Core. Tuttavia ho dovuto impostare le chiavi primarie sul genitore e gli oggetti nidificati su Guid.Empty per impedire a EF di provare a inserire righe duplicate nel database. Se si utilizzano chiavi intere, sospetto che impostarle su 0 avrebbe lo stesso effetto. – agileMike
Ecco un'altra opzione.
Lo preferisco in alcuni casi perché non richiede di eseguire una query specifica per ottenere dati da clonare. Puoi usare questo metodo per creare cloni di entità che hai già ottenuto dal database.
//Get entity to be cloned
var source = Context.ExampleRows.FirstOrDefault();
//Create and add clone object to context before setting its values
var clone = new ExampleRow();
Context.ExampleRows.Add(clone);
//Copy values from source to clone
var sourceValues = Context.Entry(source).CurrentValues;
Context.Entry(clone).CurrentValues.SetValues(sourceValues);
//Change values of the copied entity
clone.ExampleProperty = "New Value";
//Insert clone with changes into database
Context.SaveChanges();
Questo metodo copia i valori correnti dall'origine in una nuova riga che è stata aggiunta.
Funziona alla grande se si desidera che il clone venga inserito insieme agli aggiornamenti dell'originale in un SaveChanges – Dacker
'SetValues' non funziona se il nuovo oggetto non è collegato a il contesto. Genera un'eccezione 'InvalidOperationException'. Se tutto ciò che si vuole fare è clonare l'entità in uno stato distaccato, è possibile aggiungere l'entità al contesto, impostarne i valori correnti e quindi staccarla. – Suncat2000
Questo è un metodo di estensione generica che consente la clonazione generica.
Devi recuperare System.Linq.Dynamic
da nuget.
public TEntity Clone<TEntity>(this DbContext context, TEntity entity) where TEntity : class
{
var keyName = GetKeyName<TEntity>();
var keyValue = context.Entry(entity).Property(keyName).CurrentValue;
var keyType = typeof(TEntity).GetProperty(keyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).PropertyType;
var dbSet = context.Set<TEntity>();
var newEntity = dbSet
.Where(keyName + " = @0", keyValue)
.AsNoTracking()
.Single();
context.Entry(newEntity).Property(keyName).CurrentValue = keyType.GetDefault();
context.Add(newEntity);
return newEntity;
}
L'unica cosa che devi implementare è il metodo GetKeyName. Può essere qualsiasi cosa da a return the first guid property
o restituire la prima proprietà contrassegnata con DatabaseGenerated(DatabaseGeneratedOption.Identity)]
.
Nel mio caso ho già segnato le mie lezioni con [DataServiceKeyAttribute("EntityId")]
private string GetKeyName<TEntity>() where TEntity : class
{
return ((DataServiceKeyAttribute)typeof(TEntity)
.GetCustomAttributes(typeof(DataServiceKeyAttribute), true).First())
.KeyNames.Single();
}
ho cercato di clonare profondo/duplicare gli oggetti entità utilizzando la riflessione descritto al seguente [link] (http://code.msdn.microsoft. it/CSEFDeepCloneObject-12a5cb95) ma, a quanto ho capito, i tipi derivati EntityObject non sono supportati dall'API DbContext – kypk