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();
      }     
  }
2
  • Possible duplicate of stackoverflow.com/questions/4218566/… ? Commented Nov 21, 2010 at 8:42
  • hi,looked at the one the title is exactly what I am looking for too but there is no answer Commented Nov 21, 2010 at 9:57

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

Giving you up vote as the post you linked to was very helpful :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.