2013-05-06 2 views
4

Я пытаюсь абстрагироваться/инкапсулировать следующий код, чтобы все клиентские вызовы не нуждались в повторении этого кода. Например, это вызов, от модели представления (MVVM) к службе WCF:Tricky IDisposable Issue

using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
{ 
    var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 
    IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));  
    this.Applications = new ObservableCollection<Application>(prestoService.GetAllApplications().ToList()); 
} 

Моя первоначальная попытка рефакторинга было сделать это:

public static class PrestoWcf 
{ 
    public static IPrestoService PrestoService 
    { 
     get 
     { 
      using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
      { 
       var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];  
       return channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
      } 
     } 
    } 
} 

Это позволяет мой взгляд моделей сделать вызов с помощью всего одной строки кода сейчас:

this.Applications = new ObservableCollection<Application>(PrestoWcf.PrestoService.GetAllApplications().ToList()); 

Однако я получаю ошибку WcfChannelFactory уже размещенный. Это имеет смысл, потому что оно действительно используется, когда модель представления пытается его использовать. Но если я удалю using, то я не правильно распоряжаюсь WcfChannelFactory. Обратите внимание, что WcfChannelFactory вставляется в WcfClientProxy, когда вызывается CreateChannel(). Вот почему/как модель представления пытается использовать ее после того, как она была удалена.

Как я могу абстрагироваться от этого кода, чтобы мои запросы на модели зрения были максимально простыми, при правильном размещении WcfChannelFactory? Надеюсь, я объяснил это достаточно хорошо.

Редактировать - Решено!

Основываясь на стейки ответ, это сделал:

public static class PrestoWcf 
{ 
    public static T Invoke<T>(Func<IPrestoService, T> func) 
    { 
     using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
     { 
      var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 

      IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
      return func(prestoService); 
     } 
    } 
} 

А вот вид модели вызова:

this.Applications = new ObservableCollection<Application>(PrestoWcf.Invoke(service => service.GetAllApplications()).ToList()); 
+0

+1 для использования Func, который возвращает ваши приложения, а не действие с побочными эффектами! –

ответ

7

Что-то вроде следующего может помочь

public static void UsePrestoService(Action<IPrestoService> callback) 
{ 
    using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
    { 
     var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"]; 
     IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress)); 
     //Now you have access to the service before the channel factory is disposed. But you don't have to worry about disposing the channel factory. 
     callback(prestoService); 
    } 
} 

UsePrestoService(service => this.Applications = new ObservableCollection<Application>(service.GetAllApplications().ToList())); 

стороны примечание:

Я не использовал этот шаблон много с расходными материалами, потому что в последнее время я не нашел слишком много необходимости в одноразовых изделиях. Однако, в теории я думаю, что мне нравится этот шаблон, принимая обратный вызов, который выполняется внутри с помощью блока, при работе с расходными по двум причинам:

  1. Это просто
  2. Это вынуждает потребитель IDisposables распоряжаться экземпляров правильно ... Хотя я согласен (я думаю) с командой C# , чтобы не поднимать предупреждения компилятора, когда IDisposables не удаляются во всех путях выполнения, это все еще немного тревожно.
+2

+1, мне нравится это решение. – Gjeltema

+0

+1 хорошее решение! – ppetrov

0

Вы уверены, что используете шаблон локатора обслуживания? Кроме того, это анти-шаблон, используя Invoke<T> и Func<T, TResult>, я думаю, что будет некоторое замешательство в будущем использовании. Кроме того, я не думаю, что этот способ будет разделять использование сервиса на другой уровень.

Я думаю, что этот подход, возвращая результат, имеет больше SOC, чем с использованием Func<T, TResult>.

public static class PrestoWcf 
{ 
    public static IEnumerable<Application> PrestoService 
    { 
     get 
     { 
      IEnumerable<Application> appList; 
      using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding())) 
      { 
       var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];  
       appList = prestoService.GetAllApplications().ToArray(); //you can ignore the .ToArray, I just not sure whether the enumerable still keep the reference to service 
      } 
      return appList; 
     } 
    } 
} 

Очиститель, но все же я не предлагаю использовать статический метод.

+0

Простите, но я не совсем уверен, что вы имеете в виду. Служба Presto не всегда возвращает список . Мой пример был всего одним звонком. Если я пойду с вашим подходом, я должен создать новый метод для каждого существующего метода в службе. –

+0

Ну, не указано, что служба не всегда возвращает 'List '. Кроме того, я просто подчеркнул, что использование 'static T Invoke (Func func)' слишком абстрактно (и для меня это похоже на локатор сервисов). Это может быть адское обслуживание, если оно используется в бизнес-слое. – Fendy

+0

Спасибо, но это все клиентская сторона. Это всего лишь помощник, поэтому нам не нужно повторять этот же код с каждым вызовом службы. –