实体框架6:克隆ID以外的对象
本文关键字:对象 ID 克隆 框架 实体 | 更新日期: 2023-09-27 18:21:06
在我的MVVM程序中,我有一个Model类(比如MyModel
),我从中有一个读取数据库的实例(使用实体框架)。检索对象时,我将所有数据呈现给用户。稍后,用户将修改一些字段
我想要的是创建相同的对象,除了它的ID
(因为ID
是主键和自动递增)
那么我该如何处理呢?我不想一个接一个地复制所有字段,这不是一种稳健的方法。因为也许将来模型可能会被修改,所以通过这种方式,我必须在克隆方法中考虑到这一点。
那么,有没有什么优雅的方法可以复制对象,当保存到数据库中时,它的ID会再次自动递增?(将ID设置为null
会导致编译器错误,因为它属于int
类型)。
我注意到不需要复制。显然,当将模型的实例添加到数据库中时(即使ID设置为数据库中已经存在的ID),实体框架会在数据库中插入一个新行,并自动递增其主键。所以这个功能已经内置在EF中了。对不起,我不知道
为了清楚起见,这里有一个例子:
using(var database = new MyDbContext()) {
MyModel myModel = database.FirstOrDefault(m => m.SomeProperty == someValue);
myModel.SomeOtherProperty = someOtherValue; //user changed a value
database.MyModels.Add(myModel); //even though the ID of myModel exists in the database, it gets added as a new row and the ID gets auto-incremented
database.SaveChanges();
}
Lori Peterson建议使用.AsNoTracking()在EF6中执行克隆。我正在使用这种方法,可以确认它有效。甚至可以包含子对象。
var entity = context.Entities
.AsNoTracking()
.Include(x => x.ChildEntities)
.FirstOrDefault(x => x.EntityId == entityId);
entity.SomeProperty = DateTime.Now;
context.Entities.Add(entity);
context.SaveChanges();
当您从数据集中检索一个或多个实体时,您可以告诉实体框架不要跟踪您对该对象所做的任何更改,然后将该实体作为新实体添加到数据集中。使用.AsNoTracking时,上下文对现有实体一无所知。
使用ObjectContext时,QuantumHive提供的答案不起作用。
在这种情况下返回的错误是:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
at System.Data.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded)
at System.Data.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName)
at System.Data.Objects.DataClasses.RelatedEnd.AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, Boolean doAttach)
at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value)
at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value)
要正确克隆实体框架对象(至少在EF6.0中),请执行以下操作:
/// <summary>
/// Clone a replica of this item in the database
/// </summary>
/// <returns>The cloned item</returns>
public Item CloneDeep()
{
using (var context = new EntityObjectContext())
{
var item = context.Items
.Where(i => i.ItemID == this.ItemID)
.Single();
context.Detach(item);
item.EntityKey = null;
item.ItemID = 0;
return item;
}
}
我发现这是为了看看是否有比我目前使用的更好的方法来克隆对象,并注意到如果你试图进行多个克隆,接受的答案可能会出现问题。。。至少如果你想避免多次创建上下文。。。
我不知道这是否是克隆的最佳方法,这就是为什么我在寻找另一种方法。但是,它是有效的。如果需要多次克隆实体,可以使用JSON序列化来克隆。。。类似这样的东西(使用Newtonsoft JSON)。
using( var context = new Context() ) {
Link link = context.Links.Where(x => x.Id == someId);
bool isFirst = true;
foreach( var id in userIds ) {
if( isFirst ) {
link.UserId = id;
isFirst = false;
}
else {
string cloneString = JsonConvert.SerializeObject(link);
Link clone = JsonConvert.DeserializeObject<Link>(cloneString);
clone.UserId = id;
context.Links.Add(clone);
}
}
context.SaveChanges();
}
我使用postgres数据库:
CREATE TABLE public."Table" (
"Id" integer NOT NULL DEFAULT nextval('"Table_Id_seq"'::regclass),
...
上述方法中没有一种对我不起作用。我使用第二个:
Table table = _context.Table.AsNoTracking().Select(s => new Table {
// some properties, exept id
}).FirstOrDefault();
_context.Table.Add(table);
await _context.SaveChangesAsync();