0

I have a long-running service that gets data from one source, manipulates it, stores it in a database, etc.

I'd like to expose some methods on that service to other applications. Currently we do this via .NET Remoting but I'd like to move to WCF.

Unfortunately, I the endpoint I connect to is never the one I exposed via my long-running service. Below is a simple example:

    [ServiceContract]
    public interface ITestWcfService
    {
        [OperationContract]
        CounterResult GetCurrentValue();
    }

public class TestWcfService : ITestWcfService
    {
        private ITestWindowsService _service;
        public TestWcfService() { /*NOTE: For discoverability*/ }
        public TestWcfService(ITestWindowsService service)
        {
            _service = service;
        }

        public CounterResult GetCurrentValue()
        {
            return _service.GetCurrentValue();
        }
    }

public interface ITestWindowsService
    {
        CounterResult GetCurrentValue();
    }

Then I have my actual Windows Service, which self-hosts the WCF service via the ServiceHost class.

public partial class TestWindowsService : ServiceBase, ITestWindowsService
{
    private static ServiceHost _wcfService;

    public TestWindowsService()
    {
        InitializeComponent();
    }

    public void OnStart(string[] args)
    {
        //Create instance of WCF, passing in reference to this service instance
        TestWcfService wcf = new TestWcfService(this);
        _wcfService = new ServiceHost(wcf);
    }

        public CounterResult GetCurrentValue()
    {
        //Note: Some logic here
    }
}

Now, this more-or-less works except that each time I make a call to the TestWcfServiceClient(), it uses the default constructor and creates a new instance of the Wcf Service and does not use the instance created by the windows service. This means that when I call GetCurrentValue() I get a null reference because the _service member hasn't been set.

I've looked around for solutions and found some citing ServiceHostFactory and ServiceHost and IInstanceProvider but each has seemed to be very, very complicated.

Any thoughts you could provide would be most appreciated.

EDIT: Here's my ServiceModel info

<system.serviceModel>
    <services>
      <service name="WcfService.TestWcfService">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/WcfService/TestWcfService/" />
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address="" binding="basicHttpBinding" contract="WcfService.ITestWcfService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="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>
  </system.serviceModel>
Killnine
  • 5,728
  • 8
  • 39
  • 66

2 Answers2

0

WCF is VERY extensible, but in order to provide such extensibility it is also very complicated. In my opinion, more than it should be so if you want to use this implementation that's the way to go. YOu have to create a behavior that changes the service factory so you can use the custom constructor instead of the empty one.

However, I think there might be another alternative. Since both the service and the WCF share the same app domain they can share values through static objects.

I would move the logic that carries the value you want to expose to un underlying assembly (if you didn't already) and both the service and WCF service would see the same instance therefore you don't need to change all the WCF plumbing.

tucaz
  • 6,524
  • 6
  • 37
  • 60
  • Yeah, I guess the problem I am looking to solve is this: How do I let other applications call methods on my durable service? Right now we use a super-convoluted, custom .NET remoting library. I'd like to use WCF because it's been very nice for us in the past. However, this doesn't seem to be the kind of problem it commonly solves (at least, not in a simple way) – Killnine Jan 23 '13 at 20:06
  • If you implement the required classes to extend WCF and pass whatever the service needs or use the "static object" approach I described you should be able to do it. In another words, forget about what your are trying to do. Instead, do the contrary, forget that auto hosted solution exists and use the windows service to self host your WCF. When it's done you just need to add the missing behavior to your windows service. Its highly doable. – tucaz Jan 24 '13 at 13:18
0

You are trying to put a square peg in a round hole.

WCF is explicitly designed to enable calling business logic independent of how it is hosted. You are trying to maintain the old remoting design that has your business logic in the host layer (in this case a Window's service). As a result you are asking to pass information back up to the host in a way that is possible in WCF but ugly and is generally avoided for many good reasons.

If you want a clean WCF design you will need to create abstraction layers and move your business logic out of the existing TestWindowsService class. Then the windows service creates the WCF service host, the WCF host creates the WCF service, and the WCF service is not dependent on the classes that host it.

Having said all that...

To actually answer your question:

Conceptually the code that you have written should work. Passing an object instance into the ServiceHost constructor is by far the easiest way to avoid the complexity of writing a custom IInstanceProvider. A couple of things to check:

  1. When you pass a service instance into the ServiceHost constructor it needs to be marked as a singleton.

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

  2. Make sure that initialization is really working the way you think it is:

    1. Put a guard on your TestService(ITestWindowsService) constructor that throws if service is null.
    2. You have a comment on the default constructor for TestWcfService() that says "for discoverability". I would remove that constructor (at least while you are troubleshooting). A piece of code might be using that constructor to host a service instance you don't want. This is likely to cause your app to start throwing exceptions or compile errors that point out your real problem.

If you are still having problems: What does the Service Model section of your app.config look like?

ErnieL
  • 5,773
  • 1
  • 23
  • 27
  • Yeah, usually when I feel this way I know I am looking at the problem wrong and maybe WCF isn't the way to go. But MS touts WCF as the replacement for .NET remoting and yet it has basically deprecated this functionality. I *know* that calls from the client are executing the default constructor. It seems like I am making the self-hosted endpoint but cant access it... When I take the default ctr out it throws an error when my client makes method calls about missing behavior. I've added my Service Model to the post. – Killnine Jan 24 '13 at 17:28
  • When you take out the default constructor what's the error? Your real problem is that your configuration needs to be fixed so that the constructor is never called. You might want to change to true to get more information to the client. – ErnieL Jan 25 '13 at 14:57