.net - Unity Container - 将动态 DbContext 依赖注入到服务中
问题描述
我正在构建一个.Net Web API,它使用带有实体框架的服务+存储库模式。每个控制器的 CRUD 操作将通过调用服务检索到的数据中继。
我有一个扩展 DbContext 的 SomeContext:
public class SomeContext : DbContext
{
public SomeContext(string connString) : base(connString) { }
// DbSets
...
}
使用接受 ISomeContext 的构造函数初始化服务:
public class Service : IService
{
public Service(ISomeContext ctx) : base(ctx)
{
_alpha = new AlphaRepository(ctx);
...
}
GetAllAlpha()
{
return _alpha.Get();
}
...
}
我想使用 (Unity Container) Dependency Injection 将 SomeContext 的一个实例注入到 Service 构造函数中。给定 SomeContext 的生命周期应该是 API 请求的持续时间。困难在于 SomeContext 的连接字符串是动态的,只有在作为 API 请求的一部分提供运行时参数“client”时才能知道。
此外,由于我的每租户数据库环境,存在不确定数量的客户端和连接字符串。因此,我不能只根据“客户端”参数注册n 个已知的 SomeContexts 和 Resolve()。
相反,一个内部开发的带有暴露 ContextFactory 的 NuGet 包让我可以为客户端检索适当的 SomeContext:
ContextFactory.GetClientContext(client);
如何以及在哪里配置 Unity Container 来管理这个动态的 SomeContext?
补充说明:
- Unity 已经将 IService 服务依赖注入到我的每个控制器操作中。因此,Service 构造函数在我创建的任何 Web API ActionFilters 之前执行。这意味着我无法在注射之前识别“客户”......我认为这意味着我需要使用工厂和/或委托......?
- 我已经阅读了有关将抽象工厂与 DbContext 的委托
public delegate IDbContext CreateDbContext(string client);
以及 NuGet 包的 GetClientContext 请求的适配器结合使用的信息,但我无法将它们全部拼凑成一个可行的解决方案。
谢谢你的帮助!
解决方案
我认为您不应该将上下文作为依赖项传递,上下文应该尽可能短,在每次使用后将其丢弃。以下是我的 IDataService 中的一些示例(在我的其他服务/外观中使用 dep inj 注入)
public DataService(IMapper mapper) : base(mapper) { }
...
public UserDto GetUser(string ADUser)
{
Func<UserDto> action = () =>
{
return GetUsers(u => u.UserName == ADUser && u.Active == true).SingleOrDefault();
};
return ExecutorHandler(action, true);
}
public IList<UserDto> GetUsers(bool runSafeMode = true)
{
Func<IList<UserDto>> action = () =>
{
return GetUsers(_ => true);
};
return ExecutorHandler(action, runSafeMode);
}
private IList<UserDto> GetUsers(Expression<Func<User, bool>> predicate, bool runSafeMode = true)
{
Func<IList<UserDto>> action = () =>
{
using (var ymse = YMSEntities.Create())
{
var users = ymse.User
.Include(u => u.UserUserProfile)
.Include(m => m.UserUserProfile.Select(uup => uup.UserProfile))
.Include(m => m.UserUserProfile.Select(uup => uup.User))
.Include(m => m.UserUserProfile.Select(uup => uup.UserProfile.UserProfileModule))
.Where(predicate).OrderBy(u => u.UserName).ToList();
return MappingEngine.Map<IList<UserDto>>(users);
}
};
return ExecutorHandler(action, runSafeMode);
}
protected T ExecutorHandler<T>(Func<T> action, bool runSafeMode)
{
if (runSafeMode)
return SafeExecutor(action);
return Executor(action);
}
protected T SafeExecutor<T>(Func<T> action, int retryCount = 2)
{
try
{
return action();
}
catch (SqlException sqlEx)
{
if (retryCount == ConfigService.GetConfig("SQLRetryCount", 1))
{
// reached maximum number of retries
throw;
}
...
我的 IDataService 有两种实现,一种用于在线模式,一种用于离线模式,在 Unity 中像这样设置(如果启用了 wifi,工厂决定使用哪一个,可能类似于使用不同的连接字符串注入正确的 DataService):
unityContainer.RegisterType<IDataService, OfflineDataService>("OfflineDataService", new ContainerControlledLifetimeManager(), new InjectionConstructor(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ServiceLocator.Current.GetInstance<IMapper>()));
unityContainer.RegisterType<IDataService, DataService>(new ContainerControlledLifetimeManager());
我建议在这里放置一个工厂方法来在运行时确定正确的客户端(将 Entities.Create() 替换为您注入的实体创建者工厂):
using (var ymse = YMSEntities.Create())
至于您对使用事务的评论,在我看来,数据层不应该关心事务,只关心处理许多实体的业务层。这是我在外观中的操作方式(多个数据服务调用):
public void SetTrailerWeightFull(Guid idShunter, Guid idTrailer, int? previousWeight, int weight, bool runSafeMode = true)
{
Action action = () =>
{
DataService.SetTrailerWeightFull(idTrailer, weight, runSafeMode);
AddTrailerEvent(TrailerEventTypeEnum.SCALE_1, idTrailer, previousWeight, weight, "Weight Full", string.Empty, string.Empty);
DataService.SetShunterWeightWeightFull(idShunter, idTrailer);
};
TransactionExecutor(action);
}
事务代码都在一个地方,所有人共享:
protected void TransactionExecutor(Action action, TransactionScopeAsyncFlowOption transactionScopeOption = TransactionScopeAsyncFlowOption.Suppress)
{
try
{
using (var scope = new TransactionScope(transactionScopeOption))
{
action();
scope.Complete();
}
}
catch (Exception ex)
{
LogErrorEvent(ex);
throw;
}
}
推荐阅读
- python - Django makemigrations - 没有安装带有标签的应用程序'
' - c# - 通过选项自定义 JsonConverter 的不同输出?
- c++ - Boost Spirit X3解析语法a^(2n+1)
- whatsapp - whatsapp-web api(pedroslopez)触发词不起作用
- python - 获取卷积算法失败。这可能是因为 cuDNN 未能初始化 Flask 框架
- python - 为什么 PyCharm 在字符串后面加空格?
- reactjs - webpack5 不从 .json 文件导入内容?
- python - 如何创建 PLY yacc 读取数组?
- python-3.x - Why does merging unequally matched rows not work on local dataset?
- swiftui - SwiftUI .swipeactions 和核心数据