更新Entity Framework 4中的对象。您是否需要始终首先获取?

时间:2010-11-21 08:19:26

标签: entity-framework entity-framework-4

我可能完全错了,但使用EF4进行简单的插入和更新让我感到疯狂。(EF新手)

我创建了一个点头项目并上传到这里

http://cid-9db5ae91a2948485.office.live.com/browse.aspx/PublicFolder?uc=1http://cid-9db5ae91a2948485.office.live.com/browse.aspx/PublicFolder?uc=1

如果您愿意,可以随意下载并解决问题。这会非常棒。

简而言之,我希望有人告诉我,我怀疑的并非如此。

假设您有一个客户对象。你是否需要在更新之前先获取它,就像删除一样?这对我来说似乎很疯狂。

鉴于您有一个保存方法(插入和更新都在这里完成),您将客户传递给它。您已经拥有所有详细信息。您如何修改以使其工作?我得到各种各样的错误。 每当我需要修改客户的孩子时,我是否需要访问数据库?

   public void Save(CustomerDTO customerDTO)
   {
     //convert dto to EF entity
      var entityCustomer=ToEntityCustomer(customerDTO)
      using(ctx=new CustomerContext())
      {

          if(efCustomer.CustomerId > 0)
          {
            //UPDATE

             //DO I NEED TO FETCH MY CUSTOMER AGAIN FROM THE DB EVEN THOUGH I ALREADY HAVE
             //HAVE ALL THE DETAILS? I have not done it here.?????

              ctx.Customers.Attach(entityCustomer);
              ctx.ApplyCurrentValues(entityCustomer);

              //Also Customer has addresses so
              foreach(var address in entityCustomer.Addresses)
              { 
                 //some may be new addresses some might be modified one
                 ?????? lost
              }

          }
          else
          {
              ///INSERT
             ctx.Customers.AddObject(entityCustomer);
             ctx.ObjectStateManager.ChangeObjectState(entityCustomer,EntityState.Added);
             foreach(var address in entityCustomer.Addresses)
              { 
                 ctx.ObjectStateManager.ChangeObjectState(address ,EntityState.Added);
              }
          }

        ctx.SaveChanges();
      }     
  }

2 个答案:

答案 0 :(得分:4)

如果您不想在保存之前获取对象,这是一种方法:

public bool Save(CustomerInfo customerInfo)
{
    var entityCustomer = ToEntityCustomer(customerInfo);
    using (var ctx = new CustomerContext())
    {
        ctx.ContextOptions.LazyLoadingEnabled = false;
        DataAccess.Address[] addresses = new DataAccess.Address[entityCustomer.Addresses.Count];
        entityCustomer.Addresses.CopyTo(addresses, 0);
        entityCustomer.Addresses.Clear();

        if (customerInfo.Id == 0) {
            ctx.Customers.AddObject(entityCustomer);
        }
        else if (customerInfo.Id > 0) {
            ctx.Customers.Attach(entityCustomer);
            ctx.ObjectStateManager.ChangeObjectState(entityCustomer, EntityState.Modified);
        }

        foreach (var address in addresses) {
            if (address.AddressID == 0) {
                ctx.Addresses.AddObject(address);
                entityCustomer.Addresses.Add(address);
            }
            else if (address.AddressID > 0) {
                ctx.Addresses.Attach(address);
                ctx.ObjectStateManager.ChangeObjectState(address, EntityState.Modified);
                if (customerInfo.Id == 0) {
                    // New Customer so we need to add the existing addresses 
                   // since the FK  on Address is 0
                    entityCustomer.Addresses.Add(address);
                }

            }
        }
        ctx.SaveChanges();
        return true;
    }
}

诀窍是在父(例如Customer)上执行Attach / AddObject之前删除子节点(例如地址),因为这两个操作将影响整个图形,这将导致各种问题。

答案 1 :(得分:3)

您不必在更新前加载对象 - 请检查this answer。但是如果你想使用相关的对象,它真的很有用。在您的情况下,Address实体可能与一对多关系中的Customer enity相关。如果要在不加载实体图的情况下使用地址修改客户,则必须知道修改,插入和删除了哪些地址。对于一对多关系,通常可以通过这些规则进行模拟:

  • Id = 0表示新实体
  • ID> 0表示更新(因此即使地址未更改,您仍在升级)
  • Id< 0表示删除(当前Id为已删除Id的否定)

你可以看到这种技术不是很好,因为你总是更新未更改的实体,你必须以不同的方式处理删除,然后只从Customer实体中的Addresses集合中删除它们。此技术也适用于没有父实体的相关enity不存在的聚合。如果你只能删除关系,但实体仍然在数据库中,这种技术是无用的,你需要找到另一种更改跟踪机制 - 如果你想跟踪多对多关系中的变化,这正是如此。这就是我放弃这种技术的原因,以及为什么我在处理更改之前从数据库加载修改后的entites。关于这个问题还有另一个post

如果您不想自己跟踪状态,可以查看Self tracking entities(STE),但要注意他们的drawbacks

顺便说一下。删除操作也可以在不首先加载实体的情况下执行。您通常可以创建虚拟实体(空)并仅设置其Id和并发处理程序(我总是使用时间戳)。然后,您可以将此实体附加到上下文并将其删除。但是,如果实体与其他权利处于子女关系,这将不起作用。在这种情况下,您还必须手动删除关系或将抛出异常。如果首先从数据库加载实体,则不会发生这种情况。