12
\$\begingroup\$

I have a repository class that impliments a repository Interface. In my Controllers I want a way to access the repository without creating constructors each time in the controller. So now all my Controllers have to do is inherit from Base. Any pitfalls to this, or how I can write it differently.

public class BaseController : Controller
    {
        public IRepository Repo {get;set;}
        public IStudentRepositroy StudentRepo {get;set;};
      public BaseController() : this(new Repository(),new StudentRepository())
      {

      }
      public BaseController(IRepository repo,IStudentRepository studentRepo)
      {
          Repo = repo;
          StudentRepo = studentRepo;
      }

    }
\$\endgroup\$
8
\$\begingroup\$

This sounds exactly like a desire for Dependency Injection (using an IoC container). ASP.NET MVC is perfectly setup for this already with plenty of containers out there for you to use!

You can still have a base constructor take arguments if you wish but I would try and limit how many.

So your controllers would look like:

public StudentController(IRepository repo,IStudentRepository studentRepo)
        : base(repo)
{
      StudentRepo = studentRepo;
}

So by using the IoC framework these will be automatically resolved to your desired class. By doing this, it opens up the opportunity to more easily inject mock classes into your controllers and further separates them conceptually from implementation detail of the repositories. Although I typically do not Unit test my controllers (as I try to keep them as skinny as possible), it would also make unit testing easier.

Typically you might do this in the Global.ascx or in the App_Start folder. You can get most DI frameworks by using Nuget packages (which I would recommend). There are many out there but here are a couple of examples from ones I have dealt with in the past.

Autofaq

public class AutofaqConfig
{
    public static void Configure()
    {
        var builder = new ContainerBuilder();
        builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();

        RegisterRepositories(builder);

        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }

    private static void RegisterRepositories(ContainerBuilder builder)
    {       
        builder.RegisterType<StudentRepository>().As<IStudentRepository>();
        // And any other repositories here
    }
}

And in your global ascx

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();            
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        AutofaqConfig.Configure();
    }
}

Ninject (Using the MVC3 ninject module)

public static class NinjectWebCommon 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        // Install our Ninject-based IDependencyResolver into the Web API config
        GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<StudentRepository>().To<IStudentRepository>();
    }        
}

Using Unity (Using Unit.MVC4)

public class UnityConfig
{
    #region Unity Container
    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });

    /// <summary>
    /// Gets the configured Unity container.
    /// </summary>
    public static IUnityContainer GetConfiguredContainer()
    {
        return container.Value;
    }
    #endregion

    /// <summary>Registers the type mappings with the Unity container.</summary>
    /// <param name="container">The unity container to configure.</param>
    /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
    /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
    public static void RegisterTypes(IUnityContainer container)
    {
        // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
        // container.LoadConfiguration();

        // Repositories
        container.RegisterType<IStudentRepository, StudentRepository>();
    }
}
\$\endgroup\$
  • \$\begingroup\$ The thing to remember is any extra param in your base class needs to be mirrored in all your controllers constructors. We've done this in the past, where we have had a basecontroller with say 20 controllers , and then we add a parm on the base constructor. now you need to update all 20 controllers and chain it through. To stop this we created a wrapper class, called "ControllerPayload" and only pass that through. Anything we need to add, like your repo or whatever, we add as properties on this object. Now you can maintain it a lot easier, as you only need to updated the object. \$\endgroup\$ – spaceman Jan 27 '14 at 9:18
  • \$\begingroup\$ Nice idea spaceman. One reason why I like keeping parameters in controllers however is it allows me to help identify perhaps design issues. If I find my controller requiring more than 3-4 parameters I start to ask myself what's going on and is there a better way. Your idea is sound though as well. \$\endgroup\$ – dreza Jan 27 '14 at 19:39
  • \$\begingroup\$ I'm trying to figure this out myself. This article recommends using dependency injection and also adding the repository item (DBContext, in this case) as a constructor to the controller. I have a base controller that accepts this and saves it in an instance variable. However, now I must add this same constructor to every controller that derives from the base controller. I wish there was a way that I didn't need to do that for the reasons given above. \$\endgroup\$ – Jonathan Wood Dec 1 '14 at 0:35
  • \$\begingroup\$ @JonathanWood Most DI frameworks accept property injection as far as I'm aware, however from reading it's better practice to have constructor injection to be more explicit about the controllers required dependencies. If a dbContext is not needed in every controller is not inheriting it an option? \$\endgroup\$ – dreza Dec 1 '14 at 7:01
0
\$\begingroup\$

Create a service locator concept, so the object creation part will be handled by it. There is no need to always create objects. Use unit of works logic in the web config file. Here all the mappings (iClass int = new Class()), so everything will be on track.

\$\endgroup\$
  • 4
    \$\begingroup\$ I don't have enough knowledge in C#, but your answer seems to me like it would be better with more explanation. \$\endgroup\$ – Marc-Andre Apr 10 '14 at 13:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.