Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to add IoC support to my WCF Services

I am trying to add IoC support to my REST WCF service (Windows Server 2008). I am new to this and am following the instructions provided in the below video:

http://www.dimecasts.net/Content/WatchEpisode/150

The video walks through a number of classes that help me get StructureMap's IoC up and running while exposing WCF endpoints. I have posted all of the code at the end of this post.

When I run my code, the custom class StructureMapServiceHost throws an error @ the StructureMapServiceHost(Type serviceType, params Uri[] baseAddress ) method:

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

I am being told that:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

This is true, it doesn't. But the video example didn't have one either. Below is my service:

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

Any ideas? Thanks.

EDIT ********************************

From StructureMapServiceHost I removed:

public StructureMapServiceHost(Type serviceType, params Uri[] baseAddress)
            : base(serviceType, baseAddress) { }

And added:

public StructureMapServiceHost(Object singletonInstance, params Uri[] baseAddress)
            : base( singletonInstance, baseAddress) { }

And then removed the parameter from my UserService constructor. I am not getting the error:

The HTML document does not contain Web service discovery information.

like image 542
Peter Avatar asked Jan 19 '11 14:01

Peter


1 Answers

Your service uses InstanceContextMode.SingleCall and the WCF team have decided, in their infinite wisdom, that when the InstanceContextMode is SingleCall the IInstanceProvider is not invoked to create the instance (See https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-iinstanceprovider - second para below Interface declration heading).

Currrently I have a less than ideal way of getting around that in the service host factory:

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

This is a work in progress and there may be a better way, but at the moment I can't find one.

like image 192
Neal Avatar answered Oct 15 '22 06:10

Neal