【问题标题】:Copy Entity Framework Object复制实体框架对象
【发布时间】:2011-08-24 08:45:39
【问题描述】:

我有一个 EF4.1 类 X,我想复制它以及它的所有子记录。 X.Y 和 X.Y.Z

现在如果我执行以下操作,它会返回错误。

属性“X.ID”是对象的关键信息的一部分,不能修改。

public void CopyX(long ID)
{
    var c = db.Xs.Include("Y").Include("W").Include("Y.Z").SingleOrDefault(x => x.ID == ID);
    if (c != null)
    {
        c.ID = 0;
        c.Title = "Copy Of " + c.Title;
        for (var m = 0; m < c.Ys.Count; m++)
        {
            c.Ys[m].ID = 0;
            c.Ys[m].XID=0-m;
            for (var p = 0; p < c.Ys[m].Zs.Count; p++)
            {
                c.Ys[m].Zs[p].XID = 0 - m;
                c.Ys[m].Zs[p].ID = 0 - p;
            }
        }
        for (var i = 0; i < c.Ws.Count; i++)
        {
            c.Ws[i].ID = 0 - i;
            c.Ws[i].XID = 0;
        }
        db.Entry<Content>(c).State = System.Data.EntityState.Added;
        db.SaveChanges();
    }
}

或者是否有其他方法可以复制实体对象。

注意:每个 W,X,Y,Z 都有多个属性。

【问题讨论】:

  • 一旁 - 你不需要.Include("Y") .Include("Y.Z")。第二个Include 将同时包含 Y Z - 如果您考虑一下,它必须包含。

标签: c# entity-framework


【解决方案1】:

中,使用DbExtensions.AsNotracking() 非常简单。

返回一个新查询,其中返回的实体不会缓存在 DbContext 或 ObjectContext 中。

对象图中的所有对象似乎都是这种情况。

您只需要真正了解您的图表以及您所做的和不希望插入/复制到数据库中的内容。

假设我们有这样的对象:

public class Person
{
  public int ID { get; set; }
  public string Name { get; set; }
  public virtual ICollection<Address> Addresses { get; set; }
}

public class Address
{
  public int ID { get; set; }
  public AddressLine { get; set; }
  public int StateID { get; set; }

  public ICollection<State> { get; set; }
}

所以为了复制一个人,我需要复制地址,但我不想复制州。

var person = this._context.Persons
  .Include(i => i.Addresses)
  .AsNoTracking()
  .First();

// if this is a Guid, just do Guid.NewGuid();
// setting IDs to zero(0) assume the database is using an Identity Column
person.ID = 0;

foreach (var address in person.Addresses)
{
  address.ID = 0;
}

this._context.Persons.Add(person);
this._context.SaveChanges();

如果您想再次重复使用这些相同的对象来插入第三个副本,您可以再次运行查询(使用 AsNoTracking())或分离对象(示例):

dbContext.Entry(person).State = EntityState.Detached;
person.ID = 0;
foreach (var address in person.Addresses)
{
  dbContext.Entry(address).State = EntityState.Detached;
  address.ID = 0;
}

this._context.Persons.Add(person);
this._context.SaveChanges();

【讨论】:

  • 看起来 AsNoTracking() 可以做到这一点。否则它不会让我更新任何实体对象的 ID 值。
  • 这适用于一对多的关系。您将如何支持在 AsNoTracking 查询中执行 Include(x=>x.EntitiesWithManyToMany) 的多对多关系?目前它也对这些进行了深层复制 - 我只是希望查找表链接与新的人员 ID 重复。
  • 虽然这对ManyToMany项目进行了深度复制,但是当您添加对象时,(假设我可能是错的)由于ManyToMany项目已经存在,它们将不会被添加(因为它们具有相同的id已经存在)。
  • 好东西,谢谢分享。值得一提的是,AsNoTracking() 可以更快地从数据库加载对象。在不需要更改数据库对象 (GET) 的情况下,它非常有用。
【解决方案2】:

您需要对整个实体图进行正确的深拷贝 - 最好的方法是将原始实体图序列化为内存流并将其反序列化为新实例。您的实体必须是可序列化的。它是often used with DataContractSerializer,但您也可以使用二进制序列化。

【讨论】:

  • 通常当我这样做时,它只是序列化的递归问题。
  • 类型“W”的对象图包含循环,如果禁用引用跟踪,则无法序列化。 W 具有父子节点的自连接。
  • 这就是我让你的实体可序列化的意思——你必须准备它们以支持序列化(准备取决于你将使用的序列化程序的类型)。
  • .net-framework 或 sql server 或其他任何能够弄清楚发生了什么并将此更改键查询优化为仅此的方式,而不是添加新行并删除旧行?
  • @dontomaso:不。如果您需要替换密钥,EF 能够处理它的唯一方法是插入新记录并删除旧记录,因为对于 EF,现有实体的密钥是永久的。
【解决方案3】:

C 不是副本,它是记录,您得到的错误是因为您正在尝试更新它的主键,即使您不是它仍然无法工作。您需要创建一个新的 X 实体,然后从检索到的实体的属性中复制值,然后插入新实体。

【讨论】:

    【解决方案4】:

    不确定它是否适用于 4.1,来自http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4

    public static T CopyEntity<T>(MyContext ctx, T entity, bool copyKeys = false) where T : EntityObject
    {
        T clone = ctx.CreateObject<T>();
        PropertyInfo[] pis = entity.GetType().GetProperties();
    
        foreach (PropertyInfo pi in pis)
        {
            EdmScalarPropertyAttribute[] attrs = (EdmScalarPropertyAttribute[])pi.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);
    
            foreach (EdmScalarPropertyAttribute attr in attrs)
            {
                if (!copyKeys && attr.EntityKeyProperty)
                    continue;
    
                pi.SetValue(clone, pi.GetValue(entity, null), null);
            }
        }
    
        return clone;
    }
    

    您现在也可以将相关实体复制到您的克隆对象;假设您有一个实体:客户,它具有导航属性:订单。然后,您可以使用上述方法复制客户及其订单:

    Customer newCustomer = CopyEntity(myObjectContext, myCustomer, false);
    
    foreach(Order order in myCustomer.Orders)
    {
        Order newOrder = CopyEntity(myObjectContext, order, true);
        newCustomer.Orders.Add(newOrder);
    }
    

    【讨论】:

      【解决方案5】:

      我使用 Newtonsoft.Json,以及这个很棒的功能。

          private static T CloneJson<T>(T source)
          {
              return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
          }
      

      【讨论】:

        猜你喜欢
        • 2020-04-11
        • 2011-11-26
        • 2011-03-04
        • 2011-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多