使用Simple Injector与Entity Framework Core DbContext Pooling

6

看到如何使用db上下文池的示例,我发现它是设计用于与ServiceCollection一起使用的:

var serviceProvider = new ServiceCollection()
    .AddDbContextPool<AdventureWorksContext>(options => { //options })
    .BuildServiceProvider();

但是Simple Injector呢?有没有可能在Simple Injector容器中注册DB池化呢?
附注:我的应用程序不是ASP.NET MVC,只是一种DAL。

1
你是否正在遵循Simple Injector的集成指南?如果你通过调用CrossWire<EfQueries>()或使用AutoCrossWireAspNetComponents来交叉连接你的DbContext,那么你将免费获得池化行为。 - Steven
Steven,感谢您的回复,但我恐怕这是不可能的,因为它不是MVC应用程序。它只是一个类库,可以从控制台应用程序中调用。还有其他解决方法吗? - user1178399
你的类库不应该使用 DI 容器,这样可以优雅地解决你的问题。只有启动项目,比如控制台应用程序,才应该进行注册和解析。 - Steven
@Steven,你会如何在 .net core 控制台应用程序中实现这个功能? - mscrivo
3个回答

15

ASP.NET Core中的EF Core DbContext池化

在将Simple Injector集成到ASP.NET Core中时,您可以将框架和第三方组件保留在.NET Core配置系统中。这意味着启用Entity Framework Core上下文池化与Microsoft的文档完全一致:

services.AddDbContextPool<BloggingContext>(
    options => options.UseSqlServer(connectionString));

由于Simple Injector 不替换内置配置系统, 因此您需要指示 Simple Injector 自动从 .NET Core 配置系统中加载缺失的注册信息(例如您的 DbContext)。这可以通过使用AddSimpleInjectorUseSimpleInjector扩展方法来实现,如下所示。

private SimpleInjector.Container container;

public void ConfigureServices(IServiceCollection services)
{
    ...
        
    services.AddDbContextPool<BloggingContext>(
        options => options.UseSqlServer(connectionString));        

    services.AddSimpleInjector(container, options =>
    {
        options.AddAspNetCore();
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseSimpleInjector(container);

    container.Verify();

    ...
}

使用这个设置,BloggingContext可以被注入到从Simple Injector解析出的任何组件中,而BloggingContext则由Entity Framework进行池化。例如:

// Application compoment registered in and resolved from Simple Injector
public class CommentIsNoSpamValidator : IValidator<PostComment>
{
    private readonly BloggingContext context;

    // Is injected with BloggingContext from IServiceCollection
    public CommentIsNoSpamValidator(BloggingContext context)
    {
        this.context = context;
    }

    public IEnumerable<ValidationResult> Validate(PostComment command)
    {
        // Complex business logic here.
    }
}

在.NET(Core)控制台应用程序中使用EF Core DbContext池

当涉及在.NET Core控制台应用程序中使用Entity Framework Core上下文池时,解决方案将非常相似,但是您需要进行一些设置:

public void Main()
{
    var container = new Container();

    var services = new ServiceCollection();
    
    services.AddDbContextPool<BloggingContext>(
        options => options.UseSqlServer(connectionString));
            
    services.AddSimpleInjector(container);

    services
        .BuildServiceProvider(validateScopes: true)
        .UseSimpleInjector(container);

    container.Verify();

    // Run application code
    using (AsyncScopedLifestyle.BeginScope(container))
    {
        var service = container.GetInstance<MainService>();
        service.DoAwesomeStuff();
    }
}

因此,DbContext的生命周期由MS.DI范围管理,但该范围由Simple Injector的范围管理。

在库中使用EF Core DbContext池

如果您正在构建一个库,即非启动项目,请停止当前操作。只有应用程序的启动程序集应该有Composition Root,且仅应该使用DI容器(如Simple Injector或MS.DI的ServiceCollection)的Composition Root。应用程序中的所有其他库都应该对容器的(可能)存在保持无知。


谢谢@Steven,这非常有帮助!所以如果我理解正确的话,每当您想要使用注入的DbContext时,您需要将其包装在:using (AsyncScopedLifestyle.BeginScope(container)) { }中,如果它将被控制台应用程序调用?在ASP.NET Core中不需要这样做,因为每个请求已经被作用域化了,但是如果您有共享代码并将其包装在SI范围内,那么如果在ASP.NET Core请求中调用它,会产生什么影响呢? - mscrivo
我不确定我完全理解这个问题,但通常情况下,您应该在活动范围内解决问题。当您配置了UseSimpleInjectorAspNetRequestScoping时,ASP.NET Core就会发生这种情况。启动作用域是您只应在组合根内部执行的操作;不要在库内部执行。 - Steven
好的,那很有道理。再次感谢。 - mscrivo

0
你可以使用以下代码:

container.Register(() => serviceProvider.GetRequiredService<AdventureWorksContext>());

这样,ServiceProvider 就会在需要时解析依赖项。

0

在 @Steven 的优秀回答基础上,这里是他的控制台应用程序答案,但隐式地使用了UseInternalServiceProvider上下文。

var services = new ServiceCollection();
services.AddEntityFrameworkSqlServer();
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(new Microsoft.Extensions.Logging.LoggerFactory());
services.AddSingleton<IInterface>(new MyImplemenation());

services.AddDbContextPool<EFViewAndManyToManyDb>(optionsBuilder => {
  optionsBuilder.UseSqlServer("");
});

services.AddSimpleInjector(container);

services
  .BuildServiceProvider(validateScopes: true)
  .UseSimpleInjector(container);

这里是为什么你想要使用UseInternalServiceProvider。简而言之:如果你的DbContext有依赖项,例如IInterface。你还需要添加SimpleInjector.Integration.ServiceCollection Nuget包。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接