在DbContext内部使用DI

huangapple go评论62阅读模式
英文:

Use DI inside the DbContext

问题

你的服务在这个地方注入是没问题的。你需要在 SaveChangesAsync 方法中获取服务的正确方式。尝试使用 this.GetInfrastructure().GetRequiredService<...>() 替代 this.GetService<...>()

var myService = this.GetInfrastructure().GetRequiredService<IMyService>();
英文:

I'm using ef core 6 and I want to get a service from DI inside my db context. This is my DbContext:

public class MyDbContext : DbContext
{
    public MyDbContext (DbContextOptions&lt;MyDbContext&gt; opt) : base(opt)
    {
    }

    public override async Task&lt;int&gt; SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        var myService = this.GetService&lt;IMyService&gt;();
        // Do some stuffs before saving with myService
    
        var result = await base.SaveChangesAsync(cancellationToken);
    
        return result;
    }   

}

The IMyService and MyDbContext already introduced with the DI:

services.AddScoped&lt;IMyService, MyService&gt;(); 
services.AddDbContextPool&lt;MyDbContext&gt;(opt =&gt; opt.UseSqlServer(connectionString));

The service doesn't inject, and this line throws an error:

var myService = this.GetService&lt;IMyService&gt;();

Error:
Cannot resolve scoped service &#39;IMyService&#39; from root provider

What should I do? Do I miss something?

答案1

得分: 2

DI(依赖注入)与 DbContexts(数据库上下文)的工作方式与任何其他注入的服务一样 - 通过将依赖项添加为构造函数参数。

从评论中看来,你实际想要的是在 EF Core 中使用二级缓存(在会话/事务/工作单元之间),这并不是开箱即用的功能,而且一般来说,这个概念现在不如几年前那么流行。ORM(对象关系映射)通常不用于与非关系数据库进行交互,所以应用程序通常在更高层次上使用单独的对象缓存。

有一些 NuGet 包可以将二级缓存添加到 EF Core,例如 EFCoreSecondLevelCacheInterceptor。该项目使用 EF Core DbCommand 拦截器 来跟踪和缓存由 EF Core 加载或持久化的数据。示例页面展示了如何使用内存或 Redis 缓存。

一旦配置了缓存,将其添加到 DbContext 很容易:

public static class MsSqlServiceCollectionExtensions
{
    public static IServiceCollection AddConfiguredMsSqlDbContext(this IServiceCollection services, string connectionString)
    {
        services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
                optionsBuilder
                    .UseSqlServer(
                        connectionString,
                        sqlServerOptionsBuilder =>
                        {
                            sqlServerOptionsBuilder
                                .CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds)
                                .EnableRetryOnFailure()
                                .MigrationsAssembly(typeof(MsSqlServiceCollectionExtensions).Assembly.FullName);
                        })
                    .AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>()));
        return services;
    }
}

相关部分仅添加了 DbCommand 拦截器 SecondLevelCacheInterceptor

.AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>());

该包允许缓存特定查询的结果:

var post1 = context.Posts
               .Where(x => x.Id > 0)
               .OrderBy(x => x.Id)
               .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
               .FirstOrDefault();

它还可以配置为缓存所有查询:

services.AddEFSecondLevelCache(options =>
{
    options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_");
    options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
});

缓存特定查询:

services.AddEFSecondLevelCache(options =>
{
    options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
        /*.CacheQueriesContainingTypes(
            CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableTypeComparison.Contains,
            typeof(Post), typeof(Product), typeof(User)
            )*/
        .CacheQueriesContainingTableNames(
            CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,
            "posts", "products", "users"
            );
});

或者避免缓存特定查询:

services.AddEFSecondLevelCache(options =>
{
    options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix("EF_")
            // 如何跳过缓存特定命令
           .SkipCachingCommands(commandText =>
                    commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
});
英文:

DI works the same way with DbContexts as any other injected service - by adding the dependency as a constructor parameter.

From the comments it seems that what you actually want is to use second-level caching (between sessions/transactions/Units-of-Work) with EF Core. This isn't available out of the box and in general, is a concept that isn't as popular now as it was some years ago. ORMs aren't used to talk to non-relational databases so applications use separate object caching at a higher level instead.

There are some NuGet packages that do add second-level caching to EF Core, for example EFCoreSecondLevelCacheInterceptor. This project uses EF Core DbCommand interceptors to track and cache the data loaded or persisted by EF Core. The landing page examples show how to use either in-memory or Redis caching.

Once you configure caching, adding it to a DbContext is easy :

    public static class MsSqlServiceCollectionExtensions
    {
        public static IServiceCollection AddConfiguredMsSqlDbContext(this IServiceCollection services, string connectionString)
        {
            services.AddDbContextPool&lt;ApplicationDbContext&gt;((serviceProvider, optionsBuilder) =&gt;
                    optionsBuilder
                        .UseSqlServer(
                            connectionString,
                            sqlServerOptionsBuilder =&gt;
                            {
                                sqlServerOptionsBuilder
                                    .CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds)
                                    .EnableRetryOnFailure()
                                    .MigrationsAssembly(typeof(MsSqlServiceCollectionExtensions).Assembly.FullName);
                            })
                        .AddInterceptors(serviceProvider.GetRequiredService&lt;SecondLevelCacheInterceptor&gt;()));
            return services;
        }
    }

The relevant part only adds the DbCommand interceptor SecondLevelCacheInterceptor

.AddInterceptors(serviceProvider.GetRequiredService&lt;SecondLevelCacheInterceptor&gt;()));

The package allows caching the results of specific queries :

var post1 = context.Posts
                   .Where(x =&gt; x.Id &gt; 0)
                   .OrderBy(x =&gt; x.Id)
                   .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
                   .FirstOrDefault();  

It can also be configured to cache all queries:

        services.AddEFSecondLevelCache(options =&gt;
        {
            options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix(&quot;EF_&quot;);
            options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
        });

Specific queries :

        services.AddEFSecondLevelCache(options =&gt;
        {
            options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix(&quot;EF_&quot;)
                /*.CacheQueriesContainingTypes(
                    CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableTypeComparison.Contains,
                    typeof(Post), typeof(Product), typeof(User)
                    )*/
                .CacheQueriesContainingTableNames(
                    CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,
                    &quot;posts&quot;, &quot;products&quot;, &quot;users&quot;
                    );
        });

Or avoid caching specific queries

        services.AddEFSecondLevelCache(options =&gt;
        {
            options.UseMemoryCacheProvider().DisableLogging(true).UseCacheKeyPrefix(&quot;EF_&quot;)
                    // How to skip caching specific commands
                   .SkipCachingCommands(commandText =&gt;
                            commandText.Contains(&quot;NEWID()&quot;, StringComparison.InvariantCultureIgnoreCase));
        });

huangapple
  • 本文由 发表于 2023年2月16日 16:11:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75469411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定