Autofac Wcf - 根据SOAP请求中的数据注入服务

时间:2018-04-04 14:27:13

标签: c# wcf dependency-injection autofac

我有一个WCF服务,其中包含以下操作合同:

[OperationContract]
Response SearchEntities(Query query);

此操作接受包含指定实体的请求,如下所示:

[DataContract]
public class Query
{
    [DataMember]
    public string SearchTerm { get; set; }

    [DataMember]
    public string Entity { get; set; }

    [DataMember]
    public bool ExactMatch { get; set; }
}

根据Entity属性中包含的值,在此响应中填充以下属性:

[DataContract]
public class Response
{
    [DataMember]
    public List<Asset> Assets { get; set; }

    [DataMember]
    public List<Stage> Stages { get; set; }

    [DataMember]
    public List<Sector> Sectors { get; set; }
}

糟糕的设计,我知道!然而。我使用Autofac.Wcf作为我的服务工厂来注入依赖项。通常我会使用一个通用的接口和泛型来确定一个基于Entity值的服务,如下所示:

public interface IEntitySearch<T>
{
    Response Search(Query query);
}

上述界面将为响应中的每个列表提供多个实现。使用诸如服务位置之类的设计模式,我可以确定要使用哪个服务(所有服务都继承自IEntitySearch<T>,类似于:

public IEntitySearch ResolveSearcher(Query query)
{
    switch(query.Entity)
    {
        case "Assets":
            return _container.Resolve<AssetSearch>();
        case "Stages":
            return _container.Resolve<StageSearch>();
        default:
            throw new NotSupportedException();
    }
}

虽然这有效,但更优雅的解决方案(我相信)将根据请求中包含的数据,针对此特定操作的每个请求自定义Autofac容器。

IE:在WCF管道将请求发送到服务实现之前,是否可以检查请求数据并自定义容器如何解析依赖项。这样我就可以避免在服务层中暴露依赖项解析。

这可能吗?

如果Autofac以外的另一个DI库有解决方案,我很乐意改变我们的DI框架。

感谢。

2 个答案:

答案 0 :(得分:0)

我没有亲自尝试过这个,但我认为你可以采取的方向是:

通过查看DefaultServiceImplementationProvider - 默认情况下在Autofac WCF托管中工作的MultitenantServiceImplementationDataProvider,您可以看到IServiceImplementationDataProvider的两个示例;和Autofac.Multitenant.Wcf,更多的是关于生成代理以启用多租户WCF托管。

虽然这些都没有使用OperationContext.Current来确定实际的支持服务,但您可以建立在这些想法的基础上:

  • 查看Autofac WCF实施。您可以按原样使用它。实例数据提供者的要点是WCF抓住了托管服务的具体类型,如果您尝试从其下面交换类型,则会出现错误。多租户支持通过创建代理类型来欺骗WCF,并且您的实现类型可以在代理下交换出来。请注意,MultitenantServiceImplementationDataProvider实际上并未将任何内容绑定到租户或租户ID;它仅与该代理有关。
  • 在您的.svc文件中指定服务接口而不是任何单独的具体实现,因为您将交换实现。
  • 使用lambda注册来确定您的实施。
  • 确保您的服务为InstanceContextMode.PerCall,以确保按照请求换出内容。

注册可能如下所示:

builder.Register(ctx => {
  var context = OperationContext.Current;
  var type = DetermineTypeFromContext(context);
  return ctx.Resolve(type);
}).As<IMyServiceInterface>();

WCF上的Autofac Multitenant和{{3}}部分也可以提供帮助。

答案 1 :(得分:0)

在我看来,你正试图将你的问题转移到另一个地方。为什么根据低级别WCF的请求做出决策比切换SearchEntities方法更好?这更糟糕了;-)

我会考虑使用IEntitySearch工厂/提供商e.q。IEntitySearchProvider(这不是更好,但总是如此)。

public interface IEntitySearch
{
    bool IsMatchQuery(Query query);
    Response Search(Query query);
}

// without service locator
public class EntitySearchProvider : IEntitySearchProvider
{
    private readonly IEnumerable<IEntitySearch> _searchers;

    public EntitySearchProvider(IEnumerable<IEntitySearch> searchers)
    {
        _searchers = searchers;
    }

    public IEntitySearch GetSearcher(Query query)
    {
        // last registered 
        return _searchers.LastOrDefault(i=>i.IsMatchQuery(query))
            ??  throw new NotSupportedException();
    }
}

public interface IEntitySearchProvider
{
    IEntitySearch GetSearcher(Query query);
}

public class EntitySearchProvider : IEntitySearchProvider
{
    private readonly IComponentContext _container;

    public EntitySearchProvider(IComponentContext container)
    {
        _container = container;
    }

    public IEntitySearch GetSearcher(Query query)
    {
        switch(query.Entity)
        {
            case "Assets":
                return _container.Resolve<AssetSearch>();
            case "Stages":
                return _container.Resolve<StageSearch>();
            default:
                throw new NotSupportedException();
        }
    }
}

public class WcfService
{
    private readonly IEntitySearchProvider _provider;

    public WcfService(IEntitySearchProvider provider)
    {
        _provider = provider;
    }

    public Response SearchEntities(Query query)
    {
        var searcher = _provider.GetSearcher(query);
        return searcher.Search(query);
    }
}