我的web api 2应该为客户端提供两个url: test.mydomain.com和production.mydomain.com当客户端访问测试时,我想为生产url实例化测试数据库上下文和生产数据库。
目前我可以手动实例化两者:在控制器内的每个动作上,我可以检查url并确定是否这个,加载这个,如果那个加载那个。但是,我想自动设置db,而不检查每个动作的请求url。我试着在控制器构造函数中读取请求上下文,但在那个阶段它不存在。
操作过滤器吗?OnActionExecuting吗?但是我如何在过滤器中实例化它并在控制器动作中使用它呢?还有其他建议吗?
正如Erik所指出的那样,使用依赖注入可以相当容易地解决这个问题。
- 将上下文的创建封装在工厂后面,工厂负责基于url参数创建上下文
- 创建一个删除处理程序来检查请求,并使用上下文工厂基于当前url实例化一个新的上下文
- 将上下文缓存到缓存对象中,其生存期范围为当前api请求
- 使用上下文缓存对象来检索存在于api请求生命周期范围内的所有类型的url特定上下文
下面是一个使用AutoFac的例子:
public interface IContextCache
{
DbContext CurrentContext { get; set; }
}
// This will be wired as instance per api request
// and hence return the same context across the entire
// request from handler to controller
public class ContextCache : IContextCache
{
public DbContext CurrentContext { get; set; }
}
public interface IContextFactory
{
DbContext Create(Uri requestUri);
}
public class ContextFactory : IContextFactory
{
public DbContext Create(Uri requestUri)
{
return CreateContextBasedOnUri(requestUri);
}
}
// This is fired before the controller and creates and caches
// as per url context
public class ContextHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var dependencyScope = request.GetDependencyScope();
var contextFactory = (IContextFactory)dependencyScope.GetService(typeof(IContextFactory));
var contextCache = (IContextFache)dependencyScope.GetService(typeof(IContextCache));
var context = contextFactory.Create(request.RequestUri);
contextCache.CurrentContext = context;
return base.SendAsync(request, cancellationToken);
}
}
// Uses the per api request scoped cache to retrieve the
// correct context
public class MyController : ApiController
{
public MyController(IContextCache contextCache)
{
var context = contextCache.CurrentContext;
}
}
// AutoFac wiring
// Perform boiler plate AutoFac registration for WebApi
// (See AutoFac documentation)
// Custom registrations
builder.RegisterType<ContextCache>()
.As<IContextCache>()
.InstancePerApiRequest();
builder.RegisterType<ContextFactoy>()
.As<IContextFactory>()
.InstancePerDependency();
// Handler registration
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new ContextHandler());
}