我有一个 WCF 服务,托管在 IIS 中。该服务由具有接口类型作为参数或返回类型的通用接口定义,因此我们使用 ServiceKnownType 属性来定义所述接口在运行时的可用实现。
这一切似乎都运行良好,但是有时我们会看到对这些服务的所有请求都失败并出现 CommunicationException;“尝试序列化参数http://tempuri.org/时出错:arg。InnerException消息是 'Type ' MyNamespace.SomeInterfaceImplementation ',数据合同名称为 'SomeInterfaceImplementation:http://schemas.datacontract.org/2004 /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”的实现者,这反过来又为每个程序集提供可用的序列化类型。因此,每个程序集有一个 IKnownTypesPublisher,它只负责识别该程序集中的类型,并且 KnownTypesCache 简单地聚合所有这些并在运行时返回给 DataContractSerializer。
正如我所提到的,这种方法似乎在 99% 的情况下都能正常工作。然后我无法识别,它停止工作,只能通过调用iisreset来解决。
我现在很困惑,我尝试了各种解决方案,但是因为我只能等到星期一的第一件事才能可靠地重现这个错误,所以这有点困难!
我剩下的最后一个想法是,在所有程序集加载到 AppDomain 之前,可能会调用静态 KnownTypesCache 构造函数,因此缓存在实例的生命周期内将是空的......?