【发布时间】:2019-07-25 08:43:56
【问题描述】:
我遇到了以下问题,如何配置 Autofac 容器,但找不到解决方案。
假设我有一堆存储库,例如 AccountRepository、ContactRepository、LeadRepository 等。
每个存储库都有一个 IService 类型的构造函数参数,它提供基本 CRUD 方法的实现。在我的例子中,它是一个到第 3 方应用程序的通用 Web 服务连接,但这并不重要。
例如,我有这样的事情:
public class AccountRepository
{
private readonly IService service;
public AccountRepository(IService service)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
}
public int GetContactCount(Guid accountId)
{
using(DataContext ctx = new DataContext(service))
{
return ctx.Contacts.Where(c => c.AccountId == accountId).Count();
}
}
}
我的域代码是通过命令和事件实现的。因此,假设我有一个使用上述存储库的以下命令处理程序:
public class UpdateNrOfContactsCommandHandler : IHandleCommand<UpdateNrOfContactsCommand, Account>
{
private readonly AccountRepository accountRepo;
public UpdateNrOfContactsCommandHandler(AccountRepository accountRepo)
{
this.accountRepo = accountRepo ?? throw new ArgumentNullException(nameof(accountRepo));
}
public void Execute(Account account)
{
account.NrOfContacts = repo.GetContactCount(account.Id);
}
}
我不会将存储库隐藏在任何接口后面,因为它是业务逻辑的一部分,并且只有一个实现。 IService 是动态部分(也是我在单元测试中伪造的部分)。 如果有帮助,我可以在上面添加一个界面,但这只是我想避免的额外输入。
存储库和命令处理程序都是通过程序集扫描动态注册的。我显然不想在每次添加新的存储库或命令处理程序时更新我的组合根。它应该只配置一次(除非我们引入一些新的抽象)。
所以基本上我注册了来自某个程序集的所有存储库和来自另一个程序集的所有命令处理程序。当命令进来时,我解析处理程序并在其上调用 .Execute() 方法。很标准的东西。
问题是取决于上下文(只有命令处理程序知道,存储库应该是完全不可知的)我需要存储库在系统管理上下文中或在当前用户上下文。
我有两个 IService 实例:
1. IService serviceAsAdmin
2. IService serviceAsCurrentUser
管理上下文中的服务应该是默认服务。
我认为优雅的解决方案是这样的:
-
定义一个自定义属性,如
public class InUserContextAttribute : Attribute { } -
以后像这样使用这个属性:
public class UpdateNrOfContactsCommandHandler : IHandleCommand<UpdateNrOfContactsCommand, Account> { private readonly AccountRepository accountRepo; private readonly AccountRepository accountRepoAsUser; public UpdateNrOfContactsCommandHandler(AccountRepository accountRepo, [InUserContext] AccountRepository accountRepoAsUser) { this.accountRepo = accountRepo ?? throw new ArgumentNullException(nameof(accountRepo)); this.accountRepoAsUser = accountRepoAsUser ?? throw new ArgumentNullException(nameof(accountRepoAsUser)); } public void Execute(Account account) { account.NrOfContacts = repo.GetContactCount(account.Id); account.NrOfContactsSeenByCurrentUser = accountRepoAsUser.GetContactCount(account.Id); } }
我不知道该怎么做 :) 研究了很多样本,但似乎没有一个适合这种情况。额外的复杂性是这需要通过程序集扫描进行动态处理。
我知道如何通过属性注入(通过使用 Autofac 的 .OnActivated() 方法)很容易地实现类似的东西,但是这些存储库不是可选的,所以它们应该在构造函数中传递。
我还想避免在组合根/命令总线之外的任何 Autofac 引用。绝对不想在我的业务逻辑中添加任何 Autofac 特定的东西。
基本上,我需要做的是以某种方式注册 2 个 IService 实例,这样如果参数未修饰,则解析为其中一个实例,如果使用 InUserContextAttribute 属性修饰,则解析为另一个实例。
我正在努力实现的目标有可能吗?如何? ;)
【问题讨论】:
标签: constructor attributes autofac code-injection