我在一个 Web 应用程序下运行多个多语言“网站”。每个“语言环境”都有自己的 Solr 核心(在所有语言环境中具有相同的字段)。我正在使用 SolrNet 执行搜索查询。
为了根据每个请求切换核心,我注册了 ISolrOperations<SearchResult>
的命名实例在每个语言环境的 Autofac 容器中(对于那些不熟悉 SolrNet 的人来说,这实际上是我出于查询目的进入库的入口点)。
然后我有一个ISearchService
界面,具有具体SolrSearchService
实现,其构造函数签名如下所示:
public SolrSearchService(ISolrOperations<SearchResult> solr)
为了根据每个请求动态切换核心,我的 Controller 采用(注入(inject))ISearchServiceFactory
而不是简单地 ISearchService
。我的SolrSearchServiceFactory
看起来像这样:
public class SolrSearchServiceFactory : ISearchServiceFactory
{
private readonly IComponentContext _ctx;
public SolrSearchServiceFactory(IComponentContext ctx)
{
_ctx = ctx;
}
public ISearchService Create(string id)
{
var result = _ctx.ResolveNamed<ISolrOperations<SearchResult>>(id);
return new SolrSearchService(result);
}
}
id
只是区域设置标识符。为了将 Autofac 与我的服务/ Controller 解耦,但保持每个请求切换核心的能力(基于 Controller 中执行的逻辑),这是我所能做到的最好的结果。
但是,我对此仍然不太满意,因为工厂本身仍然与 Autofac 耦合。有没有办法解决这个问题(从 SolrNet 或 Autofac 的角度来看)?
我已经考虑过使用 Autofac 的工厂委托(delegate),但似乎没有办法在这种情况下应用它们。
最佳答案
您可以使用键控/命名服务和 IIndex 来完成此操作,autofac 页面 here 上有一个很好的条目.
这是一个简单的示例:
public class Factory
{
private readonly IIndex<string, ISearchService> _index;
public Factory(IIndex<string, ISearchService> index)
{
_index = index;
}
public ISearchService Resolve(string id)
{
return _index[id];
}
}
// Registrations
var builder = new ContainerBuilder();
builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A");
builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B");
builder.Register<Factory>(c => new Factory(c.Resolve<IIndex<string, ISearchService>>()));
// as per autofac's page linked above, autofac automatically implements IIndex types
// Usage
var aSearchService = fact.Resolve("A");
编辑:我认为我们可以做得更好,所以这里有一些扩展方法来避免创建显式工厂类:
public delegate TReturn AutoFactoryDelegate<TParam, TReturn>(TParam param);
public class AutoFactory<TParam, TReturn>
{
private readonly IIndex<TParam, TReturn> _index;
public AutoFactory(IIndex<TParam, TReturn> index)
{
_index = index;
}
public TReturn Resolve(TParam param)
{
return _index[param];
}
}
public delegate TReturn AutoFactoryDelegate<TParam, TReturn>(TParam param);
public static class AutofacExtensions
{
public static void RegisterAutoFactoryDelegate<TParam, TReturn>(this ContainerBuilder builder)
{
builder.Register(c => new AutoFactory<TParam, TReturn>(c.Resolve<IIndex<TParam, TReturn>>()));
builder.Register<AutoFactoryDelegate<TParam, TReturn>>(c =>
{
var fact = c.Resolve<AutoFactory<TParam, TReturn>>();
return fact.Resolve;
});
}
}
// Registration
var builder = new ContainerBuilder();
builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A");
builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B");
builder.RegisterAutoFactoryDelegate<string, ISearchService>();
// Usage
// fact is an AutoFactoryDelegate<string, ISearchService>
var aSearchService = fact("A");
编辑2:再次查看后,我们不需要工厂类,因为 IIndex 实际上已经是工厂了。这使得实现方面变得相当简单:
public static void RegisterAutoFactoryDelegate<TParam, TReturn>(this ContainerBuilder builder)
{
builder.Register(c =>
{
var iindex = c.Resolve<IIndex<TParam, TReturn>>();
return (Func<TParam, TReturn>) (id => iindex[id]);
});
}
// Registration
var builder = new ContainerBuilder();
builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A");
builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B");
builder.RegisterAutoFactoryDelegate<string, ISearchService>();
// Usage
// fact is an Func<string, ISearchService>
var aSearchService = fact("A");
关于c# - 消除工厂中的 IoC 容器耦合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21588426/