EntityObject のサブクラスの浅いコピー/クローンを作成する
-
13-12-2019 - |
質問
データベースには監査テーブルがあり、更新時に古い値と新しい値が XML にシリアル化され、同じ行に保存されます。現在、オブジェクトは次のようにディープ クローン化されています。
public EntityObject CloneEntity(EntityObject obj)
{
DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());
MemoryStream memoryStream = new MemoryStream();
dcSer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);
return newObject;
}
これは機能しますが、ディープ クローンから取得された関連レコードにより、データベースから数十万件の読み取りが行われ、膨大な量のデータが生成されます。 dcSer.WriteObject(memoryStream, obj)
, DB に書き戻されるデータの量は言うまでもなく、最終的な MemoryStream サイズは約 200MB になります。理想的ではありません。
したがって、代わりにメンバーごとのクローンを実行したいと思います。メンバーごとのクローンではオブジェクト参照が除外され、関連するすべての Entity Framework モデルのコピーが回避されると理解しているためです。
そこで私はこれを行いました:
public EntityObject CloneEntity(EntityObject obj)
{
EntityObjectAuditable auditable = (EntityObjectAuditable)obj; // invalid cast exception
return auditable.ShallowCopy();
}
// ....
public class EntityObjectAuditable : EntityObject
{
public EntityObjectAuditable ShallowCopy()
{
return (EntityObjectAuditable)this.MemberwiseClone();
}
}
しかし、受信したデータの実際の型が異なるため、無効なキャスト例外が発生します。 EntityObject
テーブル自体に関連するサブクラスです。
拡張メソッドを使用してアクセスしようとしました MemberwiseClone()
, ただし、拡張メソッドは保護されたメソッドにアクセスできません。
では、汎用 EntityObject の浅いコピーを作成するにはどうすればよいでしょうか?
解決
私の最初の推奨事項は、これを試してみることです。これは、現在行っていることと似ていますが、オーバーヘッドがほとんどなく、私にとってはうまくいきました。
DataContractSerializationUtils.SerializeToXmlString(Entity, throwExceptions);
また、私は以前にこの方法を使用して成功しましたが、出力が冗長すぎるとは思いませんでした。今やっていることとほぼ同じだと思われます。
/// <summary>
/// Creates an exact duplicate of the entity provided
/// </summary>
/// <param name="source">The source copy of the entity</param>
/// <returns>An exact duplicate entity</returns>
public TEntity Clone(TEntity Source)
{
// Don’t serialize a null object, simply return the default for that object
if (ReferenceEquals(Source, null))
{
return default(TEntity);
}
var dcs = new DataContractSerializer(typeof (TEntity));
using (Stream stream = new MemoryStream())
{
dcs.WriteObject(stream, Source);
stream.Seek(0, SeekOrigin.Begin);
return (TEntity) dcs.ReadObject(stream);
}
}
これらのオプションのどちらも魅力的ではないと思われる場合は、リフレクションを使用してソース エンティティからプロパティをコピーする短い関数を作成することをお勧めします。
他のヒント
から:
http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4.
シリアル化よりもはるかに効率的かつ高速です。まさにあなたが探しているものです。基本的に、リフレクションを使用して必要なプロパティを同じタイプの新しい EntityObject にコピーし、ジェネリックスを使用することで EntityObject から派生した任意のクラスでこれを行うことができます。
public static T CopyEntity<T>(MyContext ctx, T entity, bool copyKeys = false) where T : EntityObject
{
T clone = ctx.CreateObject<T>();
PropertyInfo[] pis = entity.GetType().GetProperties();
foreach (PropertyInfo pi in pis)
{
EdmScalarPropertyAttribute[] attrs = (EdmScalarPropertyAttribute[])pi.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);
foreach (EdmScalarPropertyAttribute attr in attrs)
{
if (!copyKeys && attr.EntityKeyProperty)
continue;
pi.SetValue(clone, pi.GetValue(entity, null), null);
}
}
return clone;
}
たとえば、次のようなエンティティがあるとします。ナビゲーション プロパティを持つ顧客:注文。次に、上記の方法を使用して、次のように顧客とその注文をコピーできます。
Customer newCustomer = CopyEntity(myObjectContext, myCustomer, false);
foreach(Order order in myCustomer.Orders)
{
Order newOrder = CopyEntity(myObjectContext, order, true);
newCustomer.Orders.Add(newOrder);
}
EF 6 の CurrentValues.ToObject() についてはどうですか?
var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();