在工厂中消除IoC容器耦合

时间:2014-02-05 21:01:46

标签: c# asp.net-mvc solr autofac solrnet

我在一个网络应用程序下运行多个多语言“网站”。每个“语言环境”都有自己的Solr核心(所有语言环境中的字段相同)。我正在使用SolrNet来执行搜索查询。

为了在每个请求的基础上切换核心,我在我的Autofac容器中为每个区域设置注册{em>名为的ISolrOperations<SearchResult>(对于那些不熟悉SolrNet的人来说,这是有效地进入库进入查询目的)。

然后我有一个ISearchService接口,具有一个具体的SolrSearchService实现,其构造函数签名如下所示:

public SolrSearchService(ISolrOperations<SearchResult> solr)

为了在每个请求上动态切换核心,我的控制器采用(注入)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与我的服务/控制器分离但保持每个请求切换核心的能力(基于控制器中执行的逻辑)而设法做的最好的。

然而,我对此仍然不满意,因为工厂本身仍与Autofac相连。有没有解决方法(从SolrNet或Autofac的角度来看)?

我已经看过使用Autofac的工厂代表,但似乎没有办法在这个实例中应用它们。

2 个答案:

答案 0 :(得分:1)

您可以使用键控/命名服务和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");

答案 1 :(得分:1)

我没有使用过Autofac,但根据温莎的做法以及我刚刚发现的你应该能够做到这样的事情

builder.Register(c => 
    new SolrSearchServiceFactory(HttpContext.Current.GetIdFromUrlOrSomething)
    )
    .As<ISearchServiceFactory>()
    .InstancePerHttpRequest();

如果您可以从http上下文获取Id,则autofac应为每个请求连接工厂。

/迈克尔