C# 依赖注入 注入一个Func

在实际代码中有时候我们会希望每一次运行都需要一个新的对象, 比如EFCore当中的 DbContext。 因为DbContext有一些设计当中存在的问题。

  1. 跟踪对象,会导致性能越来越不好,
  2. SaveChanges 出错后会导致 后面的提交也会一直出错。(另一种处理方式是清掉错误)
  3. 某条记录在数据库被更改后,context因为有缓存住这个对象导致它的属性值不会发生变化. 所以取出来的数据还是旧的.
  4. 处理的函数中有Lock锁,DbContext却是一开始就注入的。这样会导致连接池不过用。

(该对象也不适合配置成Transient)

 private static void Register(IServiceCollection services, IConfiguration configuration)
        {
            services.AddSingleton<IMessageWriter, MessageWriter>();
            services.AddSingleton<Worker>();

//在这边注入 Func<MalemaDbContext>
            services.AddSingleton(sp =>
            {
                var conn = configuration.GetConnectionString("malema");
                return new Func<MalemaDbContext>(() =>
                {
                    var builder = new DbContextOptionsBuilder<MalemaDbContext>();
                    builder.UseSqlServer(conn);
                    return new MalemaDbContext(builder.Options);
                });
            });
        }

使用它

using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace Malema.net.Example
{
    public class Worker
    {
        private readonly IMessageWriter _messageWriter;
        private readonly Func<MalemaDbContext> _func;
        public Worker(IMessageWriter messageWriter, Func<MalemaDbContext> func)
        {
            _messageWriter = messageWriter;
            _func = func;
        }

        public async Task ExecuteAsync()
        {
            _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            for(int i=0;i<10;i++)
            {
                //某些情况下我们希望dbContext每次都是不一样的
                using (var dbContext = _func())
                { 
                    //
                }
            }
        }
    }
}

通过使用 CreateScope() 我们也做到类似上面的效果. 详看 scope

Func<>这种方式可能不太好理解,更好理解的方式我们可以创建一个IMalemaDbContextFactory,这样它的注入方式就可以跟其它类一模一样了。

备注:实际上 EF5.0 之后有支持 IDbContextFactory<MalemaDbContext> 示例代码如下

var services = new ServiceCollection();
services.AddDbContextFactory<MalemaDbContext>(x => x.UseInMemoryDatabase("malema"));
//serviceCollection.AddPooledDbContextFactory<MalemaDbContext>(x => x.UseInMemoryDatabase("mlema"));//pool的方式
var factory = services.BuildServiceProvider().GetRequiredService<IDbContextFactory<MalemaDbContext>>();
using (var context = factory.CreateDbContext())
{ 
}
最近更新的
...