2

I am trying to pass an object to the WCF service during creation - 'MasOperationsService'. But I encounter an error and am unable to figure out why.

I am trying this piece of code from here...

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class MasOperationsService : IMasOperations
{
    public MasOperationsService()
        : this("INVALID")  
    {  
        throw new InvalidOperationException("This should never be called");  
    }  

    public MasOperationsService(string name)
    {

    }

    //public CoAuthorSearchResult ExtractCoAuthorsFromAuthor(long AuthorCellId, uint LevelsToExtract)
    //{
    //    //throw new NotImplementedException("Running This At Proxy, This should now query Slaves!!");
    //    return new CoAuthorSearchResult();
    //}
}

public class MyInstanceProvider : IInstanceProvider
{
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        string name = message.Headers.GetHeader<string>("Name", "http://my.namespace");
        if (name != null)
        {
            return new MasOperationsService("Service " + name);
        }
        else
        {
            return new MasOperationsService("Service with no name");
        }
    }
    public object GetInstance(InstanceContext instanceContext)
    {
        return new MasOperationsService("Service with no name");
    }
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}
public class MyServiceBehavior : IServiceBehavior
{
    MyInstanceProvider myProvider = new MyInstanceProvider();
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceProvider = this.myProvider;
            }
        }
    }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}

The MasOperationsService() is the service class. The Client code is LaunchWcfService()

public void LaunchWcfService()
    {
        string baseAddress = "http://localhost:8733/Design_Time_Addresses/MASService/Service1";
        ServiceHost host = new ServiceHost(typeof(MasOperationsService), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(IMasOperations), GetBinding(), "");
        host.Description.Behaviors.Add(new MyServiceBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<IMasOperations> factory = new ChannelFactory<IMasOperations>(GetBinding(), new EndpointAddress(baseAddress));
        IMasOperations proxy = factory.CreateChannel();

        using (OperationContextScope scope = new OperationContextScope((IContextChannel)proxy))
        {
            OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Name", "http://my.namespace", "Name 1"));
            //Console.WriteLine(proxy.Hello("foo"));
            OperationContext.Current.OutgoingMessageHeaders.RemoveAll("Name", "http://my.namespace");
            OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Name", "http://my.namespace", "Name 2"));
            //Console.WriteLine(proxy.Hello("bar"));
        }

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();  
    }

    static Binding GetBinding()
    {
        BasicHttpBinding result = new BasicHttpBinding();
        return result;
    }
ShaQ.Blogs
  • 1,264
  • 4
  • 17
  • 30
  • I'm not sure what you're trying to do but if the finality of all this is to have the values of your custom headers available inside any service implementation method, you're maybe better off using a base class for your methods input parameters using `MessageContract` instead of `Datacontract`. – Paciv Aug 23 '12 at 15:41

2 Answers2

0

Your problem is that you set InstanceContextMode = InstanceContextMode.Single. This mode specify to use a single service instance across all service requests. In this case, the WCF framework instanciate that singleton instance of your service at the moment you instantiate the ServiceHost, well before you could even try to plug your custom InstanceProvider and will use this same instance for all subsequent requests.

I'm not sure what you're trying to do but if the finality of all this is to have the values of your custom headers available inside any service implementation method, you're maybe better off using a base class for your methods input parameters using MessageContract instead of Datacontract.

[ServiceContract]
public interface IMasOperations
{
    [OperationContract]
    CoAuthorSearchResult ExtractCoAuthorsFromAuthor(ExtractCoAuthorsFromAuthorRequest request);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MasOperationsService : IMasOperations
{    
    public CoAuthorSearchResult  ExtractCoAuthorsFromAuthor(ExtractCoAuthorsFromAuthorRequest request)
    {
        Console.WriteLine("Name header accessed inside WS implementation though request.Name. Value = {0}.", request.Name);
        return new CoAuthorSearchResult();
    }
}

[MessageContract]
public class BaseRequest
{
    [MessageHeader]
    public string Name { get; set; }
}

[MessageContract]
public class ExtractCoAuthorsFromAuthorRequest : BaseRequest
{
    [MessageBodyMember]
     public long AuthorCellId { get; set; }

    [MessageBodyMember]
    public uint LevelsToExtract { get; set; }
}

[MessageContract]
public class CoAuthorSearchResult { }

public class Program
{
    static readonly Binding _binding = new BasicHttpBinding();

    public static void Main(string[] args)
    {
        string baseAddress = "http://localhost:8733/Design_Time_Addresses/MASService/Service1";
        ServiceHost host = new ServiceHost(typeof(MasOperationsService), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(IMasOperations), _binding, string.Empty);
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<IMasOperations> factory = new ChannelFactory<IMasOperations>(_binding, new EndpointAddress(baseAddress));
        IMasOperations channel = factory.CreateChannel();

        CoAuthorSearchResult result = channel.ExtractCoAuthorsFromAuthor(new ExtractCoAuthorsFromAuthorRequest
        {
            Name = "http://my.namespace",
            AuthorCellId = 0,
            LevelsToExtract = 1,
        });


        ICommunicationObject o = channel as ICommunicationObject;
        if (o != null)
        {
            if (o.State == CommunicationState.Opened)
            {
                o.Close();
            }
            else
            {
                o.Abort();
            }
        }

        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();  
    }
}
Paciv
  • 1,487
  • 10
  • 16
  • What I am trying to achieve is simple. I have a command line app - MyCmdApp. MyCmdApp will listen to client requests using the WCF service. This app will then delegate client requests to another app called MyServerApp. The WCF service *MUST* delegate calls from clients using a class called MyProxy. MyProxy needs to be kickstarted when MyCmdApp is launched. I need to create the application in such a manner that, the WCF should have a reference to the MyProxy instance which was kickstarted. – ShaQ.Blogs Aug 23 '12 at 19:04
  • Your Name parameter is the kickstart parameter right ? If so it is available inside MyCmdApp, be it server side or host / first call side, you don't need all this just do the kickstart inside the service. – Paciv Aug 24 '12 at 09:06
-1

By default, WCF uses parameterless constructor. In order to use another constructor you should implement IInstanceProvider interface.

You can refer to the following question.

How do I pass values to the constructor on my wcf service?

Community
  • 1
  • 1
daryal
  • 14,643
  • 4
  • 38
  • 54