Concurrency in ASP .NET MVC 5 with Entity Framewor

2019-05-19 23:54发布

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!

3条回答
欢心
2楼-- · 2019-05-20 00:15

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

public class FooService {
    private static object Obj = new object();
    public void Create(Foo foo) {
        lock (Obj) {
            // check + create
        }
    }
}

The problem here could be if you have multiple instances, as it is server level lock only.

Transactions

Database locks

using (var ctx = new DbCtx) {
    ctx.Database.Connection.Open();
    using (var transaction = ctx.Database.BeginTransaction(IsolationLevel.Serializable))
    {
        try {
            // check + update
            transaction.Commit();
        }
        catch (Exception) {
            transaction.Rollback();
        }
    }
    ctx.Database.Connection.Close();
}
查看更多
仙女界的扛把子
3楼-- · 2019-05-20 00:21

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?

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.

I know that some security mechanism are necessary to have safe system, but can you help me how to do this?

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.

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?

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!

查看更多
放荡不羁爱自由
4楼-- · 2019-05-20 00:28

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:

  1. Pessimistic concurrency: This means you are using locks on certain records of the database. While one process is updating a certain record, those rows get locked and no other processes can edit it meanwhile. Unfortunately this is not supported by Entity Framework.
  2. Optimistic concurrency: This is supported by EF and it means you have an extra column in your database with a rowversion. When a process tries to reinsert a record, it will first check if the rowversion hasn't changed. If it's the same, the record gets inserted. If it's changed the record gets refetched and the values get edited again. Msdn documentation on optimistic concurrency here.

In your code model you can define an extra property with a [TimeStamp] attribute:

[TimeStamp]
public virtual byte[] RowVersion {get; set;}

or in fluent API you can map it like this:

modelBuilder<MyEntity>().HasProperty(p => p.RowVersion).IsRowVersion();

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.

查看更多
登录 后发表回答