Best Practice for EF4/WCF SaveChanges()

entity-framework savechanges self-tracking-entities wcf

Question

For our EF entities, we construct a generic Save() service in WCF in the manner described above. The task is done for us by a TT. We don't have any issues with it, but I don't want to presume that this is the best course of action (even if it might be). Since you guys are so intelligent and helpful, I thought I would ask:

Exists a better approach?

[OperationContract]
public User SaveUser(User entity)
{
    bool _IsDeleted = false;
    using (DatabaseEntities _Context = new DatabaseEntities())
    {
        switch (entity.ChangeTracker.State)
        {
            case ObjectState.Deleted:
                //delete
                _IsDeleted = true;
                _Context.Users.Attach(entity);
                _Context.DeleteObject(entity);
                break;
            default:
                //everything else
                _Context.Users.ApplyChanges(entity);
                break;
        }
        // now, to the database
        try
        {
            // try to save changes, which may cause a conflict.
            _Context.SaveChanges(System.Data.Objects.SaveOptions.None);
        }
        catch (System.Data.OptimisticConcurrencyException)
        {
            // resolve the concurrency conflict by refreshing 
            _Context.Refresh(System.Data.Objects.RefreshMode.ClientWins, entity);
            // Save changes.
            _Context.SaveChanges();
        }
    }
    // return
    if (_IsDeleted)
        return null;
    entity.AcceptChanges();
    return entity;
}
1
1
6/24/2011 8:03:39 AM

Accepted Answer

Why are you using self-tracking entities for this? What was incorrect about this:

[OperationContract]
public User SaveUser(User entity)
{
    bool isDeleted = false;
    using (DatabaseEntities context = new DatabaseEntities())
    {
        isDeleted = entity.ChangeTracker.State == ObjectState.Deleted;
        context.Users.ApplyChanges(entity); // It deletes entities marked for deletion as well

        try
        {
            // no need to postpone accepting changes, they will not be accepted if exception happens
            context.SaveChanges(); 
        }
        catch (System.Data.OptimisticConcurrencyException)
        {
            context.Refresh(System.Data.Objects.RefreshMode.ClientWins, entity);
            context.SaveChanges();
        }
    }

    return isDeleted ? null : entity;
}
6
6/24/2011 8:03:03 AM

Popular Answer

People normally don't expose their Entity Framework objects directly in a WCF service, if I'm not incorrect. They are placed on distinct levels because Entity Framework is often thought of as a data-access layer whereas WCF is more of a front-end layer.

In the WCF methods, a Data-Transfer Object (DTO) is used. In most cases, this is a POCO without any kind of state-tracking. The DTO is then manually or automatically mapped to an entity using a framework like AutoMapper.

Clients should typically be aware of whether they are "adding" or "updating" an object, and I personally think it would be better if these were two distinct activities on the service interface. I would also demand that they use a different technique for object deletion. The existence (or absence) of a primary key value should allow you to determine if the object you have been given is "new" or not if you really require a general "Save" command.

A generic utility may include a large portion of the code. For instance, if your T4 template generates attributes on your entities' key values, you might automatically check to see whether the key values are available and do an Insert/Update as necessary. Further, thetry SaveChanges catch retry While the block you're using is probably not essential, it might simply be moved into a simple utility function to make it more DRY.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow