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 コンストラクターが呼び出される可能性があるため、キャッシュはインスタンスの存続期間中空になるということです...?