Reputation: 238
I have a web application where users are allowed to like items. Each item have a property of the total likes he got.
The issue happens when a lot of users like the same item at the same time, then I am receiving incorrect values in SQL (caused by race condition).
As a temporary solution, I have created a worker thread in the controller constructor that works against a queue, when an item like/dislike request is receiving, I am queuing this request. The worker thread is dequeing the values and updating a dictionary that maps itemid to totalcount.
The worker thread then updates the database once every one minute with the result.
Side question: does context.SaveChanges()
save only what changed in an object? Or does it save all of the properties of the objects?
I have a feeling that this solution is not the right one, what is the best way to deal with such an issue?
Upvotes: 4
Views: 1676
Reputation: 98
You can use RepeatableRead
transaction scope witch locks data when read and write occurs:
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
{
...
context.SaveChanges();
scope.Complete();
}
Entity Framework track entity fields change and generates sql code to update only those that have been modified. You may explicity specify that you want to update only one entity property and ignore other changes:
context.Entry(entity).State = EntityState.Unchanged;
context.Entry(entity)
.Property(c => c.entityField).IsModified = true;
Also it can be useful when you attach an object into context:
context.Attach(entity);
context.Entry(entity)
.Property(c => c.entityField).IsModified = true;
Because Attach
puts entity and it's properties into the context in Unchanged
state.
Upvotes: 1
Reputation: 4766
for performance and concurrency issues is better that use ExecuteSqlCommand:
string sql = "UPDATE Table SET LikeCount = LikeCount+1 WHERE Id={0}";
using (var context = new BloggingContext())
{
context.Database.ExecuteSqlCommand( sql, new SqlParameter("@Id", id));
}
Upvotes: 1