我使用实体框架5( DBContext
),我试图找到最好的方式来深复制一个实体(即复制的实体和所有相关对象),然后保存新的实体在数据库中。 我怎样才能做到这一点? 我已经调查使用扩展方法,如CloneHelper
,但我不知道它是否适用于DBContext
。
Answer 1:
克隆一个实体的一个廉价简便的方法是做这样的事情:
var originalEntity = Context.MySet.AsNoTracking()
.FirstOrDefault(e => e.Id == 1);
Context.MySet.Add(originalEntity);
Context.SaveChanges();
这里的技巧是AsNoTracking() -当你加载一个这样的实体,您的上下文不知道它,当你调用的SaveChanges,它将把它当作一个新的实体。
如果MySet
具有参考MyProperty
,你想它的一个副本也只需使用一个Include
:
var originalEntity = Context.MySet.Include("MyProperty")
.AsNoTracking()
.FirstOrDefault(e => e.Id == 1);
Answer 2:
这里是另一种选择。
我喜欢它在某些情况下,因为它不要求专门运行查询来获取被克隆的数据。 您可以使用此方法来创建你已经从数据库中获得的实体克隆。
//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();
这种方法可从源的当前值已添加一个新行。
Answer 3:
这是一个通用的扩展方法,其允许一般的克隆。
你必须获取System.Linq.Dynamic
从的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;
}
你必须自己实现的唯一的事情就是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();
}
Answer 4:
我在实体框架的核心相同问题,即深克隆涉及多个步骤,当孩子实体延迟加载。 克隆整个结构的一种方法如下:
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();
当然,也有办法让通用功能跃跃欲试负载一切和/或重置值的所有键,不过这应该使所有的步骤太多明确的和可定制的(例如,所有为一些孩子根本不会被克隆,通过跳过其包括)。
文章来源: Entity Framework 5 deep copy/clone of an entity