实体框架6:除ID以外的克隆对象

Qua*_*ive 51 c# entity-framework mvvm entity-framework-6

在我的MVVM程序中,我有一个Model类(比如说MyModel),我从中有一个从数据库中读取的实例(使用Entity Framework).在检索对象时,我将所有数据呈现给用户.稍后用户将修改一些字段.
我想要的是创建相同的对象,除了它ID(因为这ID主键自动递增).
那我怎么能接近这个呢?我不想一个接一个地复制所有字段,这不是一个强大的方法.因为将来可能会修改模型,所以我必须在克隆方法中考虑到这一点.

那么有没有任何优雅的方法来复制对象,当在数据库中保存时,它的ID会再次自动增加?(设置ID null会给我一个编译错误,因为它的类型int).

Qua*_*ive 63

我注意到没有必要复制.显然,在向数据库添加模型实例时(即使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();
}
Run Code Online (Sandbox Code Playgroud)

  • 不知道这是如何被接受的答案:我试过了,它仍然给出了“重复键”例外 (5认同)
  • @THEStephenStanton`.Where()`linq扩展方法只是一个例子.为此,您还可以在Entity Framework`DbSet <T>`上使用`.Find()`方法.但这不是重点,重点是当您向DbContext添加脏模型时,EF会自动增加ID. (4认同)
  • @ kiml42 - 一旦调用SaveChanges(),脏模型对象将使用新ID更新; (2认同)

jay*_*cer 43

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();
Run Code Online (Sandbox Code Playgroud)

当您从数据集中检索一个或多个实体时,您可以告诉实体框架不要跟踪您对该对象所做的任何更改,然后将该实体作为新实体添加到数据集中.使用.AsNoTracking时,上下文对现有实体一无所知.


swe*_*tfa 7

使用 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)
Run Code Online (Sandbox Code Playgroud)

正确克隆实体框架对象(至少在 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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Kev*_*son 5

我发现这是在寻找是否有比我目前使用的方法更好的克隆对象的方法,并且发现如果尝试进行多次克隆,则可接受的答案存在潜在的问题……至少如果您要避免多次创建上下文...

我不知道这是否是最好的克隆方法,这就是为什么我在寻找另一种方法。但是,它有效。如果您需要多次克隆实体,则可以使用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();
}
Run Code Online (Sandbox Code Playgroud)