IoC Container Hurdle for ASP.Net MVC Newb
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.
Here's a good video on setting up ASP.NET MVC with SM from the MVC Storefront series.
On my project I'm using Autofac and have all of my container logic in a Bootsrapper.cs file. Then in my global.asax.cs it's simply one call to setup IoC and a property to get access to the container
private static IContainerProvider _containerProvider;
public IContainerProvider ContainerProvider
{
get { return _containerProvider; }
}
protected void Application_Start ()
{
// snip..
_containerProvider = Bootstrapper.ConfigureAutofac ();
// snip..
}
I don't plan on moving away from LinqToSQL or Autofac so I didn't bother abstracting either of those components away.
Take this with a grain of salt, but IoC might be a bit overkill at first if you are just getting used to MVC. If I were just starting I don't think I'd worry about any of that at first. Even the NerdDinner app which is the standard starter tutorial doesn't use IoC. Heck, it does't even use View classes it justs accesses the context right in the view.
I don't see any reference to an error in your post but if it was me I'd just skip IoC for now and just concentrate on learning MVC.
My first scan says you are comparing how easy Service Location is in SM vs NI. This is the wrong question - Ninject intentionally doesnt surface this in a manner - it's trying to make you fall into the Pit Of Success (and SM should too - people don't read docs unti its too late unfortunately). Go look up Service Location vs Dependency Injection using a search engine.
I (as opposed to the other response) think DI is important enough that you should be trying to understand it so you can sensibly choose whether its important to you, even if it isnt blatantly clear that it is necessary in small samples.
I personally am looking forward to reading and being able to recommend (I have no doubt it's going to be an important book that will be extremely well written) Dependency Injection in Action (its not on paper yet). In the meantime, I recommend reading all the Ninject posts and all Mark Seemann's posts around here.
精彩评论