我正在构建一个.Net Web Api,它使用带有实体框架的服务/存储库模式。 下面是具有与服务接口的 CRUD 操作的控制器:
public class SomeController : BaseApiController
{
private IService _service;
public SomeController(IService _service)
{
_service = service;
}
public object Get() { return _service.GetItems(); }
...
}
我想使用 Unity IoC Microsoft将数据库上下文注入到下面的服务构造函数中:
// Service implements IService
public Service(SomeContext ctx) : base(ctx)
{
_alpha = new AlphaRepository(ctx);
_bravo = new BravoRepository(ctx);
}
这对我来说适用于单个静态 DbContext。 但是,API 必须使用动态 DbContext b/c,服务器和数据库在发出请求并通过查询字符串传递各种配置数据之前是未知的,例如 ?client=Client&property=Property。 每个客户端都有自己的数据库,每个数据库位于两个服务器之一上。
有一个内部开发的 NuGet 包,带有公开的 ContextFactory,可以在发出请求时调用它来检索相应的 DbContext:
ContextFactory.GetSomeContext(client, prop);
起初,我想在 BaseController 上使用 ActionFilter 来解析 HTTPActionContext Request 的客户端和属性键查询字符串。 使用此信息,可以检索上下文并将其注册到 Unity 容器:
// Belongs to 'public class SomeFilterAttribute : ActionFilterAttribute'
public override void OnActionExecuting(HttpActionContext actionContext)
{
var someContext = ContextFactory.GetSomeContext(client, prop);
private IUnityContainer _unityContainer;
_unityContainer = (IUnityContainer)actionContext.Request.GetDependencyScope().GetService(typeof(IUnityContainer));
_unityContainer.RegisterInstance<SomeContext>(someContext, new PerThreadLifetimeManager());
...
}
当我意识到ActionFilter是在初始化SomeController之后执行的,因此它的构造函数和服务构造函数已经完成执行并且注册DbContext为时已晚时,这个计划就失败了。
问题: 检索动态 DbContext 并使用 Unity 将此实例注入服务构造函数的最佳方法是什么?
我读过关于使用委托的信息,例如
public delegate ISomeContext CreateSomeContext(string client, string prop);
然后在 UnityConfig 中.cs RegisterComponents((
container.RegisterInstance<CreateSomeContext>((c, p) => ContextFactory.GetSomeContext(c, p));
但我不确定如何在运行时从查询字符串实际提供客户端和属性。
感谢您的任何帮助!
这听起来像是你想要使用每个租户的数据库多租户结构。第一件事是避免在查询字符串上传递租户标识符 (ClientID(。相反,我建议阅读 OAuth 和 Owin,以在身份验证令牌中嵌入客户端标识(甚至连接字符串(。我在应用程序中使用每租户数据库结构,其工作原理大致如下:
-
用户访问网站并被定向到登录名。
-
身份验证服务与管理所有租户的中央数据库通信。此数据库充当中心,用于控制租户存储的服务器/数据库及其数据库版本。(在升级转换期间定向到受版本控制的应用服务器(
-
身份验证成功后,将使用租户 ID、架构名称(数据库名称(和连接字符串加载 OAuth 令牌。这存储为加密声明。
-
数据服务器请求使用 Strategy 模式检索 Owin 会话上下文以检索令牌,并根据身份验证服务对其进行验证。举个例子:
TenantConnectionModel ITenantIdentityStrategy.RetrieveTenant() { if (_tenant != null) // Cached copy... return _tenant; var context = HttpContext.Current.GetOwinContext(); var eMailAddress = context.Authentication.User.Identity.Name; var tenantIdClaim = context.Authentication.User.Claims.SingleOrDefault(x => x.Type == "YourNamespace.TenantId"); var schemaClaim = context.Authentication.User.Claims.SingleOrDefault(x => x.Type == "YourNamespace.DBName"); var connectionStringClaim = context.Authentication.User.Claims.SingleOrDefault(x => x.Type == "YourNamespace.ConnectionString"); if (tenantIdClaim == null || schemaClaim == null || connectionStringClaim == null) return null; // TODO example: Call the auth service here with the e-mail address and tenant ID // to validate that the current user has logged in and session hasn't timed // out. _tenant = new TenantConnectionModel { TenantId = long.Parse(tenantIdClaim.Value), SchemaName = schemaClaim.Value, ConnectionString = connectionStringClaim.Value, }; return _tenant; }
-
身份验证检查成功后,它会提取租户详细信息,包括连接字符串。
-
IOC 容器在构造 DbContext 时使用委托方法,该方法检索租户标识策略(第 3 部分(并获取要提供给上下文的连接字符串。该策略可以在解析 DbContext 时缓存多个调用的租户详细信息,其生存期范围应为每个请求。
我通常使用 DbContextScope 工作单元模式,并调整了它的分支以适应每租户数据库或每租户架构环境的多租户环境。随意看看@https://github.com/StevePy/DbContextScope