我一直在使用的一个应用程序在尝试序列化类型时失败了。
像这样的陈述
XmlSerializer lizer = new XmlSerializer(typeof(MyType));
生产:
System.IO.FileNotFoundException occurred
Message="Could not load file or assembly '[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
Source="mscorlib"
FileName="[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
FusionLog=""
StackTrace:
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
我没有为我的类定义任何特殊的序列化器。
我该如何解决这个问题?
另一方面,对编译错误进行故障排除是非常复杂的。这些问题在FileNotFoundException异常中显示,并显示如下信息:
File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
at System.Reflection.Assembly.nLoad( ... )
at System.Reflection.Assembly.InternalLoad( ... )
at System.Reflection.Assembly.Load(...)
at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly()
You may wonder what a file not found exception has to do with instantiating a serializer object, but remember: the constructor writes C# files and tries to compile them. The call stack of this exception provides some good information to support that suspicion. The exception occurred while the XmlSerializer attempted to load an assembly generated by CodeDOM calling the System.Reflection.Assembly.Load method. The exception does not provide an explanation as to why the assembly that the XmlSerializer was supposed to create was not present. In general, the assembly is not present because the compilation failed, which may happen because, under rare circumstances, the serialization attributes produce code that the C# compiler fails to compile.
请注意
当XmlSerializer在不能访问临时目录的帐户或安全环境下运行时,也会发生此错误。
来源:
http://msdn.microsoft.com/en-us/library/aa302290.aspx
Like Martin Sherburn said, this is normal behavior. The constructor of the XmlSerializer first tries to find an assembly named [YourAssembly].XmlSerializers.dll which should contain the generated class for serialization of your type. Since such a DLL has not been generated yet (they are not by default), a FileNotFoundException is thrown. When that happenes, XmlSerializer's constructor catches that exception, and the DLL is generated automatically at runtime by the XmlSerializer's constructor (this is done by generating C# source files in the %temp% directory of your computer, then compiling them using the C# compiler). Additional constructions of an XmlSerializer for the same type will just use the already generated DLL.
更新:从。net 4.5开始,XmlSerializer不再执行代码生成,也不再为了在运行时创建序列化程序集而使用c#编译器执行编译,除非通过设置配置文件设置(useLegacySerializerGeneration)显式强制执行。此更改消除了对csc.exe的依赖,并提高了启动性能。来源:. net Framework 4.5 Readme,章节1.3.8.1。
异常由XmlSerializer的构造函数处理。你不需要自己做任何事情,你可以点击“继续”(F5)继续执行你的程序,一切都会好的。如果你被异常停止程序执行并弹出异常助手所困扰,你要么关闭“只是我的代码”,要么将FileNotFoundException设置为在抛出时中断执行,而不是在“User-unhandled”时。
打开“Just My Code”,请转到“Tools >> Options >> Debugging >> General >> enable Just My Code”。要关闭FileNotFound被抛出时的执行中断,请转到调试>>异常>>找到>>输入'FileNotFoundException' >>取消System.IO.FileNotFoundException中的' thrown '复选框。
为了避免异常,你需要做两件事:
向序列化类添加属性(我希望您有访问权限)
使用sgen.exe生成序列化文件
将System.Xml.Serialization.XmlSerializerAssembly属性添加到类中。
将'MyAssembly'替换为MyClass所在程序集的名称。
[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{
…
}
使用sgen.exe实用程序生成序列化文件,并将其与类的程序集一起部署。
' sgen.exe MyAssembly.dll '将生成文件myassembly . xmlserializer .dll
这两个更改将导致.net直接查找程序集。
我检查了一下,它在。net框架3.5和Visual Studio 2008上工作
XmlSerializer函数。FromTypes不会抛出异常,但它会泄漏内存。这就是为什么你需要为每个类型缓存这样的序列化器,以避免内存泄漏为每个创建的实例。
创建自己的XmlSerializer工厂并简单地使用它:
XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));
工厂是这样的:
public static class XmlSerializerFactoryNoThrow
{
public static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();
private static object SyncRootCache = new object();
/// <summary>
/// //the constructor XmlSerializer.FromTypes does not throw exception, but it is said that it causes memory leaks
/// http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor
/// That is why I use dictionary to cache the serializers my self.
/// </summary>
public static XmlSerializer Create(Type type)
{
XmlSerializer serializer;
lock (SyncRootCache)
{
if (_cache.TryGetValue(type, out serializer))
return serializer;
}
lock (type) //multiple variable of type of one type is same instance
{
//constructor XmlSerializer.FromTypes does not throw the first chance exception
serializer = XmlSerializer.FromTypes(new[] { type })[0];
//serializer = XmlSerializerFactoryNoThrow.Create(type);
}
lock (SyncRootCache)
{
_cache[type] = serializer;
}
return serializer;
}
}
更复杂的版本,没有内存泄漏的可能性(请有人检查代码):
public static XmlSerializer Create(Type type)
{
XmlSerializer serializer;
lock (SyncRootCache)
{
if (_cache.TryGetValue(type, out serializer))
return serializer;
}
lock (type) //multiple variable of type of one type is same instance
{
lock (SyncRootCache)
{
if (_cache.TryGetValue(type, out serializer))
return serializer;
}
serializer = XmlSerializer.FromTypes(new[] { type })[0];
lock (SyncRootCache)
{
_cache[type] = serializer;
}
}
return serializer;
}
}
只是作为参考。从D-B的回答和评论中,我得出了这个接近D-B的解决方案。它在我的所有情况下都工作得很好,并且是线程安全的。我不认为使用ConcurrentDictionary是可以的。
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace HQ.Util.General
{
public class XmlSerializerHelper
{
private static readonly Dictionary<Type, XmlSerializer> _dictTypeToSerializer = new Dictionary<Type, XmlSerializer>();
public static XmlSerializer GetSerializer(Type type)
{
lock (_dictTypeToSerializer)
{
XmlSerializer serializer;
if (! _dictTypeToSerializer.TryGetValue(type, out serializer))
{
var importer = new XmlReflectionImporter();
var mapping = importer.ImportTypeMapping(type, null, null);
serializer = new XmlSerializer(mapping);
return _dictTypeToSerializer[type] = serializer;
}
return serializer;
}
}
}
}
用法:
if (File.Exists(Path))
{
using (XmlTextReader reader = new XmlTextReader(Path))
{
// XmlSerializer x = new XmlSerializer(typeof(T));
var x = XmlSerializerHelper.GetSerializer(typeof(T));
try
{
options = (OptionsBase<T>)x.Deserialize(reader);
}
catch (Exception ex)
{
Log.Instance.AddEntry(LogType.LogException, "Unable to open Options file: " + Path, ex);
}
}
}
看到过很多使用ConcurrentDictionary的建议,但没有可靠的例子,所以我将全身心投入到这个解决方案竞赛中。我不是一个线程安全的开发人员,所以如果这段代码不可靠,请为那些跟随的人说出来。
public static class XmlSerializerHelper
{
private static readonly ConcurrentDictionary<Type, XmlSerializer> TypeSerializers = new ConcurrentDictionary<Type, XmlSerializer>();
public static XmlSerializer GetSerializer(Type type)
{
return TypeSerializers.GetOrAdd(type,
t =>
{
var importer = new XmlReflectionImporter();
var mapping = importer.ImportTypeMapping(t, null, null);
return new XmlSerializer(mapping);
});
}
}
我看过其他关于ConcurrentDictionary和Lazy加载值的文章。我不确定这是否与此相关,但这里是它的代码:
private static readonly ConcurrentDictionary<Type, Lazy<XmlSerializer>> TypeSerializers = new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();
public static XmlSerializer GetSerializer(Type type)
{
return TypeSerializers.GetOrAdd(type,
t =>
{
var importer = new XmlReflectionImporter();
var mapping = importer.ImportTypeMapping(t, null, null);
var lazyResult = new Lazy<XmlSerializer>(() => new XmlSerializer(mapping), LazyThreadSafetyMode.ExecutionAndPublication);
return lazyResult;
}).Value;
}