【发布时间】:2017-11-08 18:51:19
【问题描述】:
我在统一为我的 MVC 控制器提供依赖项时遇到了一些问题。
我希望我的 ApplicationUser 数据和我的业务数据在同一个数据库中,并且我正在使用 Entity Framework 的代码优先迁移。为此,我的 DbContext 继承自 IdentityDbContext,然后实现了一个代表我的业务数据的接口:
public class DealFinderDb : IdentityDbContext<ApplicationUser>, IDealFinderDb
{
public DealFinderDb() : base("name=DealFinderConnectionString", false)
{
}
public IDbSet<Deal> Deals { get; set; }
public IDbSet<Category> Categories { get; set; }
public IDbSet<SavedSearch> SavedSearches { get; set; }
public static DealFinderDb Create()
{
return new DealFinderDb();
}
}
public interface IDealFinderDb : IDisposable
{
IDbSet<Deal> Deals { get; set; }
IDbSet<Category> Categories { get; set; }
IDbSet<SavedSearch> SavedSearches { get; set; }
int SaveChanges();
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity)
where TEntity : class;
}
在我的控制器中,我需要能够获取当前用户,这意味着我的控制器不仅依赖于 IDealFinderDb,还依赖于 UserManager。我知道测试这个的最好方法是模拟一个 IUserStore 并将其传递给我的控制器的构造函数。我已经编写了模拟 IUserStore 和控制器的 HttpContext 的测试,并且这些测试按预期工作。这意味着我的控制器看起来像这样:
public class SavedSearchesController : Controller
{
private readonly IDealFinderDb dealFinderDb;
private readonly UserManager<ApplicationUser> userManager;
public SavedSearchesController(IDealFinderDb dealFinderDb, IUserStore<ApplicationUser> userStore)
{
this.dealFinderDb = dealFinderDb;
this.userManager = new UserManager<ApplicationUser>(userStore);
}
public ActionResult Index()
{
var user = this.userManager.FindById(this.User.Identity.GetUserId());
var usersSavedSearches = this.dealFinderDb.SavedSearches.Where(s => s.User.Id == user.Id);
return this.View(usersSavedSearches);
}
// Snip unrelated action methods.
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.dealFinderDb.Dispose();
}
base.Dispose(disposing);
}
}
这看起来不错,但我正在使用 Unity 在运行时为这些接口提供实现,这就是我遇到的问题。我对 UnityConfig 的第一次尝试如下所示:
container.RegisterType<IDealFinderDb, DealFinderDb>();
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(DealFinderDb)));
...但问题是我最终将 DbContext 实例化了两次,导致“System.InvalidOperationException:'一个实体对象不能被多个 IEntityChangeTracker 实例引用。'”当我调用 @ 987654324@ 在我的 DbContext 中的任何 IDBSet 上我猜这是因为 unity 正在实例化我的 DbContext 两次。
所以我的下一个尝试是确保只创建一个 DealFinderDb 实例,并且在我的 UnityConfig 中看起来像这样:
container.RegisterType<DealFinderDb, DealFinderDb>(new ContainerControlledLifetimeManager());
container.RegisterType<IDealFinderDb, DealFinderDb>();
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(DealFinderDb)));
...但是当在我的控制器中调用this.userManager.FindById() 时,我收到错误“System.InvalidOperationException:'操作无法完成,因为 DbContext 已被释放。'”。显然我可以避免在我的 Context 上调用 Dispose ,但这很糟糕,因为我认为这意味着我实际上在我的应用程序的整个生命周期中使用了相同的 DBContext 实例。
我应该在 UnityConfig 中添加什么以确保 IDealFinderDb 和 IUserStore 依赖项都得到满足,并且每次实例化我的控制器时只实例化一个上下文?
谢谢
【问题讨论】:
标签: c# asp.net-mvc unity-container