JTtheGeek 的回答是正确的,自定义SerializationBinder
是处理此问题的方法。但是,该答案中给出的示例代码不足以满足我的用例。我需要一些可以:
- 处理不匹配的版本号和命名空间。
- 查看所有程序集,而不仅仅是当前程序集。
- 仍然足够快以每秒调用 100 次。
这就是我想出的:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Company.Product.Common.Serialize
{
/// <summary>
/// A Serialization Binder that allows inexact matches (version number or namespace).
/// </summary>
public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder
{
static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>();
/// <summary>
/// When overridden in a derived class, controls the binding of a serialized object to a type.
/// </summary>
/// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param>
/// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param>
/// <returns>
/// The type of the object the formatter creates a new instance of.
/// </returns>
public override Type BindToType(string assemblyName, string typeName)
{
Type type;
var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName);
// use cached result if it exists
if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type))
{
return type;
}
// try the fully qualified name
try { type = Type.GetType(assemblyQualifiedTypeName); }
catch { type = null; }
if (type == null)
{
// allow any assembly version
var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(','));
var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion);
try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); }
catch { type = null; }
}
if (type == null)
{
// check all assemblies for type full name
try
{
type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.ExportedTypes)
.Where(a => a.FullName == typeName)
.FirstOrDefault();
}
catch { type = null; }
}
if (type == null)
{
// check all assemblies for type name
var name = typeName.Split('.').Last();
try
{
type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.ExportedTypes)
.Where(a => a.Name == name)
.FirstOrDefault();
}
catch { type = null; }
}
typeBindings[assemblyQualifiedTypeName] = type;
return type;
}
}
}