使用 Unity 实现 IUserStore 依赖

Fulfilling IUserStore Dependency with Unity

我在获取 unity 为我的 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 中的任何 IDBSet 上调用 Add() 时,我最终将 DbContext 实例化两次导致错误 "System.InvalidOperationException: 'An entity object cannot be referenced by multiple instances of IEntityChangeTracker.'"我猜这是因为 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: 'The operation cannot be completed because the DbContext has been disposed.'"。显然我可以避免在我的 Context 上调用 Dispose 但这很糟糕,因为我认为这意味着我实际上在我的应用程序的整个生命周期中使用相同的 DBContext 实例。

我应该在我的 UnityConfig 中添加什么来确保满足 IDealFinderDb 和 IUserStore 依赖关系,并且每次实例化我的控制器时只实例化一个上下文?

谢谢

What should I put in my UnityConfig to ensure that both the IDealFinderDb and IUserStore dependencies are satisfied and that only a single context is instantiated each my controller is instantiated?

您应该使用在 Unity 中称为 PerResolveLifetimeManager 的图生命周期管理器:

container.RegisterType<IDealFinderDb, DealFinderDb>(new PerResolveLifetimeManager());

根据软件设计模式的最佳实践,如果您觉得需要单例对象,那么在创建数据库上下文和记录器上下文以及许多其他业务需求时,您应该始终遵循单例模式如果你正在实现 threads.its 如此简单,那么使用单例模式确实会处理线程安全单例并寻求帮助,你可以参考 MSDN,它有单例的实现。 https://msdn.microsoft.com/en-us/library/ff647854.aspx 希望这有帮助。