4

すべてのクライアント呼び出しでこのコードを繰り返す必要がないように、次のコードを抽象化/カプセル化しようとしています。たとえば、これはビュー モデル (MVVM) から WCF サービスへの呼び出しです。

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));
            }
        }
    }
}

これにより、私のビュー モデルはたった 1 行のコードで呼び出しを行うことができます。

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

ただし、WcfChannelFactoryすでに破棄されているというエラーが表示されます。ビューモデルが使用しようとすると実際に破棄されるため、これは理にかなっています。しかし、 を削除するとusing、 が適切に破棄されませんWcfChannelFactory。が呼び出されたときWcfChannelFactoryに自身が埋め込まれていることに注意してください。これが、ビューモデルが破棄された後にそれを使用しようとする理由/方法です。WcfClientProxyCreateChannel()

このコードを抽象化して、ビュー モデルの呼び出しをできるだけシンプルに保ちながら、適切に破棄するにはどうすればよい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());
4

2 に答える 2

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()));

サイドノート:

最近、使い捨ての必要性があまり見られないため、このパターンを使い捨てであまり使用していません。ただし、理論的には、次の 2 つの理由から、ディスポーザブルを操作するときに、using ブロック内で実行されるコールバックを取得するこのパターンが好きだと思います。

  1. それは簡単です
  2. IDisposables の消費者にインスタンスを正しく破棄するように強制します... IDisposables がすべての実行パスで破棄されない場合にコンパイラの警告を発生させないという C# のチームには同意しますが (私はそう思います)、それはまだ少し心配です.
于 2013-05-06T02:25:24.813 に答える
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;
        }
    }
}

よりクリーンですが、静的メソッドを使用することはお勧めしません。

于 2013-05-06T04:43:29.917 に答える