我正在使用Entity Framework 5(DBContext
),我正在尝试找到深层复制实体的最佳方法(即复制实体和所有相关对象),然后将新实体保存在数据库中。我怎样才能做到这一点?我已经研究过使用CloneHelper
等扩展方法,但我不确定它是否适用于DBContext
。
答案 0 :(得分:120)
克隆实体的一种便宜的简单方法是执行以下操作:
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);
答案 1 :(得分:21)
这是另一种选择。
在某些情况下我更喜欢它,因为它不需要您专门运行查询来获取要克隆的数据。您可以使用此方法创建已从数据库中获取的实体的克隆。
//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();
此方法将当前值从源复制到已添加的新行。
答案 2 :(得分:1)
这是一种允许通用克隆的通用扩展方法。
您必须从nuget中获取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();
}
答案 3 :(得分:0)
我在Entity Framework Core中遇到了相同的问题,即当子实体延迟加载时,深层克隆涉及多个步骤。克隆整个结构的一种方法如下:
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();
当然,有多种方法可以使通用函数渴望加载所有内容和/或重置所有键的值,但这应该使所有步骤变得更加清晰和可自定义(例如,对于某些完全不被克隆的子项而言,通过跳过其包含)。