简体   繁体   English

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

[英]Autofac Wcf - Inject service depending on data within SOAP Request

I have a WCF Service with the following operation contract: 我有一个带有以下操作合同的WCF服务:

[OperationContract]
Response SearchEntities(Query query);

This operation takes a request that contains a specified Entity like so: 该操作接受一个包含指定实体的请求,如下所示:

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

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

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

Based on the value contained within the Entity property, one the following properties is populated within this response: 根据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; }
}

Terrible design, I know! 糟糕的设计,我知道! However. 然而。 I am using Autofac.Wcf as my service factory to inject dependencies. 我使用Autofac.Wcf作为服务工厂来注入依赖项。 Normally I would use a common Interface and Generics to determine a service to use based on the Entity value like so: 通常,我将使用通用的接口和泛型来基于Entity值确定要使用的服务,如下所示:

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

The above interface would have several implementations for each of the Lists within the response. 对于响应中的每个列表,上述接口都有几种实现。 Using a design pattern such as a service location I could determine which service to use (all of which inherit from IEntitySearch<T> , something like: 使用服务位置等设计模式,我可以确定要使用的服务(所有服务都继承自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();
    }
}

While this works, a more elegant solution (I believe) would be to customize the Autofac container per request for this particular operation, depending on the data contained within the request. 在这种情况下,一个更优雅的解决方案(我相信)是针对此特定操作的每个请求自定义Autofac容器,具体取决于请求中包含的数据。

IE: Before the WCF pipe line sends the request to the service implementation, is it possible to examine the request data and customize how the container resolves dependencies. IE:在WCF管道将请求发送到服务实现之前,可以检查请求数据并自定义容器如何解析依赖项。 That way I can avoid exposing dependency resolution within my service layer. 这样,我可以避免在服务层中公开依赖项解析。

Is this possible? 这可能吗?

If another DI library other than Autofac has a solution for this, I will happily change our DI framework. 如果Autofac以外的其他DI库对此有解决方案,我将很高兴更改我们的DI框架。

Thanks. 谢谢。

I haven't personally tried this but I think a direction you can go down is to combine: 我还没有亲自尝试过,但是我认为您可以选择的方向是结合起来:

You can see two examples of the IServiceImplementationDataProvider by looking at the DefaultServiceImplementationProvider - the one that works in Autofac WCF hosting by default; 通过查看DefaultServiceImplementationProvider可以看到IServiceImplementationDataProvider的两个示例-默认情况下,该示例可在Autofac WCF托管中使用。 and MultitenantServiceImplementationDataProvider , which is more about generating a proxy to enable multitenant WCF hosting. MultitenantServiceImplementationDataProvider ,更多有关生成代理以启用多租户WCF托管。

While neither of these use OperationContext.Current to determine the actual backing service, you can build on the ideas: 虽然这两个都不使用OperationContext.Current来确定实际的支持服务,但是您可以基于以下思想:

  • Look at the Autofac.Multitenant.Wcf implementation. 查看Autofac.Multitenant.Wcf实现。 You may be able to use it as-is. 您可能可以原样使用它。 The point of the instance data provider there is that WCF grabs on to the concrete type of the service being hosted and if you try to swap types out from under it, you get errors. 实例数据提供程序的关键是WCF抓住了所托管服务的具体类型 ,如果尝试从其下交换类型,则会出错。 The multitenant support fools WCF by creating a proxy type and your implementation type can be swapped out under the proxy. 多租户支持通过创建代理类型来愚弄WCF,您的实现类型可以在代理下换出。 Note the MultitenantServiceImplementationDataProvider doesn't actually tie anything to a tenant or tenant ID; 请注意, MultitenantServiceImplementationDataProvider实际上并未将任何内容绑定到租户或租户ID。 it's only about that proxy. 只是关于那个代理。
  • In your .svc file specify a service interface rather than any individual concrete implementation since you'll be swapping out the implementation. 在您的.svc文件中,指定服务接口而不是任何具体的实现,因为您将换出实现。
  • Use a lambda registration to figure out your implementation. 使用lambda注册来确定您的实现。
  • Make sure your service is InstanceContextMode.PerCall to ensure things get swapped out on a per request basis. 确保您的服务是InstanceContextMode.PerCall以确保在每个请求的基础上交换出东西。

The registration might look something like this: 注册可能看起来像这样:

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

The Autofac WCF and Autofac Multitenant section on WCF may also help. WCF上Autofac WCFAutofac Multitenant部分也可能会有所帮助。

In my opinion you're trying to move your problem just to another place. 我认为您正在尝试将问题转移到另一个地方。 Why would making decision based on request at low-level WCF is better than switch in SearchEntities method? 为什么在低级WCF上基于请求进行决策比在SearchEntities方法中进行切换更好? It's much worse ;-) 更糟糕的是;-)

I would consider to use IEntitySearch factory/provider eq IEntitySearchProvider (it's not so much better but always). 我会考虑使用IEntitySearch工厂/提供程序eq 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();
    }
}

or 要么

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();
        }
    }
}

with

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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM