4

I might be totally wrong but a simple insert and update using EF4 is driving me insane.(Novice in EF)

I have created a noddy project and uploaded here

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

Feel free to download if you like and fix the issue.THAT WOULD BE FANTASTIC.

In a nutshell I would like somebody to tell me that what I suspect is not the case.

Suppose you have a customer object. Do you need to fetch it first before updating like you do for delete?That seems insane to me.

Given that you have a save method(both insert and update are done here)and you pass you customer to it.You already have all the details .How would you modify to make it work? I get all sorts of errors. do I need to go to the db each time I need to modify a child of my customer?

   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();
      }     
  }
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
user9969
  • 15,632
  • 39
  • 107
  • 175
  • Possible duplicate of http://stackoverflow.com/questions/4218566/c-entity-framework-can-i-update-a-record-without-querying-first ? – Pero P. Nov 21 '10 at 08:42
  • hi,looked at the one the title is exactly what I am looking for too but there is no answer – user9969 Nov 21 '10 at 09:57

2 Answers2

4

You don't have to load object before updating - check this answer. But if you want to work with related objects it is really useful. In your case the Address entity is probably related to Customer enity in one to many relation. If you want to modify customer with addresses without loading entity graph you have to know which addresses were modified, inserted and deleted. For one to many relation this can usually by simulated by these rules:

  • Id = 0 means new entity
  • Id > 0 means update (so you are updeting even if address didn't changed)
  • Id < 0 means delete (current Id is negation of deleted Id)

You can see that this technique is not very good because you always update unchanged entities and you have to handle deletes in different way then just removing them from Addresses collection in Customer entity. Also this technique works only for aggregates where related enity cannot exists without parent entity. If you can delete only relation but the entity remains in database, this technique is useless and you need to find another change tracking mechanism - this is exactly what happens if you want to track changes in many to many relations. So that is the reason why I gave up this technique and why I load modified entites from database before I process changes. There is also another post about this problem.

If you don't want to track state by yourselves at all you can check Self tracking entities (STE) but be aware of their drawbacks.

Btw. delete operation can be also performed without loading the entity first. You can usually create dummy entity (empty) and set only its Id and concurrency handler (I'm always using timestamp). Then you can attach this entity to context and delete it. But this will not work if entity is in child relation to other entitity. In that case you also have to manually delete relation or exception will be thrown. This will not occure if you load the entity first from database.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
4

This is one way to do it if you don't like to fetch the objects before save:

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

The trick is to remove childs (e.g. Addresses) just before doing Attach/AddObject on parent (e.g. Customer) since these 2 operations will affect the whole graph and that will cause all sorts of problems.

Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83