简体   繁体   English

如何使用 Unity 注入我的 dbContext

[英]How to inject my dbContext with Unity

How do I inject my dbContext class using Unity?如何使用 Unity 注入我的 dbContext 类? 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?我应该如何处理我的 RequestContext 类以及我的 UnityConfig 应该是什么样子?

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:在我的 Repository 类中,我像这样使用它,但想改为注入:

 private RequestContext dbContext;
 private IUserRepository _userRepository;

 public RequestRepository(IUserRepository userRepository)
 {
      dbContext = new RequestContext();
      _userRepository = userRepository;
 }

I'm usually solving this with a DbContextFactory .我通常用DbContextFactory解决这个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你可以在这里看到一个更完整的例子: Repository Pattern 通用应用程序

With the factory you will also have the option to create the DbContext in constructor or in the method.使用工厂,您还可以选择在构造函数或方法中创建 DbContext。 When using Unity I recommend you to do as little as possible in the constructor, as Unity will resolve the entire chain for you.使用 Unity 时,我建议您在构造函数中尽可能少做,因为 Unity 会为您解析整个链。 This means that the DbContext will be created every time the repository is resolved.这意味着每次解析存储库时都会创建 DbContext。 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?这将要求注入存储库的类也需要处理存储库(反过来应该处理 DbContext),当两个类使用相同的存储库实例时会发生什么? 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.当您为上下文创建界面时,我还建议您将DbSet<T>更改为IDbSet<T>以进行更轻松的单元测试。 Example of interface for DbContext . DbContext的接口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).如果您DbContext在构造函数中注入DbContext ,您还可以查看Unit of Work-pattern ,它包装了DbContext并允许多个类在特定生命周期(例如请求)内使用相同的上下文。 One may argue that EF already implements the Unit of Work-pattern, but I leave that discussion for another time.有人可能会争辩说 EF 已经实现了工作单元模式,但我把这个讨论留到下次再说。 Here's a couple of examples:下面是几个例子:

http://www.codeproject.com/Articles/741207/Repository-with-Unit-of-Work-IoC-and-Unit-Test 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 洋葱架构、工作单元和通用存储库模式

I'm solving this problem with DbContext.Set<TEntity>() method, DbContext wrapper class and generics.我正在使用DbContext.Set<TEntity>()方法、 DbContext包装类和泛型解决这个问题。

I have IRepositoryContext interface and RepositoryContext to wrap my DbContext:我有IRepositoryContext接口和RepositoryContext来包装我的 DbContext:

public interface IRepositoryContext
{
    DbContext DbContext { get; }

    /// <summary>
    /// Commit data.
    /// </summary>
    void Save();
}

public class RepositoryContext : IRepositoryContext
{
    private readonly DbContext _dbContext;

    public RepositoryContext(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public DbContext DbContext { get { return _dbContext; } }

    public void Save()
    {
        _dbContext.SaveChanges();
    }
}

Ok, then I write bas implementation of generic repository:好的,然后我编写通用存储库的 bas 实现:

 public abstract class RepositoryBase<TEntity, TId> : IRepository<TEntity, TId>
    where TEntity : class , IEntity<TId>, IRetrievableEntity<TEntity, TId>
    where TId : struct
{
    protected readonly IRepositoryContext RepositoryContext;
    protected readonly DbContext Context;

    protected RepositoryBase(IRepositoryContext repositoryContext)
    {
        RepositoryContext = repositoryContext;
    }

    public DbSet<TEntity> Data { get { return RepositoryContext.DbContext.Set<TEntity>(); }

    public TEntity Get(TId id)
    {
        return Data.Find(id);
    }

    public virtual IList<TEntity> GetAll()
    {
        return Data.ToList();
    }

    public virtual TEntity Save(TEntity entity)
    {
        try
        {
            var state = entity.Id.Equals(default(TId)) ? EntityState.Added : EntityState.Modified;
            RepositoryContext.DbContext.Entry(entity).State = state;
            RepositoryContext.Save();
            return entity;
        }
        catch (DbEntityValidationException e)
        {
            throw ValidationExceptionFactory.GetException(e);
        }
    }

    public virtual void Delete(TEntity entity)
    {
        if (entity == null) return;
        Data.Remove(entity);
        Context.SaveChanges();
    }

    public void Commit()
    {
        RepositoryContext.Save();
    }

    public IList<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
    {
        return Data.Where(criteria).ToList();
    }

    // some other base stuff here
}

Ok, now I can register my DbContext with next extension methods:好的,现在我可以使用下一个扩展方法注册我的 DbContext:

public static class RikropCoreDataUnityExtensions
{
    #region Const

    private readonly static Type _repositoryInterfaceType = typeof(IRepository<,>);
    private readonly static Type _deactivatableRepositoryInterfaceType = typeof(IDeactivatableRepository<,>);
    private readonly static Type _deactivatableEntityType = typeof(DeactivatableEntity<>);
    private readonly static Type _retrievableEntityType = typeof(IRetrievableEntity<,>);

    #endregion Const

    #region public methods

    /// <summary>
    /// Register wrapper class.
    /// </summary>
    /// <typeparam name="TContext">DbContext type.</typeparam>
    /// <param name="container">Unity-container.</param>
    public static void RegisterRepositoryContext<TContext>(this IUnityContainer container)
        where TContext : DbContext, new()
    {
        container.RegisterType<IRepositoryContext, RepositoryContext>(new InjectionFactory(c => new RepositoryContext(new TContext())));
    }

    /// <summary>
    /// Register wrapper class.
    /// </summary>
    /// <typeparam name="TContext">DbContext type.</typeparam>
    /// <param name="container">Unity-container.</param>
    /// <param name="contextConstructor">DbContext constructor.</param>
    /// <param name="connectionString">Connection string name.</param>
    public static void RegisterRepositoryContext<TContext>(this IUnityContainer container,
        Func<string, TContext> contextConstructor, string connectionString)
        where TContext : DbContext
    {
        container.RegisterType<IRepositoryContext, RepositoryContext>(
            new InjectionFactory(c => new RepositoryContext(contextConstructor(connectionString))));
    }

    /// <summary>
    /// Automatically generation and registration for generic repository marked by attribute.
    /// </summary>
    /// <param name="container">Unity-container.</param>
    /// <param name="assembly">Assembly with repositories marked with RepositoryAttribute.</param>
    public static void RegisterCustomRepositories(this IUnityContainer container, Assembly assembly)
    {
        foreach (var repositoryType in assembly.GetTypes().Where(type => type.IsClass))
        {
            var repositoryAttribute = repositoryType.GetCustomAttribute<RepositoryAttribute>();
            if (repositoryAttribute != null)
            {
                container.RegisterType(
                    repositoryAttribute.RepositoryInterfaceType, 
                    repositoryType,
                    new TransientLifetimeManager());
            }
        }
    }

    /// <summary>
    /// Automatically generation and registration for generic repository for all entities.
    /// </summary>
    /// <param name="container">Unity-container.</param>
    /// <param name="assembly">Assembly with Entities which implements IRetrievableEntity.</param>
    public static void RegisterRepositories(this IUnityContainer container, Assembly assembly)
    {
        foreach (var entityType in assembly.GetTypes().Where(type => type.IsClass))
        {
            if (!entityType.InheritsFromGeneric(_retrievableEntityType))
                continue;

            Type[] typeArgs = entityType.GetGenericTypeArguments(_retrievableEntityType);
            Type constructedRepositoryInterfaceType = _repositoryInterfaceType.MakeGenericType(typeArgs);
            container.RegisterRepository(constructedRepositoryInterfaceType);

            if (entityType.InheritsFrom(_deactivatableEntityType.MakeGenericType(new[] { typeArgs[1] })))
            {
                var constructedDeactivatableRepositoryInterfaceType =
                    _deactivatableRepositoryInterfaceType.MakeGenericType(typeArgs);
                container.RegisterRepository(constructedDeactivatableRepositoryInterfaceType);
            }
        }
    }

    #endregion public methods

    #region private methods

    /// <summary>
    /// Generate and register repository.
    /// </summary>
    /// <param name="container">Unity-container.</param>
    /// <param name="repositoryInterfaceType">Repository interface type.</param>
    private static void RegisterRepository(this IUnityContainer container, Type repositoryInterfaceType)
    {
        var factoryGenerator = new RepositoryGenerator();
        var concreteFactoryType = factoryGenerator.Generate(repositoryInterfaceType);
        container.RegisterType(
            repositoryInterfaceType,
            new TransientLifetimeManager(),
            new InjectionFactory(
                c =>
                {
                    var activator = new RepositoryActivator();
                    return activator.CreateInstance(c, concreteFactoryType);
                }));
    }

    #endregion private methods
}

Finally you can just resolve IRepository<EntityType> on your classes.最后,您可以在您的类上解析IRepository<EntityType> You just need to register your RepositoryContext :你只需要注册你的RepositoryContext

container.RegisterRepositoryContext<MyDbContext>();
//container.RegisterRepositoryContext(s => new MyDbContext(s), "myConStr");

And your repository will resolve IRepositoryContext and you can have access to DbSet<TEntity> and other DbContext members via IRepositoryContext property.您的存储库将解析 IRepositoryContext,您可以通过IRepositoryContext属性访问DbSet<TEntity>和其他DbContext 成员

You can use full source code for repositories , Unity-helpers on Github.您可以在 Github 上使用存储库Unity 助手的完整源代码。

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这个站点有一个关于如何让 Unity 工作的很棒的教程: 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.我假设你已经安装了实体框架,知道如何使用System.ComponentModel.DataAnnotationsSystem.ComponentModel.DataAnnotations.Schema命名空间创建一个视图模型和放置属性,我假设你可以创建一个视图和控制器. None of that is really relevant until the end, anyway.无论如何,直到最后,这些都无关紧要。

You have to get the NuGet package Unity to install these references:您必须获得 NuGet 包Unity才能安装这些引用:

  • Microsoft.Practices.Unity微软.实践.Unity
  • Unity.Mvc3 (or 4 or 5) Unity.Mvc3(或 4 或 5)

My DataContext ( Model1.cs ) looks like this:我的 DataContext ( Model1.cs ) 看起来像这样:

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:我的存储库 ( DataRepository.cs ) 如下所示:

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:我的界面 ( IDataRepository.cs ) 如下所示:

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

My UnityConfig.cs in the App_Start folder looks like this:我在App_Start文件夹看起来像这样UnityConfig.cs:

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 :您必须在Application_Start内的Global.ascx.cs 中调用RegisterContainer

UnityConfig.RegisterContainer();

From a controller, it gets a handle to IDataRepository :从控制器,它获得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.并且您可以调用repository ,就好像它是真正的类一样——您只是获得了它的接口实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM