Search code examples
entity-frameworkdata-access-layer

Entity Framework as DAL how to implement Update and Delete correctly


I'm writing a DAL class using EF4.0, I've read

http://www.codeproject.com/Articles/43367/ADO-NET-Entity-Framework-as-Data-Access-Layer

and

http://msdn.microsoft.com/en-us/magazine/cc700340.aspx

But when I test their code, I meet some problem with the Update and Delete method.

The DAL class all code is below:

public class FriendlinkDA : IDisposable
{
    private EdiBlogEntities context;

    public FriendlinkDA()
    {
        context = new EdiBlogEntities();
    }

    public void Dispose()
    {
        context.Dispose();
    }

    public FriendLink GetFriendLink(Guid id)
    {
        return context.FriendLink.FirstOrDefault(f => f.Id == id);
    }

    public void Update(FriendLink model)
    {
        // Way 1:  (throw exception)
        //context.Attach(model);
        //model.SetAllModified(context);
        //context.SaveChanges();

        // Way 2: 
        EntityKey key;
        object originalItem;
        key = context.CreateEntityKey("FriendLink", model);
        if (context.TryGetObjectByKey(key, out originalItem))
        {
            context.ApplyCurrentValues(key.EntitySetName, model);
            //context.ApplyPropertyChanges(key.EntitySetName, model);
        }
        context.SaveChanges();
    }

    public void Delete(FriendLink model)
    {
        // Way 1:
        context.Attach(model);
        context.DeleteObject(model);
        context.SaveChanges();

        // Way 2:
        //var item = context.FriendLink.FirstOrDefault(f => f.Id == model.Id);
        //context.DeleteObject(item);
        //context.SaveChanges();
    }
}

The extension method is:

public static void SetAllModified<T>(this T entity, ObjectContext context) where T : IEntityWithKey
{
    var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
    var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select
      (pn => pn.FieldType.Name);
    foreach (var propName in propertyNameList)
        stateEntry.SetModifiedProperty(propName);
}

In the application, I am use the DAL like this:

// Delete
using (var optFriendlink = new FriendlinkDA())
{
    var test = optFriendlink.GetFriendLink(new Guid("81F58198-D396-41DE-A240-FC306C7343E8"));
    optFriendlink.Delete(test);
}

// Update
using (var optFriendlink = new FriendlinkDA())
{
    var testLink = optFriendlink.GetFriendLink(new Guid("62FD0ACF-40C3-4BAD-B438-38BB540A6080"));
    testLink.Title = "ABC";
    optFriendlink.Update(testLink);
}

Question 1:

In Delete(), both way 1 and way 2 can work. Which one is better?

Question 2:

In Update(), way 1 give me an exception: The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state.

on this statment: context.Attach(model);

but way 2 is fine.

why is this happening? I also attach the model in Delete(), why Delete() is working fine? how I can write the update correctly?


Solution

  • The exception says it all:

    An object can only be reattached when it is in an unchanged state.

    You change the object in the code snippet under // Update, so that's why it cannot be re-attached.

    As to which method is better. Normally you would get an object from a context, dispose of the context, do something with the object and then use a new context to save the object. In that case using Attach is much more comfortable then getting an object by Id first.