4

これがポイントです。私はWCFサービスを持っています。それは現在機能しています。それで、私はクライアント側で働き始めます。そして、アプリケーションの実行中に、タイムアウトという例外が発生しました。接続を維持する方法については多くの例がありますが、チャネルを作成して使用し、破棄するのが最善の方法であることもわかりました。そして正直なところ、私はそれが好きでした。したがって、チャンネルを閉鎖する最善の方法について読んでいると、それらを必要とするすべての人に役立つ可能性のある2つのリンクがあります。

1. 正しい方法でクライアントをクリーンアップする

2. Func の使用

最初のリンクでは、これは例です:

    IIdentityService _identitySvc;
...
if (_identitySvc != null)
 {
     ((IClientChannel)_identitySvc).Close();
     ((IDisposable)_identitySvc).Dispose();
     _identitySvc = null;
 }

そのため、チャネルが null でない場合は、閉じて破棄し、null を割り当てます。しかし、少し質問があります。この例では、チャネルに .Close() メソッドがありますが、私の場合、インテリセンスは Close() メソッドを表示していません。ファクトリ オブジェクトにのみ存在します。だから書かなければならないと思っています。しかし、それを実装するコントラクトまたはクラスを持つインターフェイスでは??. そして、このメソッドは何をすべきですか??.

さて、次のリンクは、これまでに試したことのないものです。Func<T>. そして、ゴールを読んだ後、それは非常に興味深いものです。ラムダを使用してチャネルを作成し、使用し、閉じて破棄する関数を作成します。Using()この例では、その関数をステートメントのように実装しています。それは本当に良いことであり、優れた改善です。しかし、私は少し助けが必要です.正直なところ、私はその機能を理解することができないので、専門家からの少しの説明は非常に役に立ちます. これは機能です:

TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code)
{
    var chanFactory = GetCachedFactory<TChannel>();
    TChannel channel = chanFactory.CreateChannel();
    bool error = true; 
    try {
        TReturn result = code(channel); 
        ((IClientChannel)channel).Close();
        error = false; 
        return result; 
    }
    finally {
        if (error) {
            ((IClientChannel)channel).Abort();
        }
    }
}

そして、これはどのように使用されているかです:

int a = 1; 
int b = 2; 
int sum = UseService((ICalculator calc) => calc.Add(a, b)); 
Console.WriteLine(sum);

ええ、私は本当に、本当に良いと思います。私が持っているプロジェクトでそれを使用するためにそれを理解したいと思います.

そして、いつものように、これが多くの人に役立つことを願っています.

4

3 に答える 3

2

プロキシ オブジェクトは、コントラクトだけでなく、プロキシの有効期間を制御できるIClientChannelも実装します。

最初の例のコードは信頼できません。チャネルがすでに無効になっている場合 (たとえば、セッション ベースの対話でサービスがダウンした場合)、リークします。2 番目のバージョンでわかるように、エラーが発生した場合、プロキシで Abort を呼び出し、クライアント側を引き続きクリーンアップします。

次のように、拡張メソッドを使用してこれを行うこともできます。

 enum OnError
 {
     Throw,
     DontThrow
 }

 static class ProxyExtensions
 {
     public static void CleanUp(this IClientChannel proxy, OnError errorBehavior)
     {
         try
         {
             proxy.Close();
         }
         catch
         {
             proxy.Abort();

             if (errorBehavior == OnError.Throw)
             {
                 throw;
             }
         }
     }
 }

ただし、これの使い方は少し面倒です

 ((IClientChannel)proxy).CleanUp(OnError.DontThrow);

ただし、コントラクトと IClientChannel の両方を拡張する独自のプロキシ インターフェイスを作成すると、これをよりエレガントにすることができます。

interface IPingProxy : IPing, IClientChannel
{

}
于 2011-07-18T07:03:56.323 に答える
1

Jason の回答のコメントに残された質問に答えるために、GetCachedFactory の簡単な例は次のようになります。この例では、ファクトリが作成するサービスの ConfigurationName と等しい "Contract" 属性を持つ構成ファイルでエンドポイントを見つけることによって、作成するエンドポイントを検索します。

ChannelFactory<T> GetCachedFactory<T>()
{
    var endPointName = EndPointNameLookUp<T>();
    return new ChannelFactory<T>(endPointName);
}

// Determines the name of the endpoint the factory will create by finding the endpoint in the config file which is the same as the type of the service the factory is to create
string EndPointNameLookUp<T>()
{
    var contractName = LookUpContractName<T>();
    foreach (ChannelEndpointElement serviceElement in ConfigFileEndPoints)
    {
        if (serviceElement.Contract == contractName) return serviceElement.Name;
    }
    return string.Empty;
}

// Retrieves the list of endpoints in the config file
ChannelEndpointElementCollection ConfigFileEndPoints
{
    get
    {
        return ServiceModelSectionGroup.GetSectionGroup(
            ConfigurationManager.OpenExeConfiguration(
                ConfigurationUserLevel.None)).Client.Endpoints;
    }
}

// Retrieves the ConfigurationName of the service being created by the factory
string LookUpContractName<T>()
{
    var attributeNamedArguments = typeof (T).GetCustomAttributesData()
        .Select(x => x.NamedArguments.SingleOrDefault(ConfigurationNameQuery));

    var contractName = attributeNamedArguments.Single(ConfigurationNameQuery).TypedValue.Value.ToString();
    return contractName;
}

Func<CustomAttributeNamedArgument, bool> ConfigurationNameQuery
{
    get { return x => x.MemberInfo != null && x.MemberInfo.Name == "ConfigurationName"; }
}

ただし、より良い解決策は、IoC コンテナーにクライアントの作成を管理させることです。たとえば、autofacを使用すると、次のようになります。まず、次のようにサービスを登録する必要があります。

var builder = new ContainerBuilder();

builder.Register(c => new ChannelFactory<ICalculator>("WSHttpBinding_ICalculator"))
  .SingleInstance();
builder.Register(c => c.Resolve<ChannelFactory<ICalculator>>().CreateChannel())
  .UseWcfSafeRelease();

container = builder.Build();

「WSHttpBinding_ICalculator」は、構成ファイル内のエンドポイントの名前です。その後、次のようにサービスを使用できます。

using (var lifetime = container.BeginLifetimeScope())
{
    var calc = lifetime.Resolve<IContentService>();
    var sum = calc.Add(a, b);
    Console.WriteLine(sum);
}
于 2011-12-30T23:36:51.260 に答える