问题
How should I solve simulation update, when one user updates already updated entery by another user?
First user request 'Category' entityobject, second user does the same.
Second user updates this object and first user updates.
I have field timestamp field in database that wa set to Concurrency Mode - Fixed.
This is how I update:
public class CategoriesRepository : BaseCategoryRepository
{
public void Update(....)
{
try
{
Category catToUpdate = (from c in Contentctx.Categories where c.CategoryID == categoryId select c).FirstOrDefault();
catToUpdate.SectionReference.EntityKey = new System.Data.EntityKey("ContentModel.Sections", "SectionID", sectionId);
if (catToUpdate != null)
{
//Set fields here....
Contentctx.SaveChanges();
}
base.PurgeCacheItems(CacheKey);
}
catch (OptimisticConcurrencyException ex)
{
}
}
}
//Contentctx comes from base class: Contentctx:
private ContentModel _contenttx;
public ContentModel Contentctx
{
get
{
if ((_contenttx == null))
{
_contenttx = new ContentModel();
}
return _contenttx;
}
set { _contenttx = value; }
}
//on Business layer:
using (CategoriesRepository categoriesRepository = new CategoriesRepository())
{
categoriesRepository.UpdateCategory(.....);
}
Exception never jumps...
How should I handle this?
回答1:
Are you sure that sequence of calls is performed as you described? Anyway the main problem here is that you should not use the timestamp returned by the query in Upate method. You should use timestamp received when user get initial data for update. It should be:
- User requests data from update - all fields and timestamp are received from DB
- Timestamp is stored on client (hidden field in web app)
- User modifies data and pushes Save button - all data including old timestamp are send to processin
- Update method loads current entity
- All updated fields are merged with old entity. Timestamp of entity is set to timestamp received in the first step.
- SaveChanges is called
The reason for this is that it can pass a lot of time between first call and update method call so there can be several changes already processed. You have to use initial timestamp to avoid silent overwritting other changes.
Edit:
// You are updating category - update probably means that you had to load category
// first from database to show actual values. When you loaded the category it had some
// timestamp value. If you don't use that value, any change between that load and c
// calling this method will be silently overwritten.
public void Update(Category category)
{
try
{
Category catToUpdate = (from c in Contentctx.Categories where c.CategoryID == categoryId select c).FirstOrDefault();
...
// Now categoryToUpdate contains actual timestamp. But it is timestamp of
// actual values loaded now. So if you use this timestamp to deal with
// concurrency, it will only fire exception if somebody modifies data
// before you call SaveChanges few lines of code bellow.
if (catToUpdate != null)
{
//Set fields here....
// To ensure concurrency check from your initial load, you must
// use the initial timestamp.
catToUpdate.Timestamp = category.Timestamp;
Contentctx.SaveChanges();
}
...
}
catch (OptimisticConcurrencyException ex)
{
...
}
}
来源:https://stackoverflow.com/questions/4699252/solving-optimistic-concurrency-update-problem-with-entity-framework