Best practice to lock a record for editing while using entity framework
Best practice is to use RowVersion and Optimistic locking.
Optimistic Concurrency Patterns explained.
If using Code first, then include a field in your POCO.
public virtual byte[] RowVersion { get; set; }
EF will add a Timestamp/RowVersion property to your table. It will be checked during Update. And automatically updated by the DB when changed.
EDIT: to better explain.
What EF is looking for is properties that are concurrency fields so you can actually control the concurrency with one or more fields.
entity.Property(p => p.RowVersion).IsConcurrencyToken()
when performing an update or delete you catch the defined exception
catch (DbUpdateConcurrencyException ex)
EF treats the RowVersion as a concurrency token. This is the Generally used approach. Since SQLServer will automatically update this field type for you.
So very fast and easy. But you can tell EF a property is a concurrency token explicitly and have more than one.
So EF should add properties to the where clause for updates and deletes
to make sure the record didn't change since accessed.
How To Avoid Locking Database In Entity Framework 4 When Doing Many Updates
Entity framework on top of SQL server by default uses read committed transaction isolation level and transaction is committed at the end of SaveChanges
. If you suspect other behavior it must be by the rest of your code (are you using TransactionScope
? - you didn't show it in your code) or it must be some bug.
Also your approach is wrong. If you want to save each record separately you should also load each record separately. EF is definitely bad choice for this type of applications. Even if you use only single SaveChange
for updating all your records it will still make single roundtrip to database for each update.
How do entity framework handle concurrency access,when issuing multiple delete & create opeartions
General approach to locking in EF is to use Optimistic locking.
EF Locking docu
SO example
-userB retrieve all the records , delete, add new records. but when userB reach the saveChnages() , entity framework will raise a
DbUpdateConcurrencyException when it try to delete a record that no
more exists ? so is this what will happen. ?
Yes this is what will happen IF you have declared a Concurrency field on the record.
eg
public virtual byte[] RowVersion { get; set; }
What is the best way to prevent updating on specific fields in Entity Framework
Well I would advice against ever using the EF classes in the View. You're best bet is to construct ViewModel classes and use Automapper to map them from the EF classes.
When you are updating records in the database though, you can control which fields in the ViewModel are used to update the existing fields in the EF class.
The normal process would be:
Use the Id to get the latest version of the existing object out of the database.
If you are using optimistic concurrency control then check that the object has not been updated since the ViewModel was created (so check timestamp for example).
Update this object with the required fields from your ViewModel object.
Persist the updated object back to the database.
Update to include Automapper examples:
Let's say your POCO is
public class MyObject
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Field1 is the field you don't want updating.
You should declare a view model with the same properties:
public class MyObjectModel
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Automap between them in the constructor of your Controller.
Mapper.CreateMap<MyObject, MyObjectModel>();
you can if you wish (although I prefer to do this manually, automap the other way too:
Mapper.CreateMap<MyObjectModel, MyObject>().ForMember(dest=>dest.Field1, opt=>opt.Ignore());
When you are sending date to your website you would use:
var myObjectModelInstance = Mapper.Map<MyObject, MyObjectModel>(myObjectInstance);
to create the viewModel.
When saving the data, you'd probably want something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
if(myModel.Id == 0 )
{
//New object
poco.Field1 = myModel.Field1 //set Field1 for new creates only
}
}
although I'd probably remove the exclusion of Field1 above and do something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco;
if(myModel.Id == 0)
{
poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
}
else
{
poco = myDataLayer.GetMyObjectById(myModel.Id);
poco.Field2 = myModel.Field2;
}
myDataLayer.SaveMyObject(poco);
}
note I believe that best-practise would have you never Automap FROM the ViewModel, but to always do this manually, including for new items.
I need to do rollback or somenthing when I don't need to do anything more?
You should check your data before begin transaction, you dont need begin trasnaction while you are reading data from DB, because first part in your logic there is no any update,insert... and you dont need any commit or rollback
Actually your code should be like this
using(Entities dbContext = new Entities())
{
//Get data from database
//check data from database
//if my data is the expected: return true;
using (DbContextTransaction myTransaction = dbContext.Database.BeginTransaction())
{
//if not is expected:
try
{
//update data;
//dbContext.Savechages();
//myTransaction.Commit();
//return true;
}
catch(Exception ex)
{
transaction.Rollback();
......
return false;
}
}
}
Related Topics
Group Query Results by Month and Year in Postgresql
How to Connect an Existing SQL Server Login to an Existing SQL Server Database User of Same Name
How to Manually Execute SQL Commands in Ruby on Rails Using Nuodb
SQL Server Script to Create a New User
How to Design a Database Schema to Support Tagging with Categories
Database-Wide Unique-Yet-Simple Identifiers in SQL Server
Differencebetween '->>' and '->' in Postgres SQL
SQL Column Definition: Default Value and Not Null Redundant
How Long Should SQL Email Fields Be
Sql-Server: Error - Exclusive Access Could Not Be Obtained Because the Database Is in Use
SQL Server: How to Get All Child Records Given a Parent Id in a Self Referencing Table
Select Random Sampling from SQLserver Quickly
Error Importing Azure Bacpac File to Local Db Error Incorrect Syntax Near External
Parse JSON into Oracle Table Using Pl/Sql
Why Is Parameterized SQL Generated by Nhibernate Just as Fast as a Stored Procedure