如何使用autofac动态更改DI?

时间:2018-06-08 06:21:01

标签: c# dependency-injection autofac repository-pattern

我们正在开发Windows服务,我想在存储库中动态更改dbcontext类。

下面是情景。

我有三个db上下文类

 public abstract class Context : DbContext, IUnitOfWork
    {
        protected Context(string connectionString) : base(connectionString)
        {

        }
    }

    public class PlatformContext : Context
    {
        private readonly string _connectionString;

        public PlatformContext(string connectionString)
            : base(connectionString)
        {
            _connectionString = connectionString;
        }
    }

    public class PlatformReplicaContext : Context
    {
        private readonly string _connectionString;
        public PlatformReplicaContext(string connectionString)
            : base(connectionString)
        {
            _connectionString = connectionString;
        }
    }

    public class TempContext : Context
    {
         private readonly string _connectionString;
         public TempContext(string connectionString)
        : base(connectionString)
         {
           _connectionString = connectionString;
         }
   }

我有资源库

public interface ICategoryRepository : IRepository<Category>
   {

   }

 public class CategoryRepository :Repository<Category>, ICategoryRepository
   {
        public CategoryRepository(Context context) : base(context)
        {

        }
   }

因此我使用CQRS我还有另外三个类

public class CategoryBasicQuery:IRequest<BaseQueryResponse>
{
    public int CategoryId { get; set; }
}

public class CategoryBasicQueryHandler : IRequestHandler<CategoryBasicQuery, BaseQueryResponse>
{
    private readonly ICategoryRepository _categoryRepository;
    private readonly IMapper _mapper;

    public CategoryBasicQueryHandler(ICategoryRepository categoryRepository, IMapper mapper)
    {
        _categoryRepository = categoryRepository;
        _mapper = mapper;
    }
    public async Task<BaseQueryResponse> Handle(CategoryBasicQuery request, CancellationToken cancellationToken)
    {
        var entry = await _categoryRepository.FindAsync(request.CategoryId);
        if (entry == null)
        {
            return new NotFoundResponse();
        }

        var response = _mapper.Map<CategoryBasicResponse>(entry);
        return response;
    }
}

现在问题是

此类别存储库应该能够在所有3种类型的上下文中执行查询。 但是如何在使用autofac时注册类? 然后我想出了一个在运行时生成存储库的解决方案,如下所示

public class RepositoryFactory
{
    public static TRepository GetRepositoryInstance<T, TRepository>(
        params object[] args)
        where TRepository : IRepository<T>
    {
        return (TRepository)Activator.CreateInstance(typeof(TRepository), args);
    }
}

我在CategoryBasicQueryHandler类中调用此方法,就像这样

 var categoryRepo = RepositoryFactory.GetRepositoryInstance<Category, CategoryRepository>(new PlatformReplicaContext("connectionString"));

但是从CQRS打电话

var categoty = new Category();
var command = new CategoryBasicQuery {CategoryId = categoryId};
var result =  _mediator.Send(command);

VS给我以下错误

enter image description here

我的autofac注册如下

    builder.RegisterType<CategoryService>().AsSelf();
    builder.RegisterType<ActionRepository>().As<IActionRepository>();
    builder.RegisterType<CategoryRepository>().As<ICategoryRepository>();
    builder.RegisterType<Mapper>().As<IMapper>();

任何人都可以帮我解决这个问题,或者提出一个好的方法来处理这种情况。

感谢。

1 个答案:

答案 0 :(得分:1)

这可能为您提供可行解决方案的良好起点:http://autofaccn.readthedocs.io/en/latest/resolve/relationships.html#keyed-service-lookup-iindex-x-b

builder.RegisterType<PlatformContext>().Keyed<Context>("platform");
builder.RegisterType<PlatformReplicaContext>().Keyed<Context>("replica");
builder.RegisterType<TempContext>().Keyed<Context>("temp");

您在评论中提到某个名为action的变量将指示要使用的实现:

public class Class1
{
    private readonly IIndex<string, Context> contexts;

    public Class1(IIndex<string, Context> contexts)
    {
         this.contexts = contexts;
    }

    public void Whatever()
    {
        string action = ...; // platform, replica or temp
        Context context = this.contexts[action];
        ...
    }
}

当然,这需要进行调整,以使其适合您的应用程序设计的其余部分。一个可能的例子可能是:

Context context = this.contexts[action];

using(ILifetimeScope scope = container.BeginLifetimeScope(builder =>
{
    builder.RegisterInstance(context).As<Context>();
}))
{
    // Because we are resolving IMediator from the scope, the selected Context will be used in all dependencies
    var mediator = scope.Resolve<IMediator>();
    mediator.Send(...);
}