通过Unity实现IUserStore依赖

时间:2017-11-08 18:51:20

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

我在为我的MVC控制器提供依赖关系时遇到了一些问题。

我希望我的ApplicationUser数据和我的业务数据位于同一个数据库中,并且我正在使用实体框架进行代码优先迁移。为此,我的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被实例化两次导致&#34; System.InvalidOperationException错误:&#39; IEntityChangeTracker的多个实例无法引用实体对象。 &#39;&#34;当我在DbContext中的任何IDBSets上调用Add()时,我想这是因为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()时,我收到错误&#34; System.InvalidOperationException:&#39;由于已经处理了DbContext,因此无法完成操作。&#39; &#34 ;.显然我可以避免在我的Context上调用Dispose,但这很糟糕,因为我认为这意味着我实际上在我的应用程序的整个生命周期中使用相同的DBContext实例。

我应该在UnityConfig中放置什么来确保IDealFinderDb和IUserStore依赖项都得到满足,并且每次实例化我的控制器时只实例化一个上下文?

由于

2 个答案:

答案 0 :(得分:0)

  

我应该在UnityConfig中放置什么来确保这两者   IDealFinderDb和IUserStore依赖项是唯一满足的   每个我的控制器实例化一个上下文实例化?

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

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

答案 1 :(得分:0)

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