【问题标题】:ASP.NET Core resolve DbContext dependency to init migrationsASP.NET Core 将 DbContext 依赖解析为初始化迁移
【发布时间】:2016-11-16 15:41:03
【问题描述】:

ASP.NET DI 开箱即用并递归解决所有构造函数依赖项非常棒。尽管有时您希望能够直接访问 DI 容器。我想知道有没有办法?也许是这样的:

IService service = Container.Instance.Resolve<IService>();

我在docs 中没有找到任何东西(虽然我知道,我可以替换内置的 DI 框架)。


在大多数情况下您不需要它,但有一些特定的情况。在我的情况下,我需要在应用启动时使用 IoC 容器初始化我的 EF DbContext,以启动应该与 EF Core 一样工作的自动迁移:

using (var context = new MyContext())
{
    context.Database.Migrate();
}

我的上下文已在容器中注册,但它位于单独的类库中。而且我不想直接称它为DbContextOptions构造函数。

【问题讨论】:

  • 非常不鼓励使用这种做法,因为它基本上首先扼杀了拥有 IoC 的所有好处,并有效地成为一种服务定位器模式,这是一种反模式(它隐藏了依赖关系) .了解为什么您想要这样做会更有趣。在 99% 的情况下,这只是一个糟糕的设计,而在 ASP.NET Core 的上下文中,我想不出有一个必要的情况,否则无法解决
  • @Tseng 我同意你的看法。如果应用程序设计良好,则解决方案会从控制器到整个系统的存储库。但也有具体案例。在我的特殊情况下,我想在启动时使用 DI 初始化我的 EF DbContext 以启动自动迁移。它发生在启动时,而不是请求到达控制器时。
  • 关键词——“启动时”。当您在启动期间解决来自容器的依赖项时,这就是您的组合根。这正是它所属的地方。请参阅 this post 在启动时从容器中解析过滤器,由 Mark Seemann 编写(关于 .NET 中的依赖注入的官方真正聪明的人。)
  • @ScottHannen 哦,我知道它属于哪里。我只是想弄清楚我是否可以在不重复自己的情况下更好地接近它。我的意思是我不想在系统中有两个地方来初始化我的上下文。 1 号位置是 IServiceCollection 注册,2 号位置是我运行迁移的位置。显然我必须为上下文创建编写工厂,我希望有一种方法可以直接使用 IoC,但看起来没有。
  • @Anrei M - 这实际上是为那些说从容器中明确解决任何事情不正确的人准备的。只是指出当它位于组合根时它不是服务定位器模式。

标签: c# dependency-injection asp.net-core .net-core di-containers


【解决方案1】:

在问题得到更新并且很明显它是关于 EntityFramework Core 迁移之后,ASP.NET Core 团队有一种半官方的方式来说明如何在应用程序启动期间正确解决DbContext

默认情况下,DbContext 注册为作用域服务,因此每个请求都会实例化一次。应用程序启动期间的问题是,还没有上下文,在 Configure 方法中只有 app.ApplicationServices 可用,ApplicationServices 提供程序本质上是在解析单例(作用域是每个作用域一个单例,应用程序作用域只要应用程序确实如此)。

所以诀窍是首先创建一个范围,解析DbContext,执行操作,然后释放上下文(以及DbContext)。

可以在 MusicStore 示例应用程序 herehere 中找到示例。

截至目前,这是解决它的唯一安全方法,不会导致任何问题,即已处置的对象异常。

另外请注意,您最早可以在Configure 方法中执行此操作,因为只有这样才能构建 IoC 容器。在ConfigureServices 中,您只填充IServiceCollection

相关代码sn-ps:

void Configure(IApplicationBuilder app)
{
    //Populates the MusicStore sample data
    SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
}

public static class SampleData
{
    ...

    public static async Task InitializeMusicStoreDatabaseAsync(IServiceProvider serviceProvider, bool createUsers = true)
    {
        using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var db = serviceScope.ServiceProvider.GetService<MusicStoreContext>();

            if (await db.Database.EnsureCreatedAsync())
            {
                await InsertTestData(serviceProvider);
                if (createUsers)
                {
                    await CreateAdminUser(serviceProvider);
                }
            }
        }
    }
}

编辑:

有关此问题的其他资源:

【讨论】:

    猜你喜欢
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    相关资源
    最近更新 更多