如何使用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;
}
答案 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上使用repositories,Unity-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.DataAnnotations
和System.ComponentModel.DataAnnotations.Schema
命名空间创建视图模型并设置属性,并且假设您可以创建视图和控制器。到最后,所有这些都没有真正的意义。
您必须获取NuGet软件包 Unity 才能安装这些引用:
我的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
-您只是在获取接口实例。