Entity Framework, Ninject dependency injection, and ASP.NET MVC 3, with existing .MDF file as database - asp.net-mvc-3

I'm making a long and detailed post here, but I sincerely believe it's better to provide as much info up front. If any further clarification is needed, please ask. My heartfelt thanks to you for reading my query.
I'm using the book Pro ASP.NET MVC3 Framework by Adam Freeman and Steven Sanderson (Apress, Third Edition), and working through the Sports Store example beginning with Chapter VII. Note that the project entails the use of DI through the use of Ninject, and this is the main reason I wanted to take the time for this walkthrough. Another impressive contribution in this tutorial is the fact that it shows you how to develop an MVC 3 solution into separate projects the way it might be done in in a real world implementation--a thing I've found to be lacking in just about every other book I've ever looked at. (BTW I think the Apress books are just about the best ones out there, generally speaking.)
OK, so I've worked through Chapter 7 and well into Chapter 8, which means that I've successfully created and populated a database and retrieved its contents to appear in my List view. I've also gotten the formatting to work through the .css file. However, there's a catch here: When going through this the first time, I created the database from the IDE itself, which was no problem because there's only one small table (Products) to worry about. Everything was working fine. But then, for reasons I will shortly make clear, I decided to start over. This time, I wanted to follow through the same walkthrough with the exception that I would access the data by attaching to an existing .MDF file rather than creating a new DB from scratch as I coded the project. All this is to pave the way for me to use a similar pattern for my real project, for which I already have built a complex DB, replete with tables, FK relationships, views, functions--and data. I hardly need to say that I want to use my existing database in my "real" project, because recreating all that from scratch would be virtually impossible.
OK, now let's return to the Sports Store example. Try as I might, I cannot get the data from the database to my UI view. There are no error messages at compile time or run time, but instead of seeing the data in the List view, I see nothing but a blank page. Note that in this case it's correct in that there are no column headings or panes because there isn't any scaffolding here.
As far as I can see I have all the parts I need. Let's begin with the Ninject controller factory:
NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override
IController GetControllerInstance
(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null
: (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
//Had the mockup here before, which worked fine.
ninjectKernel.Bind<IProductRepository>().To<SportsStore2.Domain.Concrete.EFProductRepository>();
} ...
}
IProductRepository is as follows:
namespace SportsStore2.Domain.Abstract
{
public interface IProductRepository
{
IQueryable<Product> Products { get; }
}
}
And EFProductRepository is implemented thus:
namespace SportsStore2.Domain.Concrete
{
public class EFProductRepository:IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
}
}
}
Why yes, of course EFDbContext is defined; viz the declaration:
namespace SportsStore2.Domain.Concrete
{
class EFDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
}
And the connection string in Web.config file; this is supposed to wire up the context object definition with the actual database in the .MDF file, at least as I understand it.
<connectionStrings>
<add name="EFDbContext"
connectionString="Data Source=Owner-PC\SQLEXPRESS;Initial Catalog = SportsStore20130205; Integrated security=SSPI"
providerName="System.Data.SqlClient" />
</connectionStrings>
Last and almost certainly least, I have the Product controller defined as follows. I say "least" because I'm almost positive the problem isn't here. In Debug mode, I can tell that the productRepository object isn't being populated. I can see some SQL code in there which is supposed to be run, or have been run by this point. But the invocation of repository.Products returns nothing.
namespace SportsStore2.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository;
public ProductController(IProductRepository productRepository)
{
repository = productRepository;
}
public ViewResult List()
{
return View(repository.Products);
}
public ViewResult Count()
{
int counter = repository.Products.Count();
return View(repository.Products.Count());
}
}
}
namespace SportsStore2.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository;
public ProductController(IProductRepository productRepository)
{
repository = productRepository;
}
public ViewResult List()
{
return View(repository.Products);
}
}
}

Related

Is there a recommended base repository class to use with Entity Framework?

I'm using an EF Code First approach with an ASP.NET MVC 3 application, and instead of re-creating the wheel, I was wondering if there already exists a solid base Repository class that my custom Repository classes could extend in order to provide default functionality out of the box (e.g. basic CRUD, etc...).
So something like this ...
public class CustomerRepository : BaseRepository { ... }
... would therefore provide a default way to work with Customer objects out of the box. I'd like to then inject an ICustomerRepository into my MVC controllers and have the functionality available to me there.
I'm sure something like this already exists out there as I've done something similar with NHibernate.
Thanks
No, there is no built-in repository, other than EF itself (which is in and of itself an implementation of the Unit of Work pattern, and DbSet's are basically Repositories).
There is currently a debate in the software community over whether generic repositories have much real value. For testing purposes, many argue, they provide easy unit testing. Others say that unit testing repositories doesn't help because mocked repositories don't behave the same way that real ones do (because of the linq -> Sql translation layer, which doesn't exist in a mocked repository).
Many are suggesting that you do integration testing against EF using an in-memory database like SqlLite rather than unit testing it.
Still, if you are intent on using repositories, there are many examples out there on the net, with varying styles and methods. Or you can roll your own. MS does not provide one.
In my experience, write your own repositories is redundant because EF implements this pattern already through DbSet's.
I worked with MVC3 + EF Code Fisrt in a recent project. We started implementing a generic repository following some tutorials and soon we realized that we are writing a lot of unnecessary and redundant code. Actually, the repositories were given us nothing but hiding a lot of the DbSet's functionality. Finally, we decided to remove them and work with our DbContext and DbSet's directly.
But, how about complex business logic beyond simple CRUD operations?
Well, we exposed all complex functionality like queries and multiple CRUD operations through a service layer. You can build different service classes by functionality. By example, you can write an AccountService to manage all functionality related with user accounts. Something like this:
public class AccountService {
private MyContext ctx;
public AccountService(DbContext dbContext) {
this.ctx = (MyContext)dbContext;
}
/// <summary>
/// Gets the underlying DbContext object.
/// </summary>
public DbContext DbContext {
get { return ctx; }
}
/// <summary>
/// Gets the users repository.
/// </summary>
public DbSet<User> Users {
get {return ctx.Users;}
}
public bool ValidateLogin(string username, string password) {
return ctx.Users.Any(u => u.Username == username && u.Password == password);
}
public string[] GetRolesForUser(string username) {
var qry = from u in ctx.Users
from r in u.Roles
where u.Username == username
select r.Code;
return qry.ToArray<String>();
}
public User CreateUser(string username, string password) {
if (String.IsNullOrWhiteSpace(username)) throw new ArgumentException("Invalid user name");
if (String.IsNullOrWhiteSpace(password)) throw new ArgumentException("Invalid password");
User u = new User {
Username = username.Trim().ToLower(),
Password = password.Trim().ToLower(),
Roles = new List<Role>()
};
ctx.Users.Add(u);
ctx.SaveChanges();
return u;
}
How about dependency injection?
Using this approach, the only thing we need to inject is the DbContext. The service classes has a constructor that takes a DbContext. So, when your controller constructor takes a service instance the DbContext will be injected to it.
Edit: Example code
This is an example code about how you controller could look:
public class HomeController : Controller {
private readonly AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
}
And this could be the DI configuration using NInject:
private static void RegisterServices(IKernel kernel) {
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
}
How about unit testing?
You could build specific interfaces for each service layer class and mock it where you need.
A friend of mine, Sacha Barber wrote a nice article covering some of these ideas.
Link can be found here.
RESTful WCF / EF POCO / Unit of Work / Repository / MEF: 1 of 2
EF has a base class called DbContext. You can add properties of type DbSet<TEntity>
This allows you to do something like this:
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseContext : DbContext {
public DbSet<User> Users { get; set; }
}
You can now use this like so:
using(var db = new DatabaseContext()) {
User jon = new User {Name = "Jon Smith"};
db.Users.Add(jon);
db.SaveChanges();
var jonById = db.Users.Single(x => x.Id == 1);
}
If you want more abstraction see this post about building a generic repository around EF Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit Testable Just to note, this level of abstraction is not always needed. You should decide if your abblication will truly benefit from adding a generic repository over just using DbContext directly.

What is the best way to create EF DbContext instance for ASP.NET MVC

In order to support lazy loading feature in EF, what is the best way to instantiate DbContext?
I know HttpContext's current item is good place to create DbContext via Application_BeginRequest method and Application_EndRequest method, but in some sample codes of MSDN and official asp.net mvc site, they just create DbContext in Controller's constructor and dispose it in controller's Dispose() method.
I think the both ways are not too different because all of those all implement session per request pattern.
I just want to make sure that my understanding is correct or not.
The Dispose() method in the controller isn't always reliable. By the same token, Session is probably not a good idea either. "Best" is probably subjective, but we've had the best success by using dependency injection (Castle Windsor) and following a Unit of Work Repository pattern.
Setup the unit of work along the following lines:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork()
{
this.Context = new MyEFEntities();
this.Context.ContextOptions.LazyLoadingEnabled = true;
}
public void Dispose()
{
this.Context.Dispose();
}
public ObjectContext Context { get; internal set; }
}
Setup your repository:
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public Repository(IUnitOfWork unitOfWork)
{
Context = unitOfWork.Context;
ObjectSet = Context.CreateObjectSet<TEntity>();
}
public ObjectContext Context { get; set; }
public IObjectSet<TEntity> ObjectSet { get; set; }
}
Register with Castle in Global.asax:
void Application_Start()
{
this.Container.Register(
Component.For<IUnitOfWork>()
.UsingFactoryMethod(() => new UnitOfWork())
.LifeStyle
.Is(LifestyleType.PerWebRequest)
);
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(this.Container));
}
And use in your controller (or wherever you're using it, as long as it's injectable):
public class SomeController
{
public SomeController(IRepository<MyEntity> repository)
{
this.Repository = repository;
}
public IRepository<MyEntity> Repository { get; set; }
public ActionResult MyAction()
{
ViewData.Model = this.Repository.ObjectSet.Single(x => x.Condition); //or something...
}
}
Any lazy loading here could potentially be a trap for a future issue. Without DI, without a repository - its hard to see anything working without it being a hack for lazy loading. Also do you you plan on passing your entities to your view. If so this is going to create a bad overlap. The controller should package data for your view, not have things evaluated later in your view.
For MVC best practices, you should flatten out your domain model as much as possible into a viewmodel (if flattening makes sense) and use the view model. Since you would ideally then know what would be lazy loaded, it may make more sense to take the hit up front and use .Include() in your query to eager load, otherwise you can issue many many queries to the database.
I've used a session factory pattern and saved the DBContext in the session object. It will stay open per session. I haven't had problems with it so far.

EF and repository pattern - ending up with multiple DbContexts in one controller - any issues (performance, data integrity)?

Most of my knowledge of ASP.NET MVC 3 comes from reading through the book Pro ASP.NET MVC 3 Framework by Adam Freeman and Steven Senderson. For my test application I have tried to stick to their examples very closely. I am using the repository pattern plus Ninject and Moq which means that unit testing work quite well (i.e. without needing to pull data from the database).
In the book repositories are used like this:
public class EFDbTestChildRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<TestChild> TestChildren
{
get { return context.TestChildren; }
}
public void SaveTestChild(TestChild testChild)
{
if (testChild.TestChildID == 0)
{
context.TestChildren.Add(testChild);
}
else
{
context.Entry(testChild).State = EntityState.Modified;
}
context.SaveChanges();
}
}
And here is the DbContext that goes with it:
public class EFDbContext : DbContext
{
public DbSet<TestParent> TestParents { get; set; }
public DbSet<TestChild> TestChildren { get; set; }
}
Please note: to keep things simple in this extracted example I have left out the interface ITestChildRepository here which Ninject would then use.
In other sources I have seen a more general approach for the repository where one single repository is enough for the whole application. Obviously in my case I end up with quite a list of repositories in my application - basically one for each entity in my domain model. Not sure about the pros and cons about the two approaches - I just followed the book to be on the safe side.
To finally get to my question: each repository has its own DbContext - private EFDbContext context = new EFDbContext();. Do I risk ending up with multiple DbContexts within one request? And would that lead to any significant performance overhead? How about a potential for conflicts between the contexts and any consequences to the data integrity?
Here is an example where I ended up with more than one repository within a controller.
My two database tables are linked with a foreign key relationship. My domain model classes:
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual ICollection<TestChild> TestChildren { get; set; }
}
public class TestChild
{
public int TestChildID { get; set; }
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual TestParent TestParent { get; set; }
}
The web application contains a page that allows the user to create a new TestChild. On it there is a selectbox that contains a list of available TestParents to pick from. This is what my controller looks like:
public class ChildController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult List()
{
return View(testChildRepository.TestChildren);
}
public ViewResult Edit(int testChildID)
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = testChildRepository.TestChildren.First(tc => tc.TestChildID == testChildID);
cvm.TestParents = testParentRepository.TestParents;
return View(cvm);
}
public ViewResult Create()
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = new TestChild();
cvm.TestParents = testParentRepository.TestParents;
return View("Edit", cvm);
}
[HttpPost]
public ActionResult Edit(TestChild testChild)
{
try
{
if (ModelState.IsValid)
{
testChildRepository.SaveTestChild(testChild);
TempData["message"] = string.Format("Changes to test child have been saved: {0} (ID = {1})",
testChild.Name,
testChild.TestChildID);
return RedirectToAction("List");
}
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
// something wrong with the data values
return View(testChild);
}
}
It's not enough to have an EFDbTestChildRepository available but I also need an EFDbTestParentRepository. Both of them are assigned to private variables of the controller - and voila, it seems to me that two DbContexts have been created. Or is that not correct?
To avoid the issue I tried using EFDbTestChildRepository to get to the TestParents. But that obviously will only bring up those that are already hooked up to at least one TestChild - so not what I want.
Here is the code for the view model:
public class ChildViewModel
{
public TestChild TestChild { get; set; }
public IQueryable<TestParent> TestParents { get; set; }
}
Please let me know if I forgot to include some relevant code. Thanks so much for your advice!
There won't be a performance problem (unless we are talking about nanoseconds, instantiating a context is very cheap) and you won't have damaged your data integrity (before that happens you'll get exceptions).
But the approach is very limited and will work only in very simple situations. Multiple contexts will lead to problems in many scenarios. As an example: Suppose you want to create a new child for an existing parent and would try it with the following code:
var parent = parentRepo.TestParents.Single(p => p.Id == 1);
var child = new Child { TestParent = parent };
childrenRepo.SaveTestChild(child);
This simple code won't work because parent is already attached to the context inside of parentRepo but childrenRepo.SaveTestChild will try to attach it to the context inside of childrenRepo which will cause an exception because an entity must not be attached to another context. (Here is actually a workaround because you could set the FK property instead of loading the parent: child.TestParentID = 1. But without a FK property it would be a problem.)
How to solve such a problem?
One approach could be to extend the EFDbTestChildRepository by a new property:
public IQueryable<TestParent> TestParents
{
get { return context.TestParents; }
}
In the example code above you could then use only one repository and the code would work. But as you can see, the name "EFDbTest Child Repository" doesn't really fit anymore to the purpose of the new repository. It should be now "EFDbTest ParentAndChild Repository".
I would call this the Aggregate Root approach which means that you create one repository not for only one entity but for a few entities which are closely related to each other and have navigation properties between them.
An alternative solution is to inject the context into the repositories (instead of creating it in the repositories) to make sure that every repository uses the same context. (The context is often abstracted into a IUnitOfWork interface.) Example:
public class MyController : Controller
{
private readonly MyContext _context;
public MyController()
{
_context = new MyContext();
}
public ActionResult SomeAction(...)
{
var parentRepo = new EFDbTestParentRepository(_context);
var childRepo = new EFDbTestChildRepository(_context);
//...
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
base.Dispose(disposing);
}
}
This gives you a single context per controller you can use in multiple repositories.
The next step might be to create a single context per request by dependency injection, like...
private readonly MyContext _context;
public MyController(MyContext context)
{
_context = context;
}
...and then configuring the IOC container to create a single context instance which gets injected into perhaps multiple controllers.
Do I risk ending up with multiple DbContexts within one request?
Yes. Each instance of a repository is going to instantiate its own DbContexts instances. Depending on the size and use of the application, this may not be a problem although it is not a very scalable approach. There are several ways of handling this though. In my web projects I add the DbContext(s) to the Request's Context.Item collection, this way it is available to all classes that require it. I use Autofac (similar to Ninject) to control what DbContexts are created within specific scenarios and how they are stored, e.g. I have a different 'session manager' for a WCF context to the one for a Http context.
And would that lead to any significant performance overhead?
Yes, but again not massively if the application is relatively small. As it grows though, you may notice the overhead.
How about a potential for conflicts between the contexts and any
consequences to the data integrity?
One of the reasons for using an ORM like this is so that changes can be maintained within the DbContext. If you are instantiating multiple context instances per request you lose this benefit. You wouldn't notice conflicts or any impact of the integrity per se unless you were handling a lot of updates asynchronously.
As promised I post my solution.
I came across your question because I was having trouble with the IIS application pool memory growing beyond limits and having multiple DBContexts was one of my suspects. In retrospect it is fair to say that there were other causes for my trouble. However, it challenged me to find a better layer based design for my repository.
I found this excellent blog: Correct use of Repository and Unit Of Work patterns in ASP.NET MVC leading me to the right direction. The redesign is based on the UnitOfWork pattern. It enables me to have just one constructor parameter for all my controllers instead of "never ending constructor parameters". And after that, I was able to introduce proactive caching as well, which solved a great deal of the earlier mentioned trouble I was having.
Now I only have these classes:
IUnitOfWork
EFUnitOfWork
IGenericRepository
EFGenericRepository
See the referred blog for complete information and implementation of these classes. Just to give an example, IUnitOfWork contains repository definitions for all entities that I need, like:
namespace MyWebApp.Domain.Abstract
{
public interface IUnitOfWork : IDisposable
{
IGenericRepository<AAAAA> AAAAARepository { get; }
IGenericRepository<BBBBB> BBBBBRepository { get; }
IGenericRepository<CCCCC> CCCCCRepository { get; }
IGenericRepository<DDDDD> DDDDDRepository { get; }
// etc.
string Commit();
}
}
The Dependency Injection (DI) is just one statement (I use Ninject):
ninjectKernel.Bind<IUnitOfWork>().To<EFUnitOfWork>();
The Controllers-constructors are maintainable:
public class MyController : BaseController
{
private MyModel mdl = new MyModel();
private IUnitOfWork _context;
public MyController(IUnitOfWork unitOfWork)
{
_context = unitOfWork;
// intialize whatever needs to be exposed to the View:
mdl.whatever = unitOfWork.SomeRepository.AsQueryable();
}
// etc.
Within the Controller I can use _context to access all repositories, if needed. The nice part of it, is that it needs just a single Commit()-call to save changed data for all repositories:
_context.Commit();

"An object with the same key already exists in the ObjectStateManager..." exception is thrown when setting an entity state to modified

I followed some examples(including such books as "Pro ASP.NET MVC 3" and "Professional ASP.NET MVC 3") to create simple ASP.NET MVC 3 apps using EF 4.1 (since I'm new to these technologies).
I'm using the following repository(single instance of it is used by all action methods of the controller) to access the DB:
public class ProductRepository : IProductRepository
{
private readonly EFDbContext _context = new EFDbContext();
#region Implementation of IProductRepository
....
public void SaveProduct(Product product)
{
if (product.ProductId == 0)
{
_context.Products.Add(product);
}
else
{
_context.Entry(product).State = EntityState.Modified;
}
_context.SaveChanges();
}
....
}
This repository performs updating as it was shown in the examples I used.
Product class:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
In case of updating the product, I'm getting the exception "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
I know that the similar questions have been already discussed here but my question is a bit different:
Why this code which was taken from examples is not working (though it looks pretty simple and straightforward)? What wrong might I have done or missed something.
After searching for hours for a solution, I have found one that seems suitable after doing enough reading.
The fix is here:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
Basically, fetch the record from the Context and call:
var currentProduct = _context.Products.Find(product.ProductId);
_context.Entry(currentProduct).CurrentValues.SetValues(product);
This seems like a bad idea and something I've always hated about EF in my previous workings, but cccording to Ladislav Mrnka (who apparnently answers every EF related question on Stackoverflow) in this post:
Entity Framework and Connection Pooling
EF will store a request for an entity internally, so ideally, it will already be there and it won't be making an additional call back to the database.
The root cause of the problem seems to be that once a product is fetched from the Context, the context is keeping track of it and that's what is causing all the trouble. So merging your changes back in is the only way.
Hope that helps.
It looks like you're not updating product.ProductId when the item is saved for the first time. This means that when you come back to save the item again it's adding it to the context again, hence the error.
As the Id will be added by database (I'm assuming it's the autogenerated Id) then you'll need to read your product data back onto the client.
From a Generics standpoint, here's how I have resolve the same problem very recently:
public TEntity Update(TEntity model, bool persist)
{
if (model == null)
{
throw new ArgumentException("Cannot update a null entity.");
}
var updateModel = Get(model.Id);
if (updateModel == null)
{
return model;
}
this.context.Entry<TEntity>(updateModel).CurrentValues.SetValues(model);
this.Save(persist);
return model;
}

nHibernate [TransactionAttribute] for UoW conflicts with Repository Pattern

Doing research into the best way to design IRepository<T> structures, I came across a project called 'Whiteboard' (http://whiteboardchat.codeplex.com/) while looking through some forums for NHProf.
I dug around its source code for a while, and found a really interesting attribute for MVC called TransactionAttribute, defined as follows; (I have made brief adjustment to suit my IoC solution)
using System;
using System.Linq;
using Ninject;
namespace System.Web.Mvc
{
/// <summary>
/// This will allow ASP.NET MVC to apply Transactions to the controllers.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class TransactionAttribute : ActionFilterAttribute
{
[Inject]
public NHibernate.ISession Session
{
get;
set;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Session.BeginTransaction();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (Session.Transaction.IsActive)
{
if (filterContext.Exception == null)
{
Session.Flush();
Session.Transaction.Commit();
}
else
{
Session.Transaction.Rollback();
}
}
}
}
}
This is really interesting; And useful, however something about it bothers me. When I run my queries using NHProf, it gives me warnings about 'Not using transactions properly', and suggests I wrap all queries in a Transaction. Alright, that's fine and good...
So then I go and decorate my Repository<T> : IRepository<T> class like this ...
public T Update(T instance)
{
using (var transaction = session.BeginTransaction())
{
// attempt to perform the given update
session.SaveOrUpdate(instance);
try
{
// commit the transaction to the database
transaction.Commit();
// update succeeded, so we'll return true
return instance;
}
catch
{
// restore the database to its previous state if we failed.
transaction.Rollback();
// update failed, so return a null object
return default(T);
}
}
}
Here's the problem I am running into.
Everywhere I read, the common practice is to always use a Repository for adding to the collections. However the TransactionAttribute, which in itself was brought to my attention by Ayende Rahien's blog, who is from what I can gather one of the primary developers of NHProf, and one of the people working on this Whiteboard project, makes the assumption that you are performing Repository commands at the MVC Controller Level.
So which is it? I'm utterly confused now where my Transaction logic is supposed to go for the best practice. I'm literally finding conflicting answers, and in some cases from the same people.
You are not supposed to deal with transactions inside repositories. A controller (like you have) or HTTP module should start and commit/rollback transactions. Saves or updates are not supposed to be done in isolation. They will be committed at the end of the operation by the controller. This way you can take advantage of ADO batching and other NHibernate features.
Also, make sure to set the FlushMode of the nhibernate ISession to Commit.
Are you decorating your action method or controller class with the [Transaction] attribute? If not, this action filter code won't even be called.
Also, you will need to ensure that you [Inject] the session object into your repository as well and that the session object is scoped to the request.
as an example:
public class MyRepository
{
[Inject]
public ISession Session { get; set; }
public void Save(MyModel model) { Session.Save(model); }
}
public class MyController : Controller
{
[Inject]
public MyRepository MyRepository { get; set; }
[Transaction]
public ActionResult Save(MyModel model)
{
MyRepository.Save(model);
}
}
and when registering your session;
var configuration = new NHibernateConfiguration();
Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory());
Bind<ISession>().ToMethod(x => x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
notice the InRequestScope() part
Posting this for #Fatal.
The original response answered my question, but this is inevitably what I ended up doing to avoid using method level attributes.
Instead of declaring the code to control my transaction in an attribute, I included it right in my ISession management for Ninject.
Bind<ISession>()
.ToMethod(c => OpenSession())
.InRequestScope()
.OnActivation(session =>
{
session.BeginTransaction();
session.FlushMode = FlushMode.Commit;
})
.OnDeactivation(session =>
{
if (session.Transaction.IsActive)
{
try
{
session.Transaction.Commit();
}
catch
{
session.Transaction.Rollback();
}
}
});
What this does is open the new ISession each request where an ISession is injected, and when it is activated it begins a new transaction. At this point, Ninject now tracks the state and handles the consequences of rolling it back, thus implementing a very simplistic unit of work pattern.
I do not know if this is the best approach in the world, but I have shown it to a few people and it has not been shot down for bad practice, and it has worked well for me so far.

Resources