EF4/WCF SaveChanges() Best Practice
This is how we implement a generic Save() service in WCF for our EF entities. A TT does the work for us. Even though we don't have any problems with it, I hate to assume this is the best approach (even if it might be). You guys seem pretty darn bright and helpful, so I thought I would pose the question:
Is there a better way?
[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 ref开发者_如何学Creshing
_Context.Refresh(System.Data.Objects.RefreshMode.ClientWins, entity);
// Save changes.
_Context.SaveChanges();
}
}
// return
if (_IsDeleted)
return null;
entity.AcceptChanges();
return entity;
}
Why are you doing this with Self tracking entities? What was wrong with 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;
}
If I'm not mistaken, people typically don't expose their Entity Framework objects directly in a WCF service. Entity Framework is typically thought of as a data-access layer, and WCF is more of a front-end layer, so they are put on different tiers.
A Data-Transfer Object (DTO) is used in the WCF methods. This is typically a POCO which doesn't have any state-tracking on it whatsoever. The DTO is then mapped to an Entity either by hand or via a framework like AutoMapper.
Typically clients should know whether they are "adding" or "updating" an object, and I would personally prefer these to be two separate operations on the service interface. Also, I would definitely require them to use a separate method for deleting an object. However, if you absolutely need a generic "Save", you should be able to tell whether the object you've been given is "new" or not based on the presence (or absence) of a primary key value.
A lot of the code can be put into a generic utility. For example, supposing your T4 template produces attributes on the key values of your entities, you could automatically determine whether the key values are present and perform an Insert/Update accordingly. Also, the try SaveChanges catch retry
block you're using--while probably unnecessary--could easily be put into a simple utility method to be more DRY.
精彩评论