2013-03-09 15 views
62

Używam Entity Framework 5 (DBContext) i staram się znaleźć najlepszy sposób, aby skopiować kopię obiektu (tj. Skopiować obiekt i wszystkie powiązane obiekty), a następnie zapisać nowe jednostki w bazie danych. Jak mogę to zrobić? Przyjrzałem się przy użyciu metod rozszerzenia, takich jak CloneHelper, ale nie jestem pewien, czy ma to zastosowanie do DBContext.Entity Framework 5 deep copy/clone podmiotu

+0

Próbowałem Deep klon/powielać obiekty podmiot korzystający odbicie opisanej w poniższym [LINK] (http://code.msdn.microsoft. com/CSEFDeepCloneObject-12a5cb95), ale jak rozumiem, typy pochodne EntityObject nie są obsługiwane przez interfejs API DbContext – kypk

Odpowiedz

116

Jeden tani łatwy sposób klonowania podmiot jest zrobić coś takiego:

var originalEntity = Context.MySet.AsNoTracking() 
          .FirstOrDefault(e => e.Id == 1); 
Context.MySet.Add(originalEntity); 
Context.SaveChanges(); 

trick Oto AsNoTracking() - podczas ładowania podmiotu takiego, context nie wiem o to i kiedy zadzwonisz SaveChanges, potraktuje to jak nowy byt.

Jeśli MySet ma odniesienie do MyProperty i chcesz kopię tego też wystarczy użyć Include:

var originalEntity = Context.MySet.Include("MyProperty") 
          .AsNoTracking() 
          .FirstOrDefault(e => e.Id == 1); 
+0

Ta sztuczka tylko zapewniła mi sporo czasu :-). Ale w mojej konfiguracji DbContext wystąpił wyjątek dotyczący braku możliwości automatycznego dodania encji. Musiałem przejść przez ObjectContext tak jak to DirectCast (DbContext, IObjectContextAdapter) .ObjectContext.AddObject (entitySetName, entity) ' – Patrick

+0

Mam projekcję z kontekstu ef takiego jak ten' dbContext, Select (x => {a = x, ab = x.MyCollection.Where (g => g.Id> 2)}) ToList() 'Jeśli dodaję' AsNoTracking() 'Istnieje utrata danych z zapytania. – Eldho

+1

To zadziałało dla mnie, używając EF Core. Jednak musiałem ustawić moje podstawowe klucze na obiekcie macierzystym i zagnieżdżonych na Guid.Empty, aby zapobiec EF próbując wstawić zduplikowane wiersze w bazie danych. Jeśli użyjesz klawiszy całkowitych, podejrzewam, że ustawienie ich na 0 miałoby taki sam efekt. – agileMike

20

Oto kolejna opcja.

Preferuję to w niektórych przypadkach, ponieważ nie wymaga to uruchomienia zapytania specjalnie w celu pobrania danych do sklonowania. Możesz użyć tej metody do tworzenia klonów jednostek, które już uzyskałeś z bazy danych.

//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(); 

Ta metoda kopiuje bieżące wartości ze źródła do nowego wiersza, który został dodany.

+0

Działa to świetnie, jeśli chcesz, aby klon był wstawiany razem z aktualizacjami oryginału w jednym SaveChanges – Dacker

+0

'SetValues' nie działa, jeśli nowy obiekt nie jest dołączony do kontekst. Zgłasza wyjątek 'InvalidOperationException'. Jeśli chcesz tylko sklonować obiekt w stanie odłączonym, możesz dodać obiekt do kontekstu, ustawić jego bieżące wartości, a następnie odłączyć je. – Suncat2000

1

Jest to ogólna metoda rozszerzenia, która umożliwia generyczne klonowanie.

Musisz pobrać System.Linq.Dynamic z 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; 
    } 

Jedyne, co trzeba samemu zaimplementować, to metoda GetKeyName. Może to być cokolwiek od do return the first guid property lub zwrócić pierwszą właściwość oznaczoną DatabaseGenerated(DatabaseGeneratedOption.Identity)].

W moim przypadku już zaznaczono moje zajęcia z [DataServiceKeyAttribute("EntityId")]

private string GetKeyName<TEntity>() where TEntity : class 
    { 
     return ((DataServiceKeyAttribute)typeof(TEntity) 
      .GetCustomAttributes(typeof(DataServiceKeyAttribute), true).First()) 
      .KeyNames.Single(); 
    } 
Powiązane problemy