实体框架5实体的深度拷贝/克隆

我使用的是实体框架5(DBContext) ,我试图找到深度复制一个实体(即复制该实体和所有相关对象)的最佳方法,然后将新实体保存到数据库中。我怎么能这么做?我研究过使用扩展方法,比如 CloneHelper,但是我不确定它是否适用于 DBContext

48706 次浏览

克隆一个实体的一个简单方法是这样做:

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

这里的诀窍是 追踪()-当你加载一个像这样的实体时,你的上下文不知道它,当你调用 SaveChanges 时,它会像对待一个新的实体一样对待它。

如果 MySet有一个对 MyProperty的引用,并且你也想要一个它的拷贝,只需要使用一个 Include:

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

还有一个办法。

在某些情况下,我更喜欢它,因为它不需要您专门运行查询来获取要克隆的数据。可以使用此方法创建已从数据库获得的实体的克隆。

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

此方法将当前值从源复制到已添加的新行。

这是一个允许泛型克隆的泛型扩展方法。

你必须从 Nugt 取 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 typeof(TEntity).Name + "Id"return the first guid property的任何值,也可以返回标记为 DatabaseGenerated(DatabaseGeneratedOption.Identity)]的第一个属性。

在我的例子中,我已经用 [DataServiceKeyAttribute("EntityId")]标记了我的类

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

我在实体框架核心中遇到过同样的问题,当子实体延迟加载时,深度克隆涉及多个步骤。克隆整个结构的一种方法如下:

   var clonedItem = Context.Parent.AsNoTracking()
.Include(u => u.Child1)
.Include(u => u.Child2)
// deep includes might go here (see ThenInclude)
.FirstOrDefault(u => u.ParentId == parentId);


// remove old id from parent
clonedItem.ParentId = 0;


// remove old ids from children
clonedItem.Parent1.ForEach(x =>
{
x.Child1Id = 0;
x.ParentId= 0;
});
clonedItem.Parent2.ForEach(x =>
{
x.Child2Id = 0;
x.ParentId= 0;
});


// customize entities before inserting it


// mark everything for insert
Context.Parent.Add(clonedItem);


// save everything in one single transaction
Context.SaveChanges();

当然,也有一些方法可以使通用函数能够快速加载所有内容和/或重置所有键的值,但是这应该使所有的步骤更加清晰和可定制(例如,对于一些根本不被克隆的子级,通过跳过它们的 Include)。