Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

Dot Net Rules

Yes, to dance beneath the diamond sky with one hand waving free

Ιστορικό Δημοσιεύσεων

Φεβρουάριος 2016 - Δημοσιεύσεις

Handling concurrency in Code First ASP.Net 5.0 MVC applications

In this post I am going to provide you with a hands on example on how to handle concurrency in an EF Code First ASP.Net MVC 5.0 application. Basically I want to show you how to handle conflicts when multiple users update the same entity at the same time.

You can find another post regarding concurrency with EF Database First and web forms here.

When we are talking about concurrency in an application where EF acts as the data access layer, our main focus is how to make sure that the integrity of the data in the database is ensured in a multi user connected environment.

Databases are very good in managing concurrent users. All the objects/entities are retrieved from the datastore into the memory. The changes we make are applied in the memory and then we flush the changes to the database.

But what happens if those entities have been changed from another user in the time it took us to update them in memory?Well someone will say, "Dude, you just have to use pessimistic concurrency and your problems are solved."

Well, I do not want to go down that road. Locks are applied to all related data when I use this type of concurrency management.

Remember we are in a multiuser enviroment. That means I want to have data integrity on the one hand but I also want performance to be at an acceptable level. With pessimistic concurrency you limit scalability and performance. The Entity Framework provides no built-in support for it.

So I will use optimistic concurrency which in plain words mean "Hm... I want to check if anyone has modified the data in the database while I did my own modifications in memory".

The very basic scenario in a multi-user environment where a concurency conflict takes place is when one user displays an entity's data in order to edit it, and then another user updates the same entity's data before the first user's change is written to the database.

If you don't enable the detection of such conflicts, whoever updates the database last overwrites the other user's changes.

In many applications, this risk is acceptable: if there are few users, or few updates, or if isn't really critical if some changes are overwritten, the cost of programming for concurrency might outweigh the benefit.

In that case, you don't have to configure the application to handle concurrency conflicts.

I will use Visual Studio 2015 Enterprise edition,C# and EF Code First 6.1.3 version to build an MVC 5.0 application that 

  • will list information about a database that holds information about footballers
  • will enable the user to create a new footballer
  • will enable the user to edit an existing footballer
  • will enable the user to delete an existing footballer

1) I am creating an ASP.Net Web Application, namely ConcurrencyViolationEFCodeFirst. I will use the ASP.Net 4.5.2 Empty (MVC template).I have an application with no models,views or controllers. 

2) Now I will add a model to the application.I will use this model to manage the footballer's data-entities.

I will use Entity Framework as the data access technology. More specifically I will use the Code First paradigm. You might have come across the term POCO classes.This is exactly what I am going to use.If you want to learn more about Code First have a look in this post

I need to install Entity Framework. I can do that from the Nuget Package Manager.

Then from the model class we will create our database.In the Solution Explorer right-click on the Models folder,select Add and then select Class

Name the class Footballer.cs and add some properties to the class.My complete class follows

    public class Footballer
    {
        public int FootballerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public double Weight { get; set; }
        public double Height { get; set; }
        public DateTime JoinedClub { get; set; }
     

    }

3) Let me explain what I am doing here. I will use the Footballer class to represent footballers in a database (which I am going to create). Each instance of a Footballer class will correspond to a row within a database table.Naturally  each property of the Footballer class will map to a column in the table (which I am going to create).

We need to add some more code to a separate class the FootballerDBContext.cs

    public class FootballerDBContext : DbContext
    {
        public DbSet<Footballer> Footballers { get; set; }
    }

The FootballerDBContext is a database context class.This class is responsible for talking to the underlying database,storing and updating the data to the database.

We need to add this reference to the file

using System.Data.Entity;

4) Now we need to create the connection string.The only place we can do that is by opening the web.config file and adding the following lines of code (inside the   <connectionStrings>   section)

      <add name="FootballerDBContext"
   connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Footballers.mdf;Integrated Security=True"
   providerName="System.Data.SqlClient"
/>

As you can see from the connection string I am using LocalDB.

Have a look here in order to see what LocalDB is.

5) Now we need to access our model from a controller.This is going to be a simple class that retrieves the footballers data.

Right-click the Controllers folder and create a new FootballerController controller. We will use the scaffolding system of ASP.Net MVC to create our controllers class and its action methods for edit,list,create, delete and the views needed to dispay the data to the user. We will use the Scaffold MVC 5 Controller with views, using Entity Framework.

The model class would be:Footballer (ConcurrencyViolationEFCodeFirst.Models)

The Data Context class would be:FootballerDBContext (ConcurrencyViolationEFCodeFirst.Models)

6) The ASP.NET MVC 5 framework as you can see automatically creates the CRUD (create, read, update, and delete) action methods and views.We have a fully functional web application that lets you create, list, edit, and delete records. 

7) Build and run your application.Navigate to the localhost/youport/footballer

8) You can create a new footballer object-record.This information will be saved in the LocalDB-Footballers.mdf database.It is created by EF when you first run the application.

9) Make sure you add more entries to the database through the view/application.  

We have created a new record and stored it in the database.Click the Edit,Details and Delete links.We have all this functionality out of the box through the magic of scaffolding. 

I urge you to have a look (place breakpoints as well) in the FootballerController.cs class file.

We pass a strongly typed object (Footballer) to the various views.

Have a look in the views inside the Views/Footballer folder.

In the Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, and Index.cshtml Views , at the beginning of these files, you will see this line of code.

@model IEnumerable<ConcurrencyViolationEFCodeFirst.Models.Footballer>

By adding a @model statement at the top of the view  file, we tell the view the type of object that the view should render.

10) When running the application and hitting the record (I have added a couple of rows in the database through the UI already) http://localhost:4809/footballer/Edit/1 you can edit the record.Make some changes in the record but before hitting "Save" open the same record in another browser - http://localhost:4809/footballer/Edit/1 and change some other value of the same record- entity and then hit the "Save" button in the first window/browser. The changes will be saved to the database. Hit "Save" in the other second browser window. You will then see that the changes you made initially have been lost and only the changes in the second browser window have been saved. You have lost data integrity. Basically your data is of no value. We will demonstrate below mechanisms where you will not lose your data and handle concurrency exceptions.

This is called a Client Wins or Last in Wins scenario. (All values from the client take precedence over what's in the data store.) If you don't do any coding for concurrency handling, this will happen automatically.

You can prevent changes from the second browser/windows/user from being updated in the database. Typically, you would display an error message, show the current state of the data, and allow him/her to reapply her changes if she/he still wants to make them. This is called a Store Wins scenario. (The data-store values take precedence over the values submitted by the client.)

This method ensures that no changes are overwritten without a user being alerted to what's happening.

11) We can resolve conflicts by handling OptimisticConcurrencyException exceptions that the Entity Framework throws.

In order to know when to throw these exceptions, the Entity Framework must be able to detect conflicts. Therefore, you must configure the database and the data model appropriately.

Entity framework code first offers two approaches to detect concurrency violations:

  • [ConcurrencyCheck] attribute
  • [Timestamp] attribute

The first approach is used in cases where your table doesn't have any timestamp or rowversion column.

You are required to decorate all the non-primary key properties of an entity with [ConcurrencyCheck] attribute. That usually requires a lot of work.

Entity framework then adds all these columns to the WHERE clause of the resultant SQL statement. This way you check whether all the column values that you fetched and the values in the database are same or not.

If they are same then noone else modified that record and your UPDATE / DELETE succeeds.

If the values don't match then someone else has modified the values and your UPDATE / DELETE statement fails.

In the other approach we add a timestamp or rowversion column in your table.

In this case you decorate only the timestamp / rowversion property with the [Timestamp] attribute.

Just like [ConcurrencyCheck] entity framework then adds WHERE clause for this property.The Timestamp attribute specifies that this column will be included in the Where clause of Update and Dlelete commands sent to the database.

The advantage here is that you have only one column in the WHERE clause (in addition to the primary key) instead of many as in the case of [ConcurrencyCheck] attribute. The SQL server timestamp / rowversion gets translated as a byte[] in .NET code.

12) We change the definition of the public class Footballer by adding the Timestamp attribute to the RowVersion column

public class Footballer
{
public int FootballerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public double Weight { get; set; }
public double Height { get; set; }
public DateTime JoinedClub { get; set; }

[Timestamp]
public byte[] RowVersion { get; set; }

}

13) If we build and run the application we will receive an error

"The model backing the 'FootballerDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database"

By adding a property you changed the database model, we need to do a migration. If you want to find out more about Code First Migrations .In the Package Manager Console (PMC), enter the following commands:

Enable-Migrations

Add-Migration RowVersion 

Update-Database

14) Now if we look back through Server Explorer in the table we will see a Rowversion column. Now if we build and run the application we  will receive no error. Have a look at the picture below.

15) Now we need to change the code inside the HttpPost Edit method with the following code:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "FootballerID,FirstName,LastName,Weight,Height,JoinedClub,Rowversion")] Footballer footballer)
{
if (ModelState.IsValid)
{

try
{
db.Entry(footballer).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}

catch (DbUpdateConcurrencyException ex)
{

ModelState.AddModelError("", "Unable to save changes. The record was modified by another user after you got the original value");



}

}
return View(footballer);
}

If no rows are affected by the UPDATE command (no rows have the original RowVersion value),  the Entity Framework throws a DbUpdateConcurrencyException exception.

16) Now we build and run the application again.

When running the application and hitting the record (I have added a couple of rows in the database through the UI already) http://localhost:4809/footballer/Edit/1 you can edit the record.Make some changes in the record but before hitting "Save" ,open the same record in another browser(e.g Opera) - http://localhost:4809/footballer/Edit/1 and change some other values of the same record- entity and then hit the "Save" button in the first browser. You will not have an problems, the data is persisted to the database.

Then hit save in the second browser window. You will then see that the changes you made initially have been lost and only the changes in the second browser window have been saved.

Instead of seeing your changes in the second browser window persisted to the db you will receive the error below

"Unable to save changes. The record was modified by another user after you got the original value"

Now you cannot save data back to the datastore. You can go back to the list of records, get record 1 again from the database and edit/save the new values.

Hope it helps!!!

Caching an ASP.Net MVC 5.0 application

In this post I am going to provide you with a hands on example on using caching in an ASP.Net MVC 5.0 application using Visual Studio 2015 and C#.

More particularly I'll show you how to use the underlying ASP.net caching engine to improve the performance of an application. 

There is a number of different cache settings you can use.These settings are all available from the underlying ASP.net cache engine. It's the same engine that Web Forms uses. It's not something that's specific to just the MVC framework.

But first things first. What is caching why do we need caching in the first place?

Caching is of vital significance in any high-performance web application.

Caching provides a way of storing frequently accessed data and reusing that data.

It is an effective way for improving web application’s performance and user experience.

The main advantages of caching include:

  • Reduce network traffic - Content is cached at the client side, thus reducing network traffic
  • Reduce database server round trips -Content that is cached at the web server, reduces requests to the database
  • Reduce hosting server round-trips - Requests to the server are reduced
  • Not generating reusable content - Avoid time and resource consumption for regenerating reusable content


It is profound that caching benefits the performance and user experience for a web application.

The following points indicate the situation when caching should be considered

  • Content that is frequently accessed should be a good candidate for caching
  • Content thatis unique for a user/session should not be cached
  • Content that is very infrequently accesses should not be cached
  • For content that is dynamically produced and changes frquently do not disable caching. Do define a shorter cache–expiration instead.
  • When you want to cache versions of a page based on request such as cookies, theme, browser ,you could use the VaryByCustom function


When it comes to Caching an ASP.Net MVC 5.0 application, we will use Output caching.

Output caching allows you to store the output of a particular controller action in memory.

Then the ASP.net engine can respond to future requests for the same action just by giving back the cached result.

By doing that no code inside the action is executed. Having said that even though ASP.Net MVC is an entirely new paradigm on building an ASP.Net web application, is based on ASP.Net infrastructure and on the caching functionality that ship with ASP.Net.

In the ASP.Net MVC world we are familiar with terms like Model, View, Controller, ActionResults, Action Selectors, Code Expressions, Razor expressions and Action Filters. Using Action Filters is the way to enable Output Caching in a controller action.

By default, this attribute filter caches the data for 60 seconds. After 60 seconds, you guessed it, a call is made again to this action and ASP.NET MVC will cache the output again.

You can cache controller actions that produce any king of result (Views, static contents, Json). 

I pointed out before we should cache content that is frquently accessed so in our MVC application we will cache these actions in a controller that are called often and execute "expensive statements" like db queries.

Do not randomly cache any action method in a controller. You must devise a successful caching strategy and to really know where your traffic is going. You have to know what are the most expensive operations in your software.

You have to take some measurements and do some logging in order for caching to really work effectively.

Let's move on to the actual hands-on example.

If you are not familiar with ASP.Net MVC please have a look at this post of mine.

1) I have installed Visual Studio 2015 Enterprise edition in my machine which is Windows 8 by the way.I have also installed SQL Server 2014 Express Edition in my machine.

I am going to build an ASP.Net MVC 5.0 application using Entity Framework and Code First. We will target the 4.5.2 version of the framework. Firstly I will start with a simple example with no database access.

ASP.Net MVC does not dictate what kind of data access architecture we will use in our application. It does not also dictate how to build our business layer (domain classes and objects).

2) I am launching VS 2015 and I will Visual C# as the programming language. I will also select ASP.NET MVC 5 Web Application from the available templates (Empty Template using MVC).

3) I am adding a HomeController.cs class in the Controllers folder.

4) The code in the Controller action follows

public class HomeController : Controller
{
// GET: Home

[OutputCache(Duration =20, VaryByParam="none")]
public ActionResult Index()
{

ViewBag.Message = DateTime.Now.ToString();
return View();
}
}

5) Set a break point at the beginning of this action and then run with the debugger.

6) What you should see here is on this first request, we will hit that break point so we're inside of the index action and you'll notice the one parameter I have to specify with the output cache attribute is the duration.

7) How long do I cache the response? It's specified in seconds and this is 20 seconds. So now if I press F5 to continue with the debugger, I can now see the home page and the caching time and now I can refresh this as many times as I want and the caching logic inside of ASP.net is looking at the request, seeing that there's a cached response for this request.

8) The code inside the Index.cshtml view follows:


@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<div>
<b>Caching Time:</b>@ViewBag.Message

</div>

9) Remove the debugger from the Action and then run the application again. Refresh the page.For a period of 20 seconds you will see the same time. After 20 seconds the action method will run again and cache the output again for another 20 seconds.

Let's have a look now in an example where we cache results from a database.

10)I will create a new ASP.Net MVC 5  application using the Empty Template (MVC). Now I will add a model to the application.I will use this model to manage footballer's data.I will use Entity Framework as the data access technology. More specifically I will use the Code First paradigm. You might have come across the term POCO classes.This is exactly what I am going to use.If you want to learn more about Code First have a look in this post

Then from the model class we will create our database.In the Solution Explorer right-click on the Models folder,select Add and then select Class

Name the class Footballer.cs and add some properties to the class.My complete class follows

    public class Footballer
    {
        public int FootballerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public double Weight { get; set; }
        public double Height { get; set; }
        public DateTime JoinedClub { get; set; }
     

    }

Let me explain what I am doing here. I will use the Footballer class to represent footballers in a database (which I am going to create). Each instance of a Footballer class will correspond to a row within a database table.Naturally  each property of the Footballer class will map to a column in the table (which I am going to create).

We need to add some more code in another calss to the FootballerDBContext.cs

    public class FootballerDBContext : DbContext
    {
        public DbSet<Footballer> Footballers { get; set; }
    }

The FootballerDBContext is a database context class.This class is responsible for talking to the underlying database,storing and updating the data to the database.

We need to add this reference to the file

using System.Data.Entity;

11) Now we need to create the connection string.The only place we can do that is by opening the web.config file and adding the following lines of code (inside the   <connectionStrings>   section). I am 

      <add name="FootballerDBContext"
   connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Footballers.mdf;Integrated Security=True"
   providerName="System.Data.SqlClient"
/>

As you can see from the connection string I am using LocalDB.

Have a look here in order to see what LocalDB is.

12) Now we need to access our model from a controller.This is going to be a simple class that retrieves the footballers data.

In the Controllers folder add the FootballerController.cs file.

Right-click the Controllers folder and create a new FootballerController controller. Have a look at the picture below to set the appropriate settings

Inside the FootballerController controller,  the Index action follows:

 private FootballerDBContext db = new FootballerDBContext();

 public ActionResult Index()
{
ViewBag.Message = DateTime.Now.ToString();
var model = from foot in db.Footballers
select foot;
return View(model.ToList());
}

In the above code, we have created an object of FootballerDBContext and we are querying the Footballers table using a LINQ Query under Index method. We are also passing the result of the Query to our view, so that we can display the data from the Footballers table in our Footballer’s Index view which is generated automatically with the MVC scaffolding system when the controller is created.

The Index.cshtml contents follow:

@model IEnumerable<DemoCachingData.Models.Footballer>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<b>Caching Time:</b>@ViewBag.Message

@*<p>
@Html.ActionLink("Create New", "Create")
</p>*@
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.Weight)
</th>
<th>
@Html.DisplayNameFor(model => model.Height)
</th>
<th>
@Html.DisplayNameFor(model => model.JoinedClub)
</th>
<th></th>
</tr>

@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Weight)
</td>
<td>
@Html.DisplayFor(modelItem => item.Height)
</td>
<td>
@Html.DisplayFor(modelItem => item.JoinedClub)
</td>
@*<td>
@Html.ActionLink("Edit", "Edit", new { id=item.FootballerID }) |
@Html.ActionLink("Details", "Details", new { id=item.FootballerID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.FootballerID })
</td>*@
</tr>
}

</table>

When we run for the first time the application the underlying database and table are created.Entity Framework Code First created the database for us. EF detected that the database connection string provided, pointing to a database didn’t exist, so Code First created the database automatically.

From VS you can show all files of the application. Under App_Data special folder you will see the FootBallersDBContext database.

Have a look at the picture below

13) Now let’s add some records in our Footballers table (I added two record straight from the Server Explorer UI) and then run the application. You will see the records displayed on our Index page.

For increasing the performance of our page, we will make use of OutputCache Action filter. As I mentioned earlier, this attribute can be applied either on an individual action method or on the entire controller.

Let’s apply this attribute on our Action Method Index which is available under FootballerController as shown below

[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index()
{
 ViewBag.Message = DateTime.Now.ToString();
 var model = from foot in db.Footballers
select foot;
 return View(model.ToList());
}

14) Now let’s run the page and check the output by refreshing it multiple times. The page should be cached for 1 minute.

In addition to duration, the output cache attribute supports a number of different settings like VaryByParam.  The default setting for this is star or asterisk which means vary by every parameter possible and that is usually the setting that you want. That's the default because you normally do not want to return the same cached response for different parameters and by parameters think of things like query string parameters. A query string parameter can point to a record you wouldn't want to return the same cached response when someone is looking for record 1 and 2.

15) Now let’s test the VaryByParam attribute. Pass an ID parameter to the Index action methods and modify the code as shown below

[OutputCache(Duration = 60, VaryByParam = "ID")]
public ActionResult Index(int ID)
{
ViewBag.Message = DateTime.Now.ToString();
var model = from foot in db.Footballers
where foot.FootballerID == ID
select foot;
return View(model.ToList());
}

Now run the application again.This time pass an ID as a query string to the URL and see the output of your page. When you pass the different IDs with the URL, the caching will vary based on the IDs. For example –

http://localhost:61850/Footballer/Index/1 will be different than the http://localhost:61850/Footballer/Index/2 

16) You can also set the cache location. The default value here is anywhere meaning it will cache on the server and the client can also cache the result.

You can be very specific and say that the result should only be cached on the server or on the client or on proxy servers in between.

If I want to cache the results on the  server I need to modify the Action Filter of the Index method, like the following:

 [OutputCache(Duration = 60, VaryByParam = "ID", Location=OutputCacheLocation.Server)]

The OutputCache attribute values can be: Any, Client,Downstream, Server, None, or ServerAndClient.

For configuring the cache location, you need to add the System.Web.UI namespace on your controller.

17) There's also a vary by header setting. That allows you to vary the cache based on a specific HTTP header like the language of the browser/client.

18) You can also make use of SqlDependency to invalidate the cache when the cached data gets changed on the Server side.

Finally I want to talk about Cache Profiles.

Cache Profiles are very helpful when it comes to pick the correct cache duration and other cache settings. You can create different cache profiles and use them accordingly to fit your caching strategy. ASP.net allows you to specify a cache profile in the output cache attribute.

The cache profile then is something that's stored in your web.config file and can specify a duration. It's underneath a section named caching and it's inside of here where you can put duration values.

You can have multiple cache profiles and you reference the cache profile by name in the output cache attribute.

It is recommended to use cache profiles instead of hard code the cache settings in the OutPutCache. We avoid repetition in the cache attributes.

If we want to double the duration inside of all of your cache attributes, we don't have to do a search and replace throughout the code. We just go to one location in your web.config file.

It's also easier to change the cache settings once the application is deployed because now all you do is edit the web.config file and then you avoid changing the settings in code, recompiling, and redeploying.

Inside the web.config file in the <system.web> section I can add my cache profile.


<system.web>


<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheMax" duration="3600" varyByParam="none"/>

<add name="CacheMin" duration="20" varyByParam="none"/>
</outputCacheProfiles>
</outputCacheSettings>


</caching>
....
</system.web>

Let’s apply the new CacheProfile (e.g CacheMax) on our Action Method Index which is available under FootballerController as shown below


[OutputCache(CacheProfile = "CacheMax", VaryByParam = "ID", Location = OutputCacheLocation.Server)]

Hope it helps.