Ссылка обратного вызова wcf
У меня есть настольное приложение с дуплексным сервисом WCF, но у меня возникают проблемы с использованием обратного вызова.
Сервис запускается следующим образом в main из program.cs:
ServiceHost svcHost = new ServiceHost(typeof(PeriodicService));
svcHost.Open();
Console.WriteLine("Available Endpoints :\n");
svcHost.Description.Endpoints.ToList().ForEach(endpoint => Console.WriteLine(endpoint.Address.ToString() + " -- " + endpoint.Name));
Для службы я создал функцию подписки, в которой callbackchannel сохраняется в глобальной переменной, затем обратный вызов использует эту глобальную переменную для обратной связи с клиентом (будет только один клиент, подключающийся).
IPeriodicCallback callbackClient;
public IPeriodicCallback Proxy
{
get
{
return this.callbackClient;
}
}
public void joinPeriodicService()
{
Console.WriteLine("Client subscribe");
this.callbackClient = OperationContext.Current.GetCallbackChannel<IPeriodicCallback>();
}
Теперь я хочу вызвать callbackclient из другого класса. В другом классе я создал сервис как:
private PeriodicService periodicService = new PeriodicService();
И я пытаюсь записать данные в него с помощью:
if(this.periodicService.Proxy != null)
{
this.periodicService.Proxy.On1MinuteDataAvailable(tmpPeriod);
}
Однако прокси-сервер остается нулевым, я также пытался переместить прокси-часть в класс, но это также приводит к тому, что он остается нулевым.
Когда клиент подключается, я приятно получаю сообщение "Client Subscribe", но кажется, что запущено два экземпляра periodservice.
Но моя проблема в том, что я не вижу другого способа доступа к periodservice, кроме создания его в моем классе, или он также уже создан svcHost?
Кто-нибудь может указать мне правильное направление?
1 ответ
Этот репозиторий показывает дуплексную реализацию WCF, которую я сделал, чтобы ответить на аналогичный вопрос некоторое время назад, это полный рабочий пример с как можно меньшим количеством дополнительного материала.
https://github.com/Aelphaeis/MyWcfDuplexPipeExample
Допустим, у нас есть контракт на обслуживание, подобный этому:
[ServiceContract(CallbackContract = typeof(IMyServiceCallback),SessionMode = SessionMode.Required)]
public interface IMyService
{
[OperationContract(IsOneWay=true)]
void DoWork();
}
Обратите внимание, что я указал CallbackContract. Если вы хотите создать дуплекс, вы, возможно, захотите, чтобы ваш Service Behavior реализовывал вышеуказанный контракт следующим образом:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("Hello World");
Callback.WorkComplete();
}
IMyServiceCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
}
}
}
Важная вещь здесь - Обратный звонок. Таким образом, ваш сервис позволит вам получить доступ к указанному вами клиенту.
Вам также нужно определить интерфейс обратного вызова, в моем случае это довольно просто:
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract(IsOneWay = true)]
void WorkComplete();
}
Теперь я хочу создать клиент для использования этого дуплексного сервиса. Первое, что мне нужно сделать, это реализовать IMyServiceCallback. Мне нужно сделать это на клиенте. В этом случае реализация такова:
class Callback : IMyServiceCallback
{
public void WorkComplete()
{
Console.WriteLine("Work Complete");
}
}
Теперь, когда я хочу открыть свое дуплексное соединение со службами, я бы создал прокси-класс примерно так:
public class MyServiceClient: IMyService, IDisposable
{
DuplexChannelFactory<IMyService> myServiceFactory { get; set; }
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + @"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
}
public void DoWork()
{
myServiceFactory.CreateChannel().DoWork();
}
public void Dispose()
{
myServiceFactory.Close();
}
}
Обратите внимание, что я указал InstanceContext. Этот контекст экземпляра будет экземпляром созданного мною объекта, который реализует IMyServiceCallback.
Это все, что вам нужно сделать! Просто как тот!
Обновление: объекты обратного вызова, как и любой другой объект. Вы можете хранить их в коллекции и выполнять их итерацию, основываясь на некоторых условиях.
Одним из способов является создание в IMyServiceCallback свойства, которое может однозначно идентифицировать его. Когда клиент подключается к сервису, он может вызвать метод, который указывает объект обратного вызова, который затем может быть кэширован или сохранен для последующего использования. Затем вы можете перебирать обратные вызовы и, основываясь на некоторых условиях, вызывать метод для конкретного клиента.
Это, конечно, сложнее; Тем не менее, это, безусловно, управляемым. Я добавлю пример чуть позже.
Обновление 2 Это рабочий пример того, что вы хотите; однако, это намного сложнее. Я постараюсь объяснить как можно проще: https://github.com/Aelphaeis/MyWcfDuplexPipeExample/tree/MultiClient
Вот список изменений:
- Я изменил клиентский прокси (и сервис) так, чтобы при инициализации он вызывал метод init
- Я также изменил реализацию Service, чтобы теперь это был один экземпляр, который обрабатывает все запросы (для удобства).
- Я добавил новый OperationContract в интерфейс службы под названием Msg
- Я добавил новый метод в IMyServiceCallback под названием RecieveMessage.
- Я добавил способ идентифицировать клиента.
В прокси-классе у меня есть следующее:
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + @"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
Init();
}
public void Init()
{
myServiceFactory.CreateChannel().Init();
}
В моем сервисе у меня есть следующее:
public class MyService : IMyService
{
public List<IMyServiceCallback> Callbacks { get; private set; }
public MyService(){
Callbacks = new List<IMyServiceCallback>();
}
public void Init()
{
Callbacks.Add(Callback);
}
// and so on
Мой IMyServiceCallback был переопределен для:
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract]
int GetClientId();
[OperationContract(IsOneWay = true)]
void WorkComplete();
[OperationContract(IsOneWay = true)]
void RecieveMessage(String msg);
}
Указав номер, вы можете связаться с клиентом, который соответствует этому номеру. Если два клиента имеют одинаковый идентификатор, с ними свяжутся оба клиента.