How to inject my dbContext with Unity

11,070

Solution 1

I'm usually solving this with a DbContextFactory. This would allow you to create the context when needed and also dispose it when you're done.

public interface IDbContextFactory
{
    IdentityDbContext<User> GetContext();
}

public class DbContextFactory : IDbContextFactory
{
    private readonly IdentityDbContext<User> _context;

    public DbContextFactory()
    {
        _context = new RequestContext("ConnectionStringName");
    }

    public IdentityDbContext<User> GetContext()
    {
        return _context;
    }
}

This factory can easily be injected. You can see a more complete example here: Repository Pattern universal application

With the factory you will also have the option to create the DbContext in constructor or in the method. When using Unity I recommend you to do as little as possible in the constructor, as Unity will resolve the entire chain for you. This means that the DbContext will be created every time the repository is resolved. This would require the class that injects the repository also needs to dispose the repository (which in turn should dispose the DbContext), and what happens when two classes are using the same repository instance? This can obviously be solved with lifetimemanagers and good programming practices, but I find it more elegant to simply open and close the context when needed.

Example for usage in a method:

using (var context = _dbContextFactory.GenerateContext())
{
    return context.Requests.FirstOrDefault(x => x.Id == foo);
}

And a more complete example for your repository:

public class RequestRepository
{
    private IDbContextFactory _contextFactory;

    public RequestRepository(IDbContextFactory contextFactory)
    {
        // DbContext will not be created in constructor, and therefore your repository doesn't have to implement IDisposable.
        _contextFactory= contextFactory;
    }

    public Request FindById(int id)
    {
         // Context will be properly disposed thanks to using.
        using (var context = _dbContextFactory.GenerateContext())
        {
            return context.Requests.FirstOrDefault(x => x.Id == id);
        }
    }
}

And when you're creating your interface for your context I can also recommend you to change DbSet<T> to IDbSet<T> to allow easier unit testing. Example of interface for DbContext.

public interface IDbContext : IDisposable, IObjectContextAdapter
{
        IDbSet<Request> Requests { get; set; }
        IDbSet<Record> Records { get; set; }
        int SaveChanges();

        DbSet Set(Type entityType);
        DbSet<TEntity> Set<TEntity>() where TEntity : class;
}

If your're looking to inject the DbContext in the constructor you could also take a look at the Unit of Work-pattern, which wraps the DbContext and allows several classes to use the same context over a specific lifetime (eg a request). One may argue that EF already implements the Unit of Work-pattern, but I leave that discussion for another time. Here's a couple of examples:

http://www.codeproject.com/Articles/741207/Repository-with-Unit-of-Work-IoC-and-Unit-Test

Onion Architecture, Unit of Work and a generic Repository pattern

Solution 2

This site has a GREAT tutorial on how to get Unity working: https://medium.com/aeturnuminc/repository-pattern-with-dependency-injection-mvc-ef-code-first-91344413ba1c

I'm going to assume you have Entity Framework installed, know how to create a viewmodel and put on attributes using System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema namespaces, and I'll assume you can create a view and controller. None of that is really relevant until the end, anyway.

You have to get the NuGet package Unity to install these references:

  • Microsoft.Practices.Unity
  • Unity.Mvc3 (or 4 or 5)

My DataContext (Model1.cs) looks like this:

public partial class Model1 : DbContext
{
    public Model1()
        : this(true)
    { }

    public Model1(bool enableLazyLoading = true)
        : base("name=Model1")
    {
        // You can do this....
        //Database.SetInitializer<Model1>(new CreateDatabaseIfNotExists<Model1>());            
        //this.Configuration.LazyLoadingEnabled = false;

        // or this...
        Database.SetInitializer<Model1>(null);
        this.Configuration.ProxyCreationEnabled = false;

        ((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = enableLazyLoading;
        ((IObjectContextAdapter)this).ObjectContext.ContextOptions.LazyLoadingEnabled = enableLazyLoading;
    }

    // All my tables and views are assigned to models, here...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        base.OnModelCreating(modelBuilder);
    }
}

My Repository (DataRepository.cs) looks like this:

namespace WeeklyReport.Repository
{
    public class DataRepository : IDataRepository
    {
        private bool disposing;
        private readonly Model1 context;

        public virtual void Dispose()
        {
            if (disposing)
            {
                return;
            }

            disposing = true;

            if (context != null)
            {
                context.Dispose();
            }
        }

        public void SaveChanges()
        {
            context.SaveChanges();
        }

        public DataRepository()
        {
            context = new Model1();
            context.Configuration.ProxyCreationEnabled = false;
        }

        public IEnumerable<ReportViewModel> GetAllMetrics()
        {
            var myMetrics = context.MetricsTable; // put into ReportVM and return, etc.
        }
        // etc., etc.
    }
}

My Interface (IDataRepository.cs) looks like this:

namespace WeeklyReport.Repository
{
    public interface IDataRepository
    {
        void SaveChanges();
        IEnumerable<ReportViewModel> GetAllMetrics();
    }
}

My UnityConfig.cs in the App_Start folder looks like this:

using Microsoft.Practices.Unity;
using WeeklyReport.Repository;
using System.Web.Mvc;
using Unity.Mvc3;

namespace WeeklyReport
{
    public class UnityConfig
    {
        public static void RegisterContainer()
        {
            var container = new UnityContainer();

            //ensure the repository is disposed after each request by using the lifetime manager
            container.RegisterType<IDataRepository, DataRepository>(new HierarchicalLifetimeManager());

            DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        } 
    }
}

And you have to call RegisterContainer in Global.ascx.cs inside Application_Start:

UnityConfig.RegisterContainer();

From a controller, it gets a handle to IDataRepository:

using WeeklyReport.Repository;

namespace WeeklyReport.Controllers
{    
    public class ReportController : Controller
    {
        private readonly IDataRepository repository;
        public ReportController(IDataRepository repository)
        {
            this.repository = repository;
        }

        public ActionResult Index()
        {
            List<ReportViewModel> reportVM = new List<ReportViewModel>();
            var reqs = repository.GetAllMetrics();
            // iterate reqs and put into reportVM, etc.
            return View(reportVM);
        }
    }
}

And you can call repository as if it was the real class - you're just getting an interface instance to it.

Share:
11,070
Andy
Author by

Andy

Updated on July 26, 2022

Comments

  • Andy
    Andy almost 2 years

    How do I inject my dbContext class using Unity? I can't just create a Interface like for my other "normal" classes? What should I do with my RequestContext class and what should my UnityConfig look like?

    public class RequestContext : IdentityDbContext<User>
        {
            public RequestContext()
                : base("DefaultConnection", throwIfV1Schema: false)
            {
                Database.SetInitializer<RequestContext>(new CreateDatabaseIfNotExists<RequestContext>());
            }
    
            public DbSet<Request> Requests { get; set; }
            public DbSet<Record> Records { get; set; }
    
    
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
                base.OnModelCreating(modelBuilder);
            }
    
            public static RequestContext Create()
            {
                return new RequestContext();
            }
        }
    

    In my Repository class i use it like this but want to inject instead:

     private RequestContext dbContext;
     private IUserRepository _userRepository;
    
     public RequestRepository(IUserRepository userRepository)
     {
          dbContext = new RequestContext();
          _userRepository = userRepository;
     }
    
  • thelem
    thelem almost 5 years
    With your first example aren't you creating a whole new context each time DbContextFactory is created? How does that add anything over creating a new DbContext yourself? You don't provide the source for the GenerateDbContext version, but wouldn't that create a new context every time it was used (meaning one context per SQL Query). I'm after one context per request (which might be an HTTP request, or a scheduled task)
  • smoksnes
    smoksnes almost 5 years
    @thelem you are right. My solution does not generate one context per web request, which I don't think OP was asking for. My solution gives you an opportunity to abstract the DbContext, but without hiding the dependency (via the factory). This may, or may not, be a desired solution for you. If you are looking for one DbContext per web request I would look into lifetimemanagers. There is one which I don't remember the name of, but something like PerRequestLifetimeManager.