Hi, I must admit that I'm new to ASP.Net MVC and I'm currently researching all the best practices on how to start my new project. So far I have understood the concepts of the Repository Pattern and Unit of Work and I have gone onto Dependency Injection and Inversion of Control (IoC). I have been looking into this for the last 2 days and I have concluded there are 2 IoC containers I like and they are StructureMap and NInject, although neither are perfect and I haven't managed to get StructureMap to work yet although I like the light weight syntax.
Here's how my application is constructed. First I have the following interfaces for my data context and repository:
public interface IDataContext : IDisposable
{
IRepository<T> Repository<T>() where T : class;
void Commit();
}
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
IEnumerable<T> Find(Expression<Func<T, bool>> where);
T Single(Expression<Func<T, bool>> where);
void Insert(T entity);
void Delete(T entity);
}
I then have a LinqToSql implementation of this like so:
public class LinqToSqlDataContext : IDataContext
{
private readonly DataContext _context;
public LinqToSqlDataContext(DataContext context)
{
_context = context;
}
public IRepository<T> Repository<T>() where T : class
{
return new LinqToSqlRepository<T>(_context);
}
public void Commit()
{
_context.SubmitChanges();
}
public void Dispose()
{
}
}
public class LinqToSqlRepository<T> : IRepository<T> where T : class
{
private readonly DataContext _context;
public LinqToSqlRepository(DataContext context)
{
_context = context;
}
public IEnumerable<T> GetAll()
{
return _context.GetTable<T>();
}
public IEnumerable<T> Find(Expression<Func<T, bool>> where)
{
return _context.GetTable<T>().Where(where);
}
public T Single(Expression<Func<T, bool>> where)
{
return _context.GetTable<T>().SingleOrDefault(where);
}
public void Insert(T entity)
{
_context.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
_context.GetTable<T>().DeleteOnSubmit(entity);
}
}
Currently I have found 2 places where I need to use my data context.
- Within the controller constructor
- In a data annotation attribute (which cannot have a constructor)
I have tried to remove any dependencies to the best of my abilities. Please say if you have any recommendations.
Now onto my IoC container implementations. First NInject I managed to modify my Global.asax.cs file to the following:
public class MvcApplication : NinjectHttpApplication
{
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel(new ServiceModule());
// Gives my wrapper class access to the kernel instance
IoCContainer.Initialize(kernel);
return kernel;
}
public static void RegisterRoutes(RouteCollection routes)
{
...
}
}
internal class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IDataContext>().To<LinqToSqlDataContext>().InRequestScope();
Bind<DataContext>().To<MyDataContext>().InRequestScope();
}
}
public static class IoCContainer
{
private static IKernel _kernel;
public static void Initialize(IKernel kernel)
{
_kernel = kernel;
}
public static T Get<T>()
{
return _kernel.Get<T>();
}
public static object Get(Type type)
{
return _kernel.Get(type);
}
}
This works nicely. The controller constructors now automatically have their dependencies wired up and within my data annotation attribute I can say:
var context = IoCContainer.Get<IDataContext>();
I like NInject but even with the Global.asax.cs file inheriting from NinjectHttpApplication (which handles alot of the plumbing) I still feel there's quite alot going on I'd like to remove.
Next I looked at StructureMap. StructureMap doesn't come with it's own built in ControllerFactory but it's pretty simple to generate one. I have placed it within my Global.asax.cs file temporarily while I'm testing. Here's the final contents of the file:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
// Configure structure map
ObjectFactory.Initialize(x =>
{
x.For<IDataContext>()
.HttpContextScoped()
.Use<LinqToSqlDataContext>();
x.For<DataContext>()
.HttpContextScoped()
.Use<MyDataContext>();
});
}
protected void Application_EndRequest()
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
public static void RegisterRoutes(RouteCollection routes)
{
...
}
}
public class StructureMapControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
var controllerType = base.GetControllerType(requestContext, controllerName);
return ObjectFactory.GetInstance(controllerType) as IController;
}
catch (Exception e)
{
return base.CreateController(requestContext, controllerName);
}
}
}
I should also be able to get an instance to the data context within my data annotation attribute by saying:
var context = ObjectFactory.GetInstance<IDataContext>();
This definitely feels alot lighter in syntax to me. However when I run my application it does not work.
I was wondering the following:
- Whether my pattern is correct.
- Which IoC Container is the best and easiest to use. If NInject then am I doing this correctly and if StructureMap then how could I fix the error I am receiving.
I know it's alot to take in but I'd really appreciate it if someone could help. Thanks.