2013-03-09 14 views
62

エンティティフレームワーク5(DBContext)を使用していますが、エンティティを深くコピーする(エンティティと関連するオブジェクトをすべてコピーする)ベストな方法を見つけようとしています。データベース内の新しいエンティティこれどうやってするの?私はCloneHelperのような拡張方法を調べましたが、それがDBContextに適用されるかどうかはわかりません。エンティティフレームワーク5エンティティのディープコピー/クローン

+0

で私のクラスをマーク。 com/CSEFDeepCloneObject-12a5cb95)しかし、私が理解しているように、EntityObjectの派生型はDbContext APIでサポートされていません – kypk

答えて

116

エンティティをクローニングの一つ安い簡単な方法は、このような何かを行うことです。

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

ここにトリックはAsNoTracking()です - あなたは、このようなエンティティをロードするとき、あなたの状況は知りませんSaveChangesを呼び出すと、新しいエンティティのように扱われます。

MySetMyPropertyへの参照を持っており、あなたもそのコピーをしたい場合は、単にIncludeを使用します。

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

このトリックはかなりの時間を私に安全に与えました:-)。しかし、私のDbContextの設定では、エンティティを自動的に追加できないという例外がありました。私はこの 'DirectControl(DbContext、IObjectContextAdapter).ObjectContext.AddObject(entitySetName、entity)'のようなObjectContextに行きます。 – Patrick

+0

これは 'dbContext、Select(x => {a = x、ab = ToList() '' AsNoTracking() 'を追加すると、クエリからデータが消失します。x.MyCollection.Where(g => g.Id> 2)}) – Eldho

+1

これはEFコアを使用して私にとって素晴らしい仕事をしました。しかし、EFがデータベースに重複行を挿入しようとするのを防ぐために、親とネストされたオブジェクトの主キーをGuid.Emptyに設定する必要がありました。整数キーを使用する場合、0に設定すると同じ効果があると思われます。 – agileMike

20

ここでは別のオプションがあります。

データを複製するためのクエリを特に実行する必要がないため、私はそれを好む場合があります。このメソッドを使用すると、すでにデータベースから取得したエンティティのクローンを作成できます。

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

このメソッドは、ソースからの現在の値を追加された新しい行にコピーします。

+0

これは、クローンを1つのSaveChangesにオリジナルのアップデートと共に挿入したい場合に効果的です – Dacker

+0

新しいオブジェクトが添付されていない場合、 'SetValues'は機能しません文脈'InvalidOperationException'例外がスローされます。エンティティをデタッチ状態で複製するだけの場合は、エンティティをコンテキストに追加し、現在の値を設定してからデタッチすることができます。 – Suncat2000

1

これは一般的なクローニングを可能にする一般的な拡張方法です。

ナゲットからSystem.Linq.Dynamicを取得する必要があります。

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; 
    } 

自分で実装する必要があるのは、GetKeyNameメソッドだけです。これはからreturn the first guid propertyまでのいずれかであるか、DatabaseGenerated(DatabaseGeneratedOption.Identity)]とマークされた最初のプロパティを返します。私の場合は

私はすでに、私は以下の[リンク](http://code.msdn.microsoftで説明リフレクションを使用してエンティティオブジェクトを複製/ディープクローンしようとした[DataServiceKeyAttribute("EntityId")]

private string GetKeyName<TEntity>() where TEntity : class 
    { 
     return ((DataServiceKeyAttribute)typeof(TEntity) 
      .GetCustomAttributes(typeof(DataServiceKeyAttribute), true).First()) 
      .KeyNames.Single(); 
    } 
関連する問題