如何使用Unity注入我的dbContext

时间:2016-04-17 16:13:27

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

如何使用Unity注入我的dbContext类?我不能像我的其他人一样创建一个界面" normal"班?我应该如何处理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;
 }

3 个答案:

答案 0 :(得分:0)

我正在使用DbContext.Set<TEntity>()方法,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<EntityType>问题。您只需注册RepositoryContext

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

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

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

答案 1 :(得分:0)

我通常用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<T>更改为IDbSet<T>,以便更轻松地进行单元测试。 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

答案 2 :(得分:0)

该网站上有关于如何使Unity正常工作的绝佳教程:https://medium.com/aeturnuminc/repository-pattern-with-dependency-injection-mvc-ef-code-first-91344413ba1c

我将假设您已经安装了Entity Framework,知道如何使用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));

        } 
    }
}

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

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 -您只是在获取接口实例。