简体   繁体   中英

How to call a method of the ServiceHost from the hosting process in WCF C#

I have a publisher / subscriber pattern WCF Duplex ServiceHost that is hosted by a Windows Service. The Windows Service receives events from a separate process. OnEvent I would like to force my WCF Host to publish that data to all subscribed clients. Typically if a Client is calling this is straight forward. But when my Service Host needs to do this - I can't get my head around HOW to do that. I have 2 questions:

1: I do not know how to create a Channel in WCFHost from my Windows Service so that it can use to publish to the Subscribers.

2: I read Creating WCF ChannelFactory so I do know I am creating a DuplexChannelFactory (2 per second ) which might be too much overhead.

Any help examples, hints are greatly appreciated. I am not a WCF expert and currently know more about it than I thought I should have to know in order to use it.

I had read on SO Can I call a Method in a self hosted wcf host locally?

So then I have created a method inside my WCFHost like so:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
                 AutomaticSessionShutdown = false,
                 IncludeExceptionDetailInFaults = true)]
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServerHost<TService> : ServiceHost where TService : class
{     
   public T GetDuplexClientChannel<T, Cback>(BindingType bindingType, EndpointAddress endPointAddress)  where T : class 
    {
        ServiceEndpoint sep = GetContractServiceEndPoint<T>(bindingType, endPointAddress);
        lock (_syncRoot)
        {
            DuplexChannelFactory<T> factory = new DuplexChannelFactory<T>(typeof(Cback), sep);
            return factory.CreateChannel(endPointAddress);
        }
    }    
}

I get an error of course that there is no InstanceContext because I am constructing using typeof(Cback) ..

"This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was initialized with a Type and no valid InstanceContext was provided."

So I am not sure how I can go about performing this ? And for those that say read the error : yes I read the error. Now how to do that with an InstanceContext that does not exist as OperationContext.Current does not exist at this point as I am calling this method form my Hosting Process into my WCFHost.

So if I could have a nice example of how to do this - even if I must use the code example on the 2nd link (of course implementing the DuplexChannelFactory) I would greatly appreciate it.

EDIT Basically the windows Service is doing some heavy work monitoring other services, about 2 times a second it then must publish that to "Subscribed" Clients via WCF.

I think you have got very confused about how everything is wired together and are mixing concepts from the client in with the service. You haven't provided much concrete information about your scenario to go on so I'm going to provide a small example and hopefully you will be able to apply the ideas to your problem.

[ServiceContract(CallbackContract=typeof(IMyServiceCallback))]
public interface IMyService
{
    [OperationContract]
    void Register();
}

public interface IMyServiceCallback
{
    [OperationContract]
    void ReceiveData(string data);
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyService : IMyService
{
    static HashSet<IMyServiceCallback> s_allClients = new HashSet<IMyServiceCallback>();
    static object s_lockobj = new object();

    public void Register()
    {
        lock(s_lockobj)
        {
            _allClients.Add(OperationContext.Current.GetCallbackChannel<IMyServiceCallback>());
        }
    }

    public static void SendDataToClients(string data)
    {
        HashSet<IMyServiceCallback> tempSet;
        lock(s_lockobj)
        {
            tempSet = new HashSet<IMyServiceCallback>(_allClients);
        }
        foreach(IMyServiceCallback cb in tempSet)
        {
            try
            {
                cb.ReceiveData(data);
            }
            catch(Exception)
            {
                lock(s_lockobj)
                {
                    _allClients.Remove(cb);
                    cb.Abort();
                    cb.Dispose();
                }
            }
        }
    }
}

In your OnEvent method, you would call something similar to this inside your event method.

MyService.SendDataToClients(mydata);

This uses static data to store the list of clients. If you wanted to do something like segment your clients for different endpoints, you would need to do something different. There is a potential out of order message and scaling problem with this code if your OnEvent method can be called again while the previous call hasn't completed. For example, if you receive 2 messages, the first being large and the second being small, you could potentially send the second smaller message to clients later in the HashSet iteration order before they have been sent the first message. Also this won't scaled to a large number of clients as you could block timing out on one client holding up messages being sent to other clients. You could use something similar to Task's to dispatch multiple message deliveries. If this needs to scale, I would suggest looking at Reactive Extensions for .Net

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM