Calling SaveChanges once per request in Asp.Net MVC application - asp.net-mvc

I'm trying to create a mostly simple web app with Asp.Net MVC and Entity Framework. I actually finished few projects with it but wasn't satisfied about the code so I'm following some more popular ways to do it. Here is my structure:
Controller gets the request,
I have services which contain business logic. They also use my Database Context to change the data on the database. I didn't want to create another database layer so I'm using them,
I inject my services to controller (with Unity) and call stuff like CustomerService.Delete(Id)
My service deletes the data based on Id.
So my controller does not include any logic or database operations, my services does include both. I think it's a good way to do stuff but I have a problem.
Let's say I add a customer for the first time and there is another table keeping their balance and I want to include a bonus $10 for first registration, when I call my CustomerService.Add(Customer), that method also calls CustomerService.AddBalance(Customer, 10). In those methods I call DbContext.SaveChanges, here is the problem, I call SaveChanges 2 times, and if CustomerService.AddBalance(Customer, 10) fails for some reason I will still have the customer data but not the balance one. I know I can use a transactions but where do I put that code? If I knew there was a place which runs last before request is finished, I could call SaveChanges() there and it would work but I think that's not a great idea either.
Basically I want to call SaveChanges() once per request but I couldn't find a good place to do it.
Thanks

If you are doing any work with the database you want to be using transactions. If you don't you are going to leave your db in an inconsistent state if one of your calls fails. Using transactions, nothing will be committed in the database until you call Commit. You can call SaveChanges as much a you like and it won't make a difference.
You can create a TransactionScope in a attribute and then add it to any controller action where you are accessing the db. Here' one I use
[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : ActionFilterAttribute
{
private TransactionScope TransactionScope { get; set; }
public override void OnActionExecuting(HttpActionContext filterContext)
{
TransactionScope =
new TransactionScope(TransactionScopeOption.Required, new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted
});
}
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
if (TransactionScope == null)
return;
if (filterContext.Exception == null)
{
TransactionScope.Complete();
return;
}
TransactionScope.Dispose();
}
}
You could put the transaction code in BeginRequest stash it in HttpContext and commit on EndRequest. This is going to create a TransactionScope for every call that asp.net handles.

According to documentation EF by default sets all the changes to database in transaction until SaveChanges() is called.
So you can extract the logic of adding to database to separate methods, create separate method which saves changes to DB and design your code as following:
AddCustomer to DB (do not call save changes)
AddBalance to DB(do not call save changes)
SaveChanges on the end of the in your service method
This will guarantee that all happens in transaction and in case of any error changes to database won't be applied.

I have found that creating an Extension to your Context (i am assuming your are using EF) is a good way to go:
public static class TIContextCompleteExtention
{
public static void Complete(this TIContext context)
{
try
{
context.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
public static async Task CompleteAsync(this TIContext context)
{
try
{
await context.SaveChangesAsync();
}
catch (Exception ex)
{
throw ex;
}
}
}
The Above TIConext is derived from EF's dbContext.
Wherever you need to COMPLETE your transaction use
_context.Complete(); or _context.CompleteAsync() instead of EF's SaveChanges() / SaveChangesAsync()
If you later swap out your ORM all you need to do is change the above extensions to fit your new Context.
Hope this helps.

Related

Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter

I am using EF Core 1.0 (previously known ad EF7) and ASP.NET Core 1.0 (previously known as ASP.NET 5) for a RESTful API.
I'd like to have some unit of work scoped to an http request in such a way that when responding to the HTTP request either ALL the changes made to the DbContext will be saved onto the database, or none will be saved (if there was some exception, for example).
In the past I have used WebAPI2 for this purpose with NHibernate by using an Action filter where I begin the transaction on action executing, and on action executed I end the transaction and close the session. This was the way recommended at http://isbn.directory/book/9781484201107
However now I am using Asp.Net Core (with Asp.Net Core Mvc although this should not be relevant) and Entity Framework which, I understood, already implements a unit of work.
I think having a middleware plugged into the ASP.NET pipeline (before MVC) would be the right way to do things. So a request would go:
PIPELINE ASP.NET: MyUnitOfWorkMiddleware ==> MVC Controller ==> Repository ==> MVC Controller ==> MyUnitOfWorkMiddleware
I was thinking of having this middleware save the DbContext changes if no exception happened, so that in my repository implementations I don't even need to do dbcontext.SaveChanges() and everything would be like a centralized transaction. In pseudocode I guess it would be something like:
class MyUnitOfWorkMiddleware
{
//..
1-get an instance of DbContext for this request.
try {
2-await the next item in the pipeline.
3-dbContext.SaveChanges();
}
catch (Exception e) {
2.1-rollback changes (simply by ignoring context)
2.2-return an http error response
}
}
Does this make sense? Does anybody have any example of something similar? I can't find any good practice or recommendation around this.
Also, if I go with this approach at my MVC controller level I would not have access to any resource ID created by the database when POSTing a new resource because the ID would not be generated until the dbContext changes are saved (later on in the pipeline in my middleware AFTER the controller has finished executing). What if I needed to access the newly created ID of a resource in my controller?
Any advice would be greatly appreciated!
UPDATE 1: I found a problem with my approach to use middleware to achieve this because the DbContext instance in the middleware is not the same as during the MVC (and repositories) lifetime. See the question Entity Framework Core 1.0 DbContext not scoped to http request
UPDATE 2:I haven't yet found a good solution. Basically these are my options so far:
Save the changes in DB as soon as possible. That means saving it on the repository implementation itself. The problem with this approach is that for an Http request maybe I want to use several repositories (i.e: save something in database and then upload a blob to a cloud storage) and in order to have a Unit of Work I would have to implement a repository that deals with more than one entity or even more than one persistance method (DB and Blob Storage), which defeats the whole purpose
Implement an Action Filter where I wrap the whole action execution in a DB transaction. At the end of the controller's action execution, if there are no exceptions I commit chanches to DB but if there are exceptions I rollback and discard the context. The problem with this is that my controller's action may need a generated Entity's Id in order to return it to the http client (i.e: If I get a POST /api/cars I would like to return a 201 Accepted with a location header that identifies the new resource created at /api/cars/123 and the Id 123 would not be available yet since the entity has not been saved in DB and the Id is still a temporary 0). Example in controller's action for a POST verb request:
return CreatedAtRoute("GetCarById", new { carId= carSummaryCreated.Id }, carSummaryCreated); //carSummaryCreated.Id would be 0 until the changes are saved in DB
How could I have the whole controller's action wrapped in a DB transaction and at the same time have available any Id generated by the database in order to return it in the Http Response from the controller? Or.. is there any elegant way to overwrite the http response and set the Id at the action filter level once the DB changes have been commited?
UPDATE 3: As per nathanaldensr's comment I could get the best of both worlds (wrapping my controller's action execution in a DB transaction _ UoW and also knowing the Id of the new resource created even before the DB commits changes) by using code generated Guids instead relying on database to generate the Guid.
As per Entity Framework Core 1.0 DbContext not scoped to http request
I could not use a middleware to achieve this because the instance of DbContext that the middleware gets injected is not the same as the DbContext during MVC execution (in my controllers, or repositories).
I had to go with a similar approach to save the changes in DbContext after the controller's action execution using a Global Filter.
There is no official documentation yet about filters in MVC 6 so if anybody is interested on this solution see below the filter and the way I make this filter global so that it executes before any controller's action.
public class UnitOfWorkFilter : ActionFilterAttribute
{
private readonly MyDbContext _dbContext;
private readonly ILogger _logger;
public UnitOfWorkFilter(MyDbContext dbContext, ILoggerFactory loggerFactory)
{
_dbContext = dbContext;
_logger = loggerFactory.CreateLogger<UnitOfWorkFilter>();
}
public override async Task OnActionExecutionAsync(ActionExecutingContext executingContext, ActionExecutionDelegate next)
{
var executedContext = await next.Invoke(); //to wait until the controller's action finalizes in case there was an error
if (executedContext.Exception == null)
{
_logger.LogInformation("Saving changes for unit of work");
await _dbContext.SaveChangesAsync();
}
else
{
_logger.LogInformation("Avoid to save changes for unit of work due an exception");
}
}
}
and the filter gets plugged into my MVC at Startup.cs when configuring MVC.
public void ConfigureServices(IServiceCollection services)
{
//..
//Entity Framework 7
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<SpeediCargoDbContext>(options => {
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
});
//MVC 6
services.AddMvc(setup =>
{
setup.Filters.AddService(typeof(UnitOfWorkFilter));
});
//..
}
This still leaves a question (see UPDATE 2 on my question). What if I want my controller to respond to an http POST request with a 201 Accepted with a Location header that includes the Id of the entity created in DB? When the controller's action finalises execution the changes have not yet been committed to DB therefore the Id of the entity created is still 0 until the action filter saves changes and the DB generates a value.
I am also facing the same issue and not sure which approach to follow.
One of the approach that I used is as follow:
public class UnitOfWorkFilter : ActionFilterAttribute
{
private readonly AppDbContext _dbContext;
public UnitOfWorkFilter(AppDbContext dbContext,)
{
_dbContext = dbContext;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if (!context.HttpContext.Request.Method.Equals("Post", StringComparison.OrdinalIgnoreCase))
return;
if (context.Exception == null && context.ModelState.IsValid)
{
_dbContext.Database.CommitTransaction();
}
else
{
_dbContext.Database.RollbackTransaction();
}
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.HttpContext.Request.Method.Equals("Post", StringComparison.OrdinalIgnoreCase))
return;
_dbContext.Database.BeginTransaction();
}
}
My advice, use dbContext.SaveChanges() in the controller as it is demonstrated in all examples over the web. What you want to do sounds quite fancy and could backfire as you guessed at the end of your post. And IMO, it doesn't make sense.
Regarding your second question/task:
....when responding to the HTTP request either ALL the changes made to the DbContext will be saved onto the database, or none will be saved (if there was some exception, for example).
I think you need something like 'transaction-per-request'. It is just an idea, haven't tested it at all. I just put the code together in this sample middleware:
public class TransactionPerRequestMiddleware
{
private readonly RequestDelegate next_;
public TransactionPerRequestMiddleware(RequestDelegate next)
{
next_ = next;
}
public async Task Invoke(HttpContext context, DbContext dbContext)
{
var transaction = dbContext.Database.BeginTransaction(
System.Data.IsolationLevel.ReadCommitted);
await next_.Invoke(context);
if (context.Response.StatusCode == 200)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
Good luck

context.SaveChanges not working

My update method is not working in an ASP.NET MVC 3 application. I have used the following EF 4.1 code:
[HttpPost]
public ActionResult UpdateAccountDetails(Account account)
{
if (ModelState.IsValid)
{
service.SaveAccount(account);
}
}
and SaveAccount looks like this:
internal void SaveAccount(Account account) {
context.SaveChanges();
}
internal void SaveAccount(Account account) {
// Load current account from DB
var accountInDb = context.Accounts.Single(a => a.Id == account.Id);
// Update the properties
context.Entry(accountInDb).CurrentValues.SetValues(account);
// Save the changes
context.SaveChanges();
}
Alternative:
internal void SaveAccount(Account account) {
context.Entry(account).State = EntityState.Modified;
context.SaveChanges();
}
The problem here is that you're not accounting for the fact that Web pages are stateless. You probably pupulate your page with the account data returned from the database, but then the object is destroyed at the end of the request.
On postback, a new Acccount object is created by the model binder, but this one is not hooked up to the database, so your database context has no idea that it even exists. So when you call SaveChanges, nothing has changed as far as it is concerned.
You have to either get a new Account object from the database and update it's fields with the data from the model binder created Account, or attach the new account object to the database.
This article should help
http://msdn.microsoft.com/en-us/library/bb896271.aspx
You may need to add context.Accounts.Attach(account); to reattach your entity to the context
You aren't making any changes, so there is really nothing to be saved. The simplest way may be doing the following:
internal void SaveAccount(Account account)
{
context.Attach(account);
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(account);
entry.SetModified();
context.SaveChanges();
}
If you're using an .edmx model in your application, make sure the property StoreGeneratedPattern is set to none for all the fields that have to be updated.
EF doesn't tell you, and acts like it is storing the changes to the database, when in fact it's not.

Using Linq To SQL to update a record from a MVC3 ViewModel

I'm trying to update a record in a database with the values in a ViewModel via Linq To SQL. I had it working but it has since stopped (more on this later).
I have a Customers domain object mapped to a table. I don't need all the fields so I use AutoMapper to map it to a ViewModel (CustomerEditVM) that has a subset of the Customer fields. I do this in my service layer:
public CustomerEditVM GetCustomerEditVMById(int custId)
{
var domainCustomer = _repository.GetCustomerById(custId);
Mapper.CreateMap<Customer, CustomerEditVM>();
CustomerEditVM customer = Mapper.Map<Customer, CustomerEditVM>(domainCustomer);
return customer;
}
I send the CustomerEditVM ViewModel to my view and the user edits the record. In my service layer I map it back to a Customer object and call the Update method in my repository:
public void SaveCustomer(CustomerEditVM customer)
{
Mapper.CreateMap<CustomerEditVM, Customer>();
Customer newCust = Mapper.Map<CustomerEditVM, Customer>(customer);
_repository.Update(newCust);
}
Here is my repository and Update method:
namespace AuctionAdmin.Models.Repositories
{
public interface ICustomerRepository
{
Customer GetCustomerById(int custId);
void Update(Customer customer);
}
public class CustomerRepository : ICustomerRepository
{
private AuctionAdminDataContext _dataContext;
public CustomerRepository()
{
_dataContext = new AuctionAdminDataContext();
}
public Customer GetCustomerById(int custId)
{
var customer = _dataContext.Customers.SingleOrDefault(c => c.CustomerID == custId);
return customer;
}
public void Update(Customer customer)
{
_dataContext.Customers.Attach(customer);
_dataContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, customer);
_dataContext.SubmitChanges();
}
}
}
The Update used to work fine but now fails with this error:
Unable to refresh the specified object. The object no longer exists
in the database.
I'm not sure why this worked so well before and now doesn't but clearly I'm not using Linq to update the database properly. How should I be doing this?
Thanks
So my understanding is that Automapper wasn't really designed to work this way. It flattens objects like you are doing to get your view model but it doesn't really do things the other way. I believe this is by design because Jimmy & Crew are using more of a command pattern with messaging to save things back into the database.
However, I know that doesn't solve your problem. So here are a few things.
With Linq2Sql You need to pull the object out, then update it, then save it. This is
because linq2sql is tracking the changes of the object. However, between requests you no longer have the linq2sql object.
public void SaveCustomer(CustomerEditVM customer)
{
//Get the customer from repo
var domainCustomer = _repository.GetCustomerById(customer.Id);
Mapper.CreateMap<CustomerEditVM, Customer>();
Customer newCust = Mapper.Map<CustomerEditVM, Customer>(domainCustomer);
_repository.Update(newCust);
}
However, that most likely won't work because of the way linq2sql and automapper work. Even if the mapping does work, linq2sql might not show that changes have been made to the object. You are going to be better off mapping this by hand.
Also, there is really no such thing as Update in Linq2Sql.
public void Update(Customer customer)
{
_dataContext.Customers.Attach(customer);
_dataContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, customer);
_dataContext.SubmitChanges();
}
All you need to do is get the object from linq2sql update it and call SubmitChanges(); on the _dataContext. It takes care of it for you. I have seen some repository interfaces include a Update method but it doesn't do anything in a Linq2Sql implementation. Also, its probably not the best idea to call SubmitChanges in the update method as you may want to update may items then submit all the changes at once, which is kind of the purpose of submitchanges (ie unit of work)

enforcing ObjectStateManager entry deletion on page leave

I have a edited RESTful wizard based upon Shoulders of Giants | A RESTful Wizard Using ASP.Net MVC… Perhaps? . This wizard has a CANCEL button which, when pressed, fires the code below.
// If the user cancels, drop out altogether
if (!string.IsNullOrEmpty(CANCEL_BUTTON)) {
Session.Remove(VACANCYWIZARD_SESSION_KEY);
repository._entities.ObjectStateManager.GetObjectStateEntry(inProgressVacancyWizard.Vacancy).Delete();
return this.RedirectToAction("Index", "Home");
}
Now, to be able to call SaveChanges() after the cancel button I have to manually delete the entry from the wizard from my ObjectStateManager. But when you cancel the wizard by just manually returning to the home page it stays in and a next call to _entities.SaveChanges() will throw an exception that it cannot Save the object, from the wizard progress to the database, since it is still in the object state.
Note that in between the steps of the wizard I do not save anything to the database. I keep it in session state retrieving it each step:
NewVacancy inProgressVacancyWizard = Session[VACANCYWIZARD_SESSION_KEY] as NewVacancy;
Somehow, however, the inProgressVacancyWizard.Vacancy does appear in the ObjectStateManager so I have to delete it else I will get errors about incomplete Vacancies models while the _entities.SaveChanges() is called for another object.
Is there a way to cover for this problem?
//edit
After some reading I have found out that the fundaments of my repository are not good. As found here. Currently I'm doubting to implement the option mentioned in "One ObjectContext instance per business transaction" in the same article. Would that be a wise thing? I would like to hear some more about it since it will be a major refactor.
public static Repository Instance
{
get
{
if (instance == null) {
instance = new Repository();
}
return instance;
}
}
#region Constructor: Repository()
/// <summary>
/// Constructor
/// </summary>
private Repository()
{
_entities = new DBModelEntitiesNew2();
}
It seems like you are using a single ObjectContext instance across multiple requests. Don't do that. It will cause you nothing but misery. It makes your web server stateful. Dispose the ObjectContext after the response is rendered (we do it, indirectly, from Controller.Dispose), and new up a new one for the next request.

Using MVC and fluent Nhibernate, how do I validate unique fields on my ViewModel before I bind them to my Domain Object and Save them?

I have a website where I allow users to create new Part records. I'm trying to figure out the best way to validate specific fields for uniqueness. I want to make sure that somebody doesn't try to add a Part with PartNumber 1234 if that PartNumber already exists on a different Part.
The Web Application is using Asp.net MVC with fluent nHibernate for mapping my objects to the database. I'm using Castle validation on my view models for things like ValidateNonEmpty, ValidateRange, etc. Should I use the ValidateSelf method to query the repository to see if that part number already exists? Something doesn't feel right about using my Repository on the ViewModel.
Would it be better for me to place that logic on the controller action? That doesn't seem right because I expect my ViewModel to already be Validated at the point (during ModelBind).
Or maybe its none of the above. Thanks for any help on this one.
UPDATE
Ok, not sure if this will help, but here is what my Save action looks like for a typical Create Action in my project:
public ActionResult Create(PartViewModel viewModel)
{
//I think I'd like to know if its Valid by this point, not on _repository.Save
if(ModelState.IsValid)
{
try
{
var part = _partCreateViewModelMap.MapToEntity(viewModel);
_repository.Save(part);
return Redirect("~/Part/Details/" + part.Id);
}
catch (Exception e)
{
// skip on down...
}
}
// return view to edit
return View(viewModel);
}
I have been asked this question many times. My friends were worried about whether they can perform data access from the validator code. The answer is simple. If you need to do this, you should do it. Usually we need to do such checks at each level of abstraction. And after all checks you should be ready to catch an exception, caused by unique constraint violation.
If you define a unique constraint within the database, then why not delegate the responsibility to check for whether a unique value already exists to the database? Using NHibernate, you can use the NHibernate.Exceptions.ISQLExceptionConverter interface to capture and transform known errors relating to constraint violations. You can also use NHibernate.Exceptions.IViolatedConstraintNameExtracter implementers (see NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter) to get at the grubby details of your database exception, and transform it into a user-friendly message, repackage as a validation exception of your chosing and catch it in the relevant controller.
Example of a quick, very specific quick and dirty exception converter from one of my projects:
Imports NHibernate
Imports NHibernate.Exceptions
Imports System.Data.SqlClient
Imports System.Data.Common
Namespace NHibernate
Public Class ConstraintViolationExceptionConverter
Implements ISQLExceptionConverter
Public Function Convert(ByVal adoExceptionContextInfo As Global.NHibernate.Exceptions.AdoExceptionContextInfo) As System.Exception Implements Global.NHibernate.Exceptions.ISQLExceptionConverter.Convert
Dim dbEx As DbException = ADOExceptionHelper.ExtractDbException(adoExceptionContextInfo.SqlException)
If TypeOf dbEx Is SqlException Then
Dim sqlError As SqlException = DirectCast(dbEx, SqlException)
Select Case sqlError.Number
Case 547
Return New ConstraintViolationException(adoExceptionContextInfo.Message, adoExceptionContextInfo.SqlException)
End Select
End If
Return SQLStateConverter.HandledNonSpecificException(adoExceptionContextInfo.SqlException, adoExceptionContextInfo.Message, adoExceptionContextInfo.Sql)
End Function
End Class
End Namespace
Configured through the web.config/nhibernate-configuration/session-factory property element:
<property name="sql_exception_converter">csl.NHibernate.ConstraintViolationExceptionConverter, csl</property>
Edit: Should probably mention that the converter interface has changed in recent versions of NHibernate, the interface from this example is from NHibernate.dll v2.1.0.4000
I typically put a Service layer between my controllers and repositories.
The service layer would then handle the validation and calls to the repository.
Then, if there's a validation error in the service layer, I throw a custom exception, catch it in the controller, and inject the errors in to the model state.
I have no answer for your question but you can check sharparchitecture.net site. It contains some best practives for asp.net mvc and nhibernate. Also I can recommend you to check xval project and tutorials about Validating with Data Annotation Validators
I have found the solution that works for me is to
1.) Ask if the entity is valid to execute your validation work.
2.) After this is complete you should have something on your object to show it's valid or not (in my case I use a CSLA like concept of "broken rules").
3.) If you have something like this you can verify the object is valid before NHibernate tries to persist it as shown below.
The only issue with this approach is that you do need to implement an interface on each entity requiring validation. If you can live with this it will stop NHibernate from persisting the changes of an object that's not valid according to your rules.
using System;
using NHibernate;
using NHibernate.Event;
using Validation.Entities.Interfaces;
using Persistence.SessionBuilder;
namespace Persistence.Validation
{
public class ValidationEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
public bool OnPreInsert(NHibernate.Event.PreInsertEvent #event)
{
var entityToInsert = #event.Entity as IBusinessBase;
if (entityToInsert != null)
{
if (entityToInsert.BrokenRules != null)
{
RollbackTransactionBecauseTheEntityHasBrokenRules();
}
}
return false;
}
public bool OnPreUpdate(NHibernate.Event.PreUpdateEvent #event)
{
var entityToUpdate = #event.Entity as IBusinessBase;
if (entityToUpdate != null)
{
if (entityToUpdate.BrokenRules != null)
{
RollbackTransactionBecauseTheEntityHasBrokenRules();
}
}
return false;
}
private void RollbackTransactionBecauseTheEntityHasBrokenRules()
{
try
{
ISession session = SessionBuilderFactory.GetBuilder().CurrentSession;
if (session != null)
{
session.Transaction.Rollback();
}
}
catch (Exception ex)
{
//this will force a rollback if we don't have a session bound to the current context
throw new NotImplementedException();
}
}
}
}
I would say this matters on your architecture. With MVC apps that I have done in the past we abstract away the domain stuff away from the web stuff and naturally we use dependency injection to avoid hard dependencies.
When it comes to validating the model when you are in the act of binding it, yes you could easily use the service, repository, or whatever you have next in your architecture in a ValidateSelf method. I think the question rises of what about that dependency.
If I remember correctly you can create your own custom binder that will use your dependency injection framework to plug-in any services your model needs for validation when you create it, call MVC's default binder to fill in the object, then call into Castle Validation's framework to do the validation. This isn't a fully thought solution, but hopefully it provokes some ideas.

Resources