【问题标题】:Fulfilling IUserStore Dependency with Unity使用 Unity 实现 IUserStore 依赖
【发布时间】: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


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 我已经阅读了几篇关于避免使 DbContext 成为单例的文章,特别是由于内存和延迟加载问题。我认为 DbContext 的生命周期管理是一件相当复杂的事情。我想我现在不想让它成为单身,但感谢您的链接。
      猜你喜欢
      • 1970-01-01
      • 2015-08-03
      • 2014-08-16
      • 2015-10-25
      • 1970-01-01
      • 2013-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多