【问题标题】:How to inject my dbContext with Unity如何用 Unity 注入我的 dbContext
【发布时间】:2016-04-17 16:13:27
【问题描述】:

如何使用 Unity 注入我的 dbContext 类?我不能像其他“普通”类一样创建一个接口吗?我应该如何处理我的 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();
        }
    }

在我的 Repository 类中,我像这样使用它,但想注入:

 private RequestContext dbContext;
 private IUserRepository _userRepository;

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

【问题讨论】:

    标签: c# asp.net-mvc entity-framework unity-container


    【解决方案1】:

    我通常用DbContextFactory 来解决这个问题。这将允许您在需要时创建上下文,并在完成后处理它。

    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;
        }
    }
    

    这个工厂很容易被注入。您可以在此处查看更完整的示例:Repository Pattern universal application

    使用工厂,您还可以选择在构造函数或方法中创建 DbContext。使用 Unity 时,我建议您在构造函数中尽可能少做,因为 Unity 会为您解析整个链。这意味着每次解析存储库时都会创建 DbContext。这将要求注入存储库的类也需要释放存储库(这反过来应该释放 DbContext),当两个类使用相同的存储库实例时会发生什么?这显然可以通过生命周期管理器和良好的编程实践来解决,但我发现在需要时简单地打开和关闭上下文更优雅。

    方法中的使用示例:

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

    还有一个更完整的存储库示例:

    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);
            }
        }
    }
    

    当您为您的上下文创建界面时,我还建议您将 DbSet&lt;T&gt; 更改为 IDbSet&lt;T&gt; 以便更轻松地进行单元测试。 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;
    }
    

    如果您希望在构造函数中注入DbContext,您还可以查看Unit of Work-pattern,它包装了DbContext,并允许多个类在特定生命周期内使用相同的上下文(例如一个要求)。有人可能会争辩说 EF 已经实现了工作单元模式,但我将讨论另当别论。举几个例子:

    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

    【讨论】:

    • 在您的第一个示例中,您不是在每次创建 DbContextFactory 时都创建一个全新的上下文吗?与自己创建新的 DbContext 相比,这有什么好处?您不提供 GenerateDbContext 版本的源代码,但不会在每次使用它时创建一个新上下文(意味着每个 SQL 查询一个上下文)。我针对每个请求(可能是 HTTP 请求或计划任务)处理一个上下文
    • @thelem 你是对的。我的解决方案不会为每个 Web 请求生成一个上下文,我认为 OP 并没有要求。我的解决方案让您有机会抽象 DbContext,但不隐藏依赖项(通过工厂)。这可能是也可能不是您想要的解决方案。如果您正在为每个 Web 请求寻找一个 DbContext,我会考虑lifetimemanagers。有一个我不记得名字了,但是像 PerRequestLifetimeManager。
    【解决方案2】:

    这个网站有一个关于如何让 Unity 工作的很棒的教程:https://medium.com/aeturnuminc/repository-pattern-with-dependency-injection-mvc-ef-code-first-91344413ba1c

    我假设您已安装实体框架,知道如何创建视图模型并使用 System.ComponentModel.DataAnnotationsSystem.ComponentModel.DataAnnotations.Schema 命名空间添加属性,并且我假设您可以创建视图和控制器。无论如何,直到最后,这些都不是真正相关的。

    您必须获取 NuGet 包 Unity 才能安装这些参考:

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

    我的 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);
        }
    }
    

    我的存储库 (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.
        }
    }
    

    我的界面 (IDataRepository.cs) 如下所示:

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

    我的 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));
    
            } 
        }
    }
    

    您必须在Application_Start 内的Global.ascx.cs 中调用RegisterContainer

    UnityConfig.RegisterContainer();
    

    从一个控制器,它得到一个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);
            }
        }
    }
    

    你可以调用repository,就好像它是真正的类一样——你只是得到了一个接口实例。

    【讨论】:

      【解决方案3】:

      我正在使用DbContext.Set&lt;TEntity&gt;() 方法、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();
          }
      }
      

      好的,那我写泛型仓库的基本实现:

       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
      }
      

      好的,现在我可以使用下一个扩展方法注册我的 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
      }
      

      最后你可以在你的类上解决IRepository&lt;EntityType&gt;。你只需要注册你的RepositoryContext:

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

      您的存储库将解析 IRepositoryContext,您可以通过IRepositoryContext 属性访问DbSet&lt;TEntity&gt; 和其他DbContext members

      您可以在 Github 上使用 repositoriesUnity-helpers 的完整源代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-02
        • 2018-06-30
        • 2017-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-25
        相关资源
        最近更新 更多