Can I call a method in a Self-Hosted WCF Service locally?
Asked Answered
S

4

11

I have a WCF Service contract which is basically the Publish Subscriber pattern.

The WCF Service is hosted inside the Windows Service that I want to publish from. The Clients subscribe to messages and when the Windows Service does something it publishes to all clients.

To host the service I have declared a ServiceHost class and the Contract Class has a method which is not flagged in the Interface but is implemented in the Class to publish.

I want to be able to call this method locally (not going through WCF) which then publishes the message via Callbacks.

I can't seem to get from ServiceHost to the instance of the Contract Class.

Is this possible and if so how? I know the work around is to have a client built into the service as well, but it seems a bit strange creating a client to connect to itself.

Thanks in advance

DJIDave

app.config

<system.serviceModel>
    <services>
      <service behaviorConfiguration="Processor.Wcf.ServiceBehavior"
        name="Processor.Wcf.ProcessorService">
        <endpoint address="net.tcp://localhost:9000/processor/service"
              binding="netTcpBinding" name="procService"
              bindingConfiguration="netTcpBindingConfig"
              contract="Processor.Wcf.IProcessorService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          <host>
            <baseAddresses>
              <add baseAddress="http://localhost:8732/Design_Time_Addresses/Processor.Wcf/Service1/" />
            </baseAddresses>
          </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Processor.Wcf.ServiceBehavior">
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="netTcpBindingConfig"
                 closeTimeout="00:01:00"
                 openTimeout="00:01:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:01:00"
                 transactionFlow="false"
                 transferMode="Buffered"
                 transactionProtocol="OleTransactions"
                 hostNameComparisonMode="StrongWildcard"
                 listenBacklog="10"
                 maxBufferPoolSize="524288"
                 maxBufferSize="65536"
                 maxConnections="10"
                 maxReceivedMessageSize="65536">
          <readerQuotas maxDepth="32"
                        maxStringContentLength="8192"
                        maxArrayLength="16384"
                        maxBytesPerRead="4096"
                        maxNameTableCharCount="16384" />
          <reliableSession ordered="true"
                           inactivityTimeout="00:10:00"
                           enabled="false" />
          <security mode="Transport">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

  </system.serviceModel>
Spell answered 20/5, 2011 at 9:40 Comment(0)
A
9

Unless you provide the service instance reference to the ServiceHost as a constructor parameter, there isn't a way to have the ServiceHost provide you an service instance reference. If you do provide that instance reference then you are creating a singleton service which is generally not a good idea.

To keep the service as it is configured, you will have to call it through a client. This is actually easier than you might think. Since your host code has access to the service contract, you can use it with the ChannelFactory class to get a proxy for the service. Besides the service contract, all you have to provide is the endpoint name and ChannelFactory will do the rest. Below is an example of how to do this:

private IMyServiceContract GetLocalClient(string serviceEndpointName)
{
    var factory = new ChannelFactory<IMyServiceContract>(serviceEndpointName);
    return factory.CreateChannel();
}

UPDATE: Along with this approach, you should consider having you service expose a NetNamedPipeBinding endpoint to improve performance. This binding pretty much does everything in memory and is the fastest binding for same machine service invocation.

Altaf answered 20/5, 2011 at 11:0 Comment(3)
Thanks for the response, I can successfully get the factory, but when trying to call CreateChannel I get "The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified.". I will post the app.config as an edit to the original post.Spell
If the exception is thrown even if the endpoint name you are passing the function is "procService" then it may be a permission problem. This is just a guess but the account for the Windows service is the one used to access the WCF service. It could be possible that it doesn't have permission to invoke the service since you're requiring Windows authentication. If that's not it then I would recommend that you add and configure the netNamedPipeBinding instead of try to troubleshoot the TCP binding since it a better choice for what you are doing.Altaf
Helpful: social.msdn.microsoft.com/Forums/vstudio/en-US/…Johnny
D
3

For a WCF service instantiating more than once (non-singleton), you can maintain a list containing each instance's corresponding callback function as given here: mdsn. You can call the method CallClients() (from this MSDN example) from the hosting code directly as it is a static member of the service class. This is the only other way I found..

Deth answered 19/4, 2012 at 13:37 Comment(0)
J
2

Unless you provide the service instance reference to the ServiceHost as a constructor parameter,

This line from Sixto's solution solved things for me. Credit and thanks to this post as well.

I'm using a duplex binding at the moment.


The key concept is that you can pass in a Type or an instance to the ServiceHost constructor.

So what I had before was:

 ServiceHost host = new ServiceHost(typeof(MyService), myUri);

What I needed was:

 MyService service = new MyService(foo);  // Can now pass a parameter
 ServiceHost host = new ServiceHost(service, myUri);

Also, I needed to mark MyService with

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 

...and now I can call the host's methods from inside the service.

However, keep in mind that the instance you created will not have an OperationContext if you call its methods directly: https://mcmap.net/q/959802/-current-operationcontext-is-null-in-wcf-windows-service

Good luck!

Johnny answered 1/3, 2018 at 21:36 Comment(0)
P
2

Old question, but here is another way to call a Singleton WCF Service hosted on a Windows Service

Following @Ben's requirements, the Service would need to be forced to be a Singleton:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 

Then:

var host = new ServiceHost(typeof(MyService), myUri);
var instance = (MyService)host.SingletonInstance;

That's it. Basically, the host already has a property that needs to be 'casted' to be able to access all the features from the Service.

Pedaiah answered 4/4, 2019 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.