我有一个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框架。
感谢。
答案 0 :(得分:0)
我没有亲自尝试过这个,但我认为你可以采取的方向是:
OperationContext.Current
获取当前请求消息数据。IServiceImplementationDataProvider
for Autofac,告知Autofac要为该请求托管哪个WCF接口。OperationContext.Current
的支持服务。通过查看DefaultServiceImplementationProvider
- 默认情况下在Autofac WCF托管中工作的MultitenantServiceImplementationDataProvider
,您可以看到IServiceImplementationDataProvider
的两个示例;和Autofac.Multitenant.Wcf,更多的是关于生成代理以启用多租户WCF托管。
虽然这些都没有使用OperationContext.Current
来确定实际的支持服务,但您可以建立在这些想法的基础上:
MultitenantServiceImplementationDataProvider
实际上并未将任何内容绑定到租户或租户ID;它仅与该代理有关。.svc
文件中指定服务接口而不是任何单独的具体实现,因为您将交换实现。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);
}
}