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

问题描述 投票:67回答:4

我使用实体框架5(DBContext),我试图找到最好的方式来深复制一个实体(即复制的实体和所有相关对象),然后保存新的实体在数据库中。我怎样才能做到这一点?我已经调查使用扩展方法,如CloneHelper,但我不知道它是否适用于DBContext

entity-framework-5 dbcontext
4个回答
122
投票

克隆一个实体的一个廉价简便的方法是做这样的事情:

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

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

这种方法可从源的当前值已添加一个新行。


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

1
投票

我在实体框架的核心相同问题,即深克隆涉及多个步骤,当孩子实体延迟加载。克隆整个结构的一种方法如下:

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

当然,也有办法让通用功能跃跃欲试负载一切和/或重置值的所有键,不过这应该使所有的步骤太多明确的和可定制的(例如,所有为一些孩子根本不会被克隆,通过跳过其包括)。

© www.soinside.com 2019 - 2024. All rights reserved.