This project has moved. For the latest updates, please go here.

NTierEntityFramework and MVC thread handling

May 6, 2015 at 4:40 PM
Hi,

I try to use NTierEntityFramework with MVC on client side and have thread locking issue.

As per MVC code example, when new controller initiated it creates DataContext object which stores current thread in DataContext.Dispetcher field.

Lets say current thread is #N. When controller serves next request it takes a working thread from thread pool. There are two possible cases:

a) the same thread #N (works fine), or
b) another thread #K (here the issue comes)

Controller creates new view in thread #K but DataContext invokes thread #N (BTW #N may or may not even exist) for processing data for the view. As long as thread #N is not UI thread anymore it gets locked at dispatcher.Invoke(action) in code below.
   protected void Invoke(Action action, bool invokeAsync = false)
    {
        var dispatcher = Dispatcher;
        if (dispatcher == null || dispatcher.CheckAccess())
        {
            action();
        }
        else if (dispatcher.HasShutdownStarted || dispatcher.HasShutdownFinished)
        {
            // dispatcher must no longer be used
        }
        else if (invokeAsync)
        {
            dispatcher.BeginInvoke(action);
        }
        else
        {
            dispatcher.Invoke(action);
        }
    }
Any suggestions?

Thank you,

Serge.
Coordinator
May 7, 2015 at 11:26 PM
What happens if you set the dispatcher to null when creating your data context instance?
ctx = new MyDataContext() { Dispatcher = null };
As a side note: would it not make sense to new-up a new data context for every new request being served in order to ensure your controller remains stateless?
There might be no need to keep the data context in the session as per the MVC sample.
May 11, 2015 at 7:17 PM
Yep, having Dispatcher = null solves the problem. That was my initial solution but just being new to N-Tier Entity Framework was not 100% sure.

Regarding creating new data context for each request. Looks like it does not work for me. I have to support optimistic concurrency in my application.

I am thinking to have data context instance per session instead of per controller as it done in MVC code sample. I am expecting quite a few controllers and not sure of benefits of having so many instances of data context. Any downside in that approach?

Thank you for your help.

Serge.
Coordinator
May 11, 2015 at 7:35 PM
Good to hear the dispatching issue vanished :-)

Data context kept in sessions is an issue if it comes to scalability. Hence, avoid if possible as it might strike back later…. That is, if you can round-trip the original concurrency properties via your web-client you can also do your optimistic locking.
May 12, 2015 at 7:47 PM
Yep, it makes sense to release data context after each request, especially in web application where amount of active sessions could be fairly big. Completely agree.

Now, going back to concurrency tracking. In my tests it works fine when I read original data and write modified data in the same instance of data context. To have concurrency tracking in separate instances of data context I need to restore DBContext.Entry(<Entity>).OriginalValues[<concurrency property>] before saving. I do have original values for concurrency property, problem is I do not have access from client to those server entries to reset original values.

EF uses Timestamp or ConcurencyProperty attributes to mark concurrency properties. Is it possible to restore properties with such attributes to their original values in some new or existed Common.Domain level method which accepts original and modified entities as parameters. For example, in AttachAsModified(). Those properties are not being updated in database by EF anyway so should not affect existed functionality. Please advise.

Thank you,

Serge.
Coordinator
May 13, 2015 at 8:44 AM
Typically you’d just use Attach(Entity) to save changes of a detached entity. You can use AttachedAsModified(Entity, Entity) in case your entity has not change tracking included and you’d rather provide two instances of an entity, one containing updated properties and the other with the properties set to their original values.

You may want to have a look at the ConcurrencyDemo sample for reference.
May 25, 2015 at 4:55 PM
Hi Christof,

My goal is to use EF build-in concurrency handling. Due to MVC specific I use new data context for each controller method. I keep original entity and before save/delete I upload existed entity. Then I use
AttachAsModified(originalEntity, existedEntity); 
for changes tracking. To have EF concurrency tracking to work after AttachAsModified() call I have to set
existedEntity.ChangeTracker.OriginalValues["RowVersion"] = originalEntity.RowVersion; 
(where RowVersion is concurrency property). EF adds those concurrency fields with original values in WHERE clause of UPDATE/DELETE. If record was modified by another user then record with such values not found and concurrency exception fired.

My suggestion is to have this logic embedded inside AttachAsModified() (or inside one of the methods called from it) for all concurrency properties instead of handling that by caller outside for each entity. If it makes sense for you.

Also you use custom ConcurrencyPropertyAttribute to mark concurrency properties in a model. Not sure if you are aware that starting from v 4.0 of .NET framework there are System.ComponentModel.DataAnnotations.ConcurrencyCheckAttribute and System.ComponentModel.DataAnnotations.TimestampAttribute which are used by EF.

Thank you,

Serge
Coordinator
May 28, 2015 at 1:47 PM
Hi Serge

Maybe I got you wrong, but as I understand the originalEntity is the unmodified version you keep on the server (e.g. in the UserSession) while existedEntity was modified and submitted by the client. In this case you need to switch order of the parametes:
AttachAsModified(existedEntity, originalEntity);
Apart from that it’s not clear why you have to set original value on the change tracker, since RowVersion would not have changed and is always sent along updates to the server. You just have to ensure the RowVersion property is annotated with ConcurrencyPropertyAttribute.

Concerning ConcurrencyCheckAttribute and TimestampAttribute I’m not sure if they would make any difference as N-Tier Entity Framework is currently using model-fist approach rather than code-first.

Regards, Christof
May 29, 2015 at 4:19 PM
Hi Christof,

Thank you for your responses. My previous explanations seems to be confusing. I will try to be more specific and detailed. Below is my Product controller code example for edits:
    #region Edit

    // GET: /Product/Edit/[id]
    public async Task<ActionResult> EditAsync(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        using (var dc = new NorthwindDataContext() {MergeOption = MergeOption.NoTracking, Dispatcher = null})
        {
            var product =
                (await dc.Products
                        .AsQueryable()
                        .Where(p => p.ProductID == id)
                        .ExecuteAsync())
                        .ResultSet
                        .FirstOrDefault();

            if (product == null)
            {
                return HttpNotFound();
            }

            await GetNavigationListsAsync(dc, product.SupplierID, product.CategoryID);
            return View(product);
        }
    }

    // POST: /Product/Edit/[id]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> EditAsync([Bind] Product product)
    {
        using (var dc = new NorthwindDataContext() {MergeOption = MergeOption.NoTracking, Dispatcher = null})
        {
            if (ModelState.IsValid)
            {
                var existingProduct = (await dc.Products
                            .AsQueryable()
                            .Where(p => p.ProductID == product.ProductID)
                            .ExecuteAsync())
                            .ResultSet
                            .FirstOrDefault();
                if (existingProduct != null)
                {
                    dc.AttachAsModified(product, existingProduct);
                    existingProduct.ChangeTracker.OriginalValues["RowVersion"] =
                        product.RowVersion;
                    try
                    {
                        await dc.SaveChangesAsync();

                        return RedirectToAction("IndexAsync");
                    }
                    catch (OptimisticConcurrencyException /* ex */)
                    {
                        ModelState.AddModelError(string.Empty,
                            "Unable to save changes. The record you attempted to edit was modified by another user.");
                    }
                    catch (UpdateException /* ex */)
                    {
                        //Log the error (uncomment ex variable name and add a line here to write a log.
                        ModelState.AddModelError(string.Empty,
                            "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
                    }
                }
                else
                {
                    ModelState.AddModelError(string.Empty,
                        "Unable to save changes. The record was deleted by another user.");
                }
            }
            await GetNavigationListsAsync(dc, product.SupplierID, product.CategoryID);
        }
        return View(product);
    }

    #endregion Edit

Each time I need to communicate to database I create data context instance and destroy it immediately after usage. In GET EditAsync method I retrieve a product and display in Edit view for user edition. When user done and saves changes POST EditAsync gets the product changed by user as parameter. Then I load server version (existingProduct) in separate data context and call AttachAsModified(product, existingProduct). In case server version of object were changed it has new value of RowVersion and to let EF to catch a conflict it's OriginalValue should be fabricated to original object value (product.RowVersion). I do this before saving changes and it works fine. But for me it makes sense to have it done inside AttachAsModified by N-Tier library.

Regarding concurrence attribute. Yes, that is not big deal now. But in EF 7 version announced that EMDX format getting retired and the only option remaining is code first.

Thank you,

Serge