尝试向我的WCF服务添加IoC支持

时间:2011-01-19 14:51:04

标签: c# wcf ioc-container

我正在尝试向我的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   服务发现信息。

3 个答案:

答案 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