I´m working on simple CMS in ASP .NET MVC 5 with Entity Framework. I have a few questions about concurrency in MVC´s applications.
First of all - public section of my app (section for users without any authentification):
All data (posts, category informations, tags, documents) are stored in DB (using Entity)
In controllers for public section, there is only reading or writing data to DB, not deleting or editing
So my first question - is it necessary to have some mechanisms to avoid concurrency dangers in public section? Will everything be correct when multiple users will be browsing my website at the same time?
And then my admin section (authentification needed):
- There can be multiple users with multiple roles (but in real there will be only few registered users)
- Users can create/edit/delete data in DB
I know that some security mechanism are necessary to have safe system, but can you help me how to do this?
I´m also using this pattern for DbContext in each method, where DB is necessary:
using (var db = new CmsContext())
{
// other stuff here
}
Instead of one class variable db that is used in all methods. Is this right?
Many thanks for answers!
First of all, you should understand all the cases when the concurrency can lead to exceptions. Some common cases, which are usually about the relations and contracts.
Writes and Updates:
A table contains some unique constraint, so you check if an entity exists, and tries to create one if it doesn't. Now two concurrent actions performs the check, both get empty result, and both tries to create the entity. One action will fail with an exception, while the other one got more luck, and created the entity first.
A table contains foreign key: by creating new entity, some other action have removed the dependent entry. As a result - exception.
Concurrent updating of any scalar values wont cause any exceptions, but the last one wins.
So you have to consider the structure of your app/db and scenarios and decide if you want to do something to prevent the uncontrolled concurrent updates/writes.
What you can do against?
Locks
The problem here could be if you have multiple instances, as it is server level lock only.
Transactions
Database locks
As far as I can see, concurrency is related with two people editing the same record at the same time. Considering that the public section doesn't have any writing method, avoid concurrency mechanism is not necessary, neither is possible.
You could write your queries in others classes, like repositories and/or services. Searching for "repository and services entity framework" at google, I found this link http://techbrij.com/service-layer-entity-framework-asp-net-mvc-unit-testing, which sounds very interesting.
That's a hard question. It is too hard to say that something is right or wrong these days. There are a lot of people, with several different opinions. In my opinion, your example is not the best way to do (but it doesn't mean it's incorrect). When using a repository class, we usually have only one instance of the DbContext. Take a look at this thread c# entity framework: correct use of DBContext class inside your repository class
Hope this helps!
For your first question about the users only retrieving/writing data, concurrency won't be a problem. They will never try to update/delete a single row at the same time. The admin part is another story. You expect that multiple admin users will be using the database at the same time and this will lead to conditions where admin1 grabs a record, edits it, but when it reinserts the record it was already updated by admin2. There are 2 ways to handle this:
In your code model you can define an extra property with a
[TimeStamp]
attribute:or in fluent API you can map it like this:
You can also use the entire row as 'rowversion' to check for changes, but typically an extra column is used. Otherwise if you want to update just 1 field you'd have to send the whole row to the database because it's needed to check the rowversion. Also note that
[TimeStamp]
will only work for a byte array, if you want to use another type you have to use the[ConcurrencyCheck]
attribute. If you use the entire row as a rowversion you have to apply this attribute to all your properties.