Showing posts with label Concurrency. Show all posts
Showing posts with label Concurrency. Show all posts

Sunday, November 20, 2011

Auditing and Concurrency don’t mix (easily)…

In MSDN forums I came across a post addressing an issue I have also faced. Auditing fields can cause concurrency issues in LightSwitch (not exclusively).
In general basic auditing includes keeping track of when an entity was created/modified and by whom. I say basic auditing because auditing is in general much more than this.
Anyhow, this basic auditing mechanism is very widely implemented (it’s a way for developers to be able to easily find a user to blame for their own bugs :-p), so let’s see what this can cause and why in LightSwitch.
In the aforementioned post but also in this one, I have clearly stated that IMHO the best way to handle concurrency issues is using RIA Services. If you don’t, read what follows.
Normally in any application, updating the fields that implement Audit tracking would be a task completed in the business layer (or even Data layer in some cases and this could go as deep as a database trigger). So in LightSwitch the first place one would look into to put this logic would be EntityName_Inserting and EntityName_Updating  partial methods that run on the server. Which is right, but causes concurrency issues, since after saving the client instance of the entity is not updated by the changes made at the server and as soon as you try to save again this will cause concurrency error.
So, what can you do, apart from refreshing after every save which is not very appealing? Update at the client. Not appealing either but at least it can be done elegantly:
Let’s say all entities to implement auditing have 4 fields:
  • DateCreated
  • CreatedBy
  • DateModified
  • ModifiedBy
Add to the Common project a new interface called IAuditable like below:

namespace LightSwitchApplication{
    public interface IAuditable{
        DateTime DateCreated { get; set; }
        string CreatedBy { get; set; }
        DateTime DateModified { get; set; }
        string ModifiedBy { get; set; }
    }
}



Then, also in the common project, add a new class called EntityExtensions:
namespace LightSwitchApplication{
    public static class EntityExtensions{
        public static void Created<TEntityType>(this TEntityType entity, IUser user)
           where TEntityType: IAuditable{
           entity.DateCreated = entity.DateModified = DateTime.Now;
           entity.CreatedBy = enity.ModifiedBy = user.Name;
        }

        public static void Modified<TEntityType>(this TEntityType entity, IUser user)
           where TEntityType: IAuditable{
           entity.DateModified = DateTime.Now;
           entity.ModifiedBy = user.Name;
        }
    }
}



Now let’s suppose your entity’s name is Customer (imagination was never my strong point), the screen’s name is CustomerList and the query is called Customers.

First Viewing the Customer entity in designer click write code and make sure that:
partial class Customer : IAuditable{
}

Then at your screen’s saving method write this:
partial void CustomerList_Saving{
    foreach(Customer customer in this.DataworkSpace.ApplicationData.Details.GetChanges().AddedEntities.OfType<Customer>())
        customer.Created(this.Application.User);
    foreach(Customer customer in this.DataworkSpace.ApplicationData.Details.GetChanges().ModifiedEntities.OfType<Customer>())
        customer.Modified(this.Application.User);
}


This should do it. This way you can also easily move your logic to the server as the interface and extension class are defined in the Common project and they are also available to the server.
 



Thursday, October 6, 2011

Concurrent or fast? I’d rather have both…

Did you know that concurrency provided automatically by LightSwitch might cause performance issues that you cannot easily trace?
LightSwitch uses Entity Framework’s concurrency mechanisms to ensure data consistency while 2 or more users modify the same entries. You can read about it in detail and with examples in this great (as always) post by Michael Washington.
The thing, though, is that using native data sources or SQL server imported data sources, LightSwitch applies concurrency checking to all fields and you can do nothing about it. So, what is wrong about that? The price you have to pay in performance terms is small in exchange to ensuring concurrency, one would say. Unfortunately this is not always the case. Apart from functionality issues that can easily come up (entities with last modification fields for example), there is one special but not so uncommon case that can kill your performance. Large binary fields. Images is the most common example. Checking images byte against byte with every record updated for modifications, is something that, in most cases at least, no one wants.
As in many other cases, RIA comes to the rescue. With RIA services, importing your domain objects in an EF entity model you can fully control which fields are checked for concurrency and which are not. Not only this, but also concurrency exceptions are passed-through to LightSwitch and the resulting behavior is exactly the same as if you were using native or SQL server imported data sources. This is great don’t you think…Winking smile