我正在尝试向我的REST WCF服务(Windows Server 2008)添加IoC支持。我是新手,并且遵循以下视频中提供的说明:
http://www.dimecasts.net/Content/WatchEpisode/150
视频介绍了许多类,这些类可以帮助我在暴露WCF端点的同时启动和运行StructureMap的IoC。我在本文末尾发布了所有代码。
当我运行我的代码时,自定义类StructureMapServiceHost会抛出一个错误@ StructureMapServiceHost(类型serviceType,params Uri [] baseAddress)方法:
public class StructureMapServiceHost : ServiceHost
{
public StructureMapServiceHost() {}
public StructureMapServiceHost(Type serviceType, params Uri[] baseAddress)
: base(serviceType, baseAddress)
{
}
protected override void OnOpening()
{
Description.Behaviors.Add( new IoCServiceBehavior());
base.OnOpening();
}
}
我被告知:
提供的服务类型无法作为服务加载,因为它没有默认(无参数)构造函数。要解决此问题,请为该类型添加默认构造函数,或将该类型的实例传递给主机。
这是事实,但事实并非如此。但视频示例也没有。以下是我的服务:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class UserService : IUserService
{
public UserService(IUserRepository specification)
{
Specification = specification;
}
public List<User> GetAllUsers()
{
return Specification.GetAllUsers();
}
public User GetUser(string userId)
{
return Specification.GetUserById(new Guid(userId));
}
private List<User> SearchForUsers(string searchString)
{
return Specification.SearchUsers(searchString);
}
public IUserRepository Specification { get; set; }
}
public class IoCServiceBehavior : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
new StructureMapInstanceProvider(serviceDescription.ServiceType);
}
}
}
public class StructureMapInstanceProvider : IInstanceProvider
{
private readonly Type _serviceType;
public StructureMapInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
var instance = ObjectFactory.GetInstance(_serviceType);
return instance;
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
throw new NotImplementedException();
}
}
public class StructureMapServiceHostFactory : ServiceHostFactory
{
public StructureMapServiceHostFactory()
{
IoCBootstrap.SetupIoc();
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new StructureMapServiceHost(serviceType, baseAddresses);
}
}
有什么想法吗?感谢。
编辑 * ** * ** * ** * ** * ** * ** * ** * ** * ** * ****
从StructureMapServiceHost我删除了:
public StructureMapServiceHost(Type serviceType, params Uri[] baseAddress)
: base(serviceType, baseAddress) { }
并补充道:
public StructureMapServiceHost(Object singletonInstance, params Uri[] baseAddress)
: base( singletonInstance, baseAddress) { }
然后从我的UserService构造函数中删除参数。我没有收到错误:
HTML文档不包含Web 服务发现信息。
答案 0 :(得分:17)
您的服务使用InstanceContextMode.SingleCall,WCF团队以其无限的智慧决定,当InstanceContextMode为SingleCall时,不会调用IInstanceProvider来创建实例(请参阅http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/31/wcf-extensibility-iinstanceprovider.aspx - 第二个参数declration下面的标题)。
目前我在服务主机工厂中有一种不太理想的解决方法:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using StructureMap;
using StructureMap.Pipeline;
using System.Linq;
using ServiceHostCreator = System.Func<System.Type, System.Uri[], System.ServiceModel.ServiceHost>;
namespace x.ServiceExtensions
{
public class xWebServiceHostFactory : ServiceHostFactory
{
private readonly IDictionary<InstanceContextMode, ServiceHostCreator> _serviceHostCreators;
public xWebServiceHostFactory()
{
ObjectFactory.Initialize( init =>
init.Scan( scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.IgnoreStructureMapAttributes();
scan.LookForRegistries();
} ) );
_serviceHostCreators = new Dictionary<InstanceContextMode, ServiceHostCreator>
{
{ InstanceContextMode.PerCall, ( t, a ) => PerCallServiceHostCreator( t, a ) },
{ InstanceContextMode.PerSession, ( t, a ) => PerSessionServiceHostCreator( t, a ) },
{ InstanceContextMode.Single, ( t, a ) => SingleInstanceServiceHostCreator( t, a ) }
};
}
protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses )
{
var serviceInstanceContextMode = GetServiceInstanceContextMode( serviceType );
var serviceHostCreator = _serviceHostCreators[ serviceInstanceContextMode ];
return serviceHostCreator( serviceType, baseAddresses );
}
private static InstanceContextMode GetServiceInstanceContextMode( Type serviceType )
{
var serviceBehaviour = serviceType
.GetCustomAttributes( typeof ( ServiceBehaviorAttribute ), true )
.Cast<ServiceBehaviorAttribute>()
.SingleOrDefault();
return serviceBehaviour.InstanceContextMode;
}
private static ServiceHost PerCallServiceHostCreator( Type serviceType, Uri[] baseAddresses )
{
var args = new ExplicitArguments();
args.Set( serviceType );
args.Set( baseAddresses );
var serviceHost = ObjectFactory.GetInstance<TelaWebServiceHost>( args );
return serviceHost;
}
private static ServiceHost PerSessionServiceHostCreator( Type serviceType, Uri[] baseAddresses )
{
return PerCallServiceHostCreator( serviceType, baseAddresses );
}
private static ServiceHost SingleInstanceServiceHostCreator( Type serviceType, Uri[] baseAddresses )
{
var service = ObjectFactory.GetInstance( serviceType );
var args = new ExplicitArguments();
args.Set( typeof(object), service );
args.Set( baseAddresses );
var serviceHost = ObjectFactory.GetInstance<TelaWebServiceHost>( args );
return serviceHost;
}
}
}
这是一项正在进行的工作,可能有更好的方法,但目前我找不到。
答案 1 :(得分:0)
我现在无法看一下视频(互联网限制),但我很确定他们示例中的类没有任何构造函数 。在这种情况下,编译器代表您生成一个空的无参数构造函数。因此,他们的班级确实毕竟有一个默认的构造函数。
就异常而言,这似乎非常简单:您的无参数构造函数不会初始化Specification
属性,因此始终为null
- 这自然会导致NullReferenceException
一旦你尝试在你的方法中访问它。
您在此处的目的似乎是自己创建UserService
对象并将IUserRepository
传递给它,不是吗? (或者,也许,使用你的IoC框架?)
在这种情况下,您最好使用ServiceHost
的构造函数的重载,该构造函数需要object
而不是Type
。这样,您就可以完全控制UserService
对象,而根本不需要默认构造函数。
答案 2 :(得分:0)
对于那些尝试做类似事情的人,我目前强烈建议使用Spring.NET作为IoC容器。虽然它可能不像其他容器那样容易使用(我不是特别喜欢它的XML配置),但它具有迄今为止最好的WCF集成。它还为InstanceContextMode.SingleCall问题提供了一个聪明而透明的解决方法(使用其AOP /动态代理框架)。
http://www.springframework.net/docs/1.2.0-M1/reference/html/wcf.html
http://www.springframework.net/doc-latest/reference/html/wcf-quickstart.html