Как получить ссылку на объект службы WCF из кода, который его создает?

Я модифицирую код в этом руководстве, чтобы создать несколько базовых классов клиент / сервер wcf с подпиской, и я только что наткнулся на кирпичную стену.

Класс сервера в учебнике создается с использованием следующего кода:

class Program
  {
    static void Main(string[] args)
    {
      using (ServiceHost host = new ServiceHost(
        typeof(StringReverser),
        new Uri[]{
          new Uri("net.pipe://localhost")
        }))
      {

        host.AddServiceEndpoint(typeof(IStringReverser),
          new NetNamedPipeBinding(),
          "PipeReverse");

        host.Open();

        Console.WriteLine("Service is available. " +
          "Press <ENTER> to exit.");
        Console.ReadLine();

        host.Close();
      }
    }
  }

Я предполагаю, что публикует экземпляр StringReverser, моя проблема в том, что мне нужна ссылка на этот экземпляр, чтобы я мог вызвать на нем метод, чтобы отправить данные обратно клиенту.

В учебнике сервер просто отвечает клиенту с помощью метода обратного вызова, вместо этого я сохраняю ссылку на клиента в списке подписчиков. Когда мне нужно отправить данные обратно клиентам, мне нужна ссылка на объект Service, чтобы я действительно мог использовать функцию обратного вызова.

Есть ли способ опубликовать службу с помощью WCF, позволяющую иметь ссылку на объект службы? или я могу получить ссылку на объект службы из объекта хоста?

Любая помощь будет оценена ...

Ответов (3)

Решение

Вы можете использовать шаблон singleton в своем классе StringReverser и передать его экземпляр конструктору ServiceHost:

ServiceHost host = new ServiceHost(
  StringReverser.Instance,
  new Uri[]{new Uri("net.pipe://localhost")}
);

Я согласен с тем, что ответ Жюльена - правильный подход, но он неполный (по крайней мере, для .NET 4.5). После передачи экземпляра службы необходимо установить для контекстного режима экземпляра ServiceHost значение Single. Если вы этого не сделаете, при ServiceHost Open() вызове метода вы получите сообщение об ошибке .

Способ установки контекстного режима был совсем не очевиден. Вот фрагмент одной из моих программ, взятый из другого ответа SO:

var baseAddress = new Uri("http://localhost:15003/MockGateway");

using (var host = new ServiceHost(new MockGatewayService(), baseAddress))
{
    // Since we are passing an instance of the service into ServiceHost (rather 
    // than passing in the type) we have to set the context mode to single.
    var behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
    behavior.InstanceContextMode = InstanceContextMode.Single;

    // Continue to use the service here.  If you ever need to get a reference
    // to the service object you can do so with...
    MockGatewayService myService = host.SingletonInstance as MockGatewayService;

    // ...
}

в вашем договоре об обслуживании вы можете позвонить

OperationContext.Current.GetCallbackChannel ());

in any method that is called after the service connects, and then pass it out of the service contract. I typically have a join method where I get a guid for the client and grab the callback there. This is harder than it seems. You either need a singleton/global variable to get it out (easy), or you need to make it so that WCF can use parameterized constructors (hard). For the latter, more correct way of doing it, you need to roll your own classes that implement IInstanceProvider and IEndPointBehavior and add your behavior to the endpoint you are interested in. This has the added benefit of allowing you to use different constructors with different endpoints without redefining your contract. There is unfortunately no typesafe way to do this as you will have to use reflection. Sorry I can't provide a sample, but everything I have is proprietary.