3

IIS でホストされている WCF サービスがあります。このサービスは、interface-types を引数または戻り値の型として使用する汎用インターフェイスによって定義されるため、ServiceKnownType 属性を使用して、実行時に上記インターフェイスの使用可能な実装を定義します。

これはすべて正常に機能しているように見えますが、これらのサービスへのすべてのリクエストが CommunicationException で失敗する場合があります。「パラメーターhttp://tempuri.org/ : argをシリアル化しようとしているときにエラーが発生しました。InnerException メッセージは、データ コントラクト名が 'SomeInterfaceImplementation:http://schemas.datacontract.org/2004 の'Type ' MyNamespace.SomeInterfaceImplementation ' でした。 /07/MyNamespace' は予期されていません。たとえば、KnownTypeAttribute 属性を使用するか、DataContractSerializer に渡される既知の型のリストにそれらを追加することにより、静的に認識されていない型を既知の型のリストに追加します。'.InnerException を参照してください詳しくは。

このエラーを確実に再現することはできませんが、サービスをしばらく実行したままにしておくと (週末など)、定期的に表示されます。私は当初、IIS アプリケーション プールのリサイクルが原因でこれが発生していると推測しましたが、手動のリサイクル、または短い間隔 (2 分など) でスケジュールされたリサイクルでは、問題を再現できませんでした。

「ServiceKnownTypes」のプロバイダーから「 MyNamespace.MyType 」を除外することで、例外を確実に再現できますが、そもそもこれが断続的に発生する理由はわかりません。

次のスニペットは、サービス宣言を示しています。これは、さまざまな実装タイプの汎用サービスです。. CommunicationException を生成しているのは操作引数 ' arg ' であることに注意してください。

[ServiceKnownType("GetKnownTypes", typeof(KnownTypesCache))]
[ServiceContract]
public interface IMyService<T>
    where T : class, IMyServiceTypeInterface
{
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    MyReturnType HandleRequest(T element, ISomeInterfaceArgumentType arg);
}

さて、ISomeInterfaceArgumentType の既知の型を提供する KnownTypesCache の実装はそのようなものです。

public static class KnownTypesCache
{
    private static readonly List<Assembly> queriedAssemblies = new List<Assembly>();
    private static readonly List<Type> knownTypes = new List<Type>();


    static KnownTypesCache()
    {
        // get all available assemblies at this time
        List<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

        // find all available known types publishers
        IEnumerable<Type> knownTypesPublisherTypes = assemblies
            .Where(a => !queriedAssemblies.Contains(a)) // exclude already queried assemblies to speed things up
            .SelectMany(s => s.GetTypes())
            .Where(p => typeof(IKnownTypesPublisher).IsAssignableFrom(p) && p.HasAttribute(typeof(KnownTypesPublisherAttribute)));

        // add all known types
        foreach (Type type in knownTypesPublisherTypes)
        {
            IKnownTypesPublisher publisher = (IKnownTypesPublisher)Activator.CreateInstance(type);

                AddRange(publisher.GetKnownTypes());
            }
        }


        // record the assemblies we've already loaded to avoid relookup
        queriedAssemblies.AddRange(assemblies);
    }

    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
        return knownTypes;
    }
}

基本的に、このグローバル スタティック キャッシュは、AppDomain に読み込まれたすべてのアセンブリを照会して、'IKnownTypesPublisher' の実装者を探します。これにより、アセンブリごとに使用可能なシリアル化の型が提供されます。そのため、アセンブリごとに 1 つの IKnownTypesPublisher があり、そのアセンブリでのみ型を識別する役割を果たします。KnownTypesCache はこれらすべてを単純に集約し、実行時に DataContractSerializer に返します。

前述したように、このアプローチは 99% の確率でうまく機能するようです。そして、私が特定できる理由もなく、動作を停止し、iisresetを呼び出すことによってのみ解決できます。

私は今かなり困惑しています。あらゆる種類の解決策を試しましたが、このエラーを確実に再現できるのは月曜日まで待つことだけなので、少し難しいです!

私の最後の考えは、すべてのアセンブリが AppDomain にロードされる前に静的な KnownTypesCache コンストラクターが呼び出される可能性があるため、キャッシュはインスタンスの存続期間中空になるということです...?

4

1 に答える 1

2

私の最後の考えは、すべてのアセンブリがAppDomainにロードされる前に、静的なKnownTypesCacheコンストラクターが呼び出されている可能性があるということです。

これで、あなたはおそらくあなた自身の質問に答える道を進んでいます。

AppDomain.GetAssemblies()AppDomainの実行コンテキストにすでにロードされているアセンブリのみを返します。新しいAppDomainを作成するIISワーカープロセスをリサイクルした後、KnownTypesCacheタイプを最初に使用してから、IKnownTypesPublisherタイプの1つを含むアセンブリを読み込むまでの間に競合状態が発生する可能性があります。断続的で再現が難しいバグは、競合状態が原因であることがよくあります。

デザインが正しく機能するためには、KnownTypesCacheメソッドを呼び出す前に、サービス実装が既知のタイプを含むすべてのアセンブリを常にロードするようにする必要があります。

于 2012-07-23T09:15:45.553 に答える