作者: "zyl910" 一、緣由 XML序列化是一個很常用的功能,但對於.NET Core/Standard,其直到2.0版才內置支持XML序列化。具體來說, .NET Core 2.0 或 .NET Standard 2.0 才有 "XmlIgnoreAttribute" 類,而1.X版(.NE ...
作者: zyl910
一、緣由
XML序列化是一個很常用的功能,但對於.NET Core/Standard,其直到2.0版才內置支持XML序列化。具體來說, .NET Core 2.0 或 .NET Standard 2.0 才有 XmlIgnoreAttribute類,而1.X版(.NET Core 1.0~1.1 或 .NET Standard 1.0~1.6)版沒有。
這一點可以在官網的API參考頁面(.NET API Browser)驗證,若以 .NET Core/Standard 1.X版查看 XmlIgnoreAttribute 類,會被回退到 .NET Framework 4.7。這就表示XML序列化功能是在.NET Core/Standard 1.X版標準範圍外的。
可是XML序列化是很常用的功能,特別是XmlIgnoreAttribute很常用。有什麼辦法可以使 .NET Core/Standard 1.X 項目中 能使用它呢?
二、在 .NET Core 項目中使用NuGet的包,工作正常
查了一下,發現NuGet上有一個叫 System.Xml.XmlSerializer 的包。
把該包加入 .NET Core 項目,果然能正常使用XML序列化功能了。
三、在 .NET Standard 項目中使用NuGet的包,遇到“violation of security transparency rules failed”異常
3.1 問題
既然.NET Core項目能工作,那麼應該也能用到.NET Standard項目中。
於是我嘗試了一下,在.NET Standard類庫項目中增加此NuGet包。編譯成功了,隨後在使用時發現問題——
- 在.NET Framework項目引用它(.NET Standard類庫項目)時,能正常工作。
- 但在.NET Core項目引用它時,運行時卻報告“violation of security transparency rules failed”(違反安全透明規則失敗)異常。
Unhandled Exception: System.InvalidOperationException: There was an error reflecting type 'ZylLib.UnionTypes.UnionShort'. ---> System.MethodAccessException: Attempt to access method System.Xml.Serialization.XmlIgnoreAttribute..ctor() in violation of security transparency rules failed.
at System.RuntimeMethodHandle.CheckLinktimeDemands(IRuntimeMethodInfo method, RuntimeModule module, Boolean isDecoratedTargetSecurityTransparent)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType)
at System.Attribute.GetCustomAttributes(MemberInfo element, Boolean inherit)
at System.Xml.Serialization.XmlAttributes..ctor(MemberInfo memberInfo)
at System.Xml.Serialization.XmlReflectionImporter.GetAttributes(MemberInfo memberInfo)
at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at ZylLib.UnionTypes.UnionTypesExample.TestShort(StringBuilder sb)
at ZylLib.UnionTypes.UnionTypesExample.Output(StringBuilder sb)
at ZylLib.UnionTypes.ConsoleExample.Program.Main(String[] args)
3.2 中途分析
調試時查了一下程式集的位置,發現所引用的是這個dll——
C:\Users\<用戶名>\.nuget\packages\System.Xml.XmlSerializer\4.0.10\lib\DNXCore50\System.Xml.XmlSerializer.dll
看了一下,該程式集沒有 AllowPartiallyTrustedCallers、SecurityTransparent 特性。導致所有代碼預設是 SecurityCritical(安全關鍵)的,透明方法無法調用。
但這一點太奇怪了,.NET Core項目也用的是這個dll啊。.NET Core項目直接使用XML序列化是能正常工作的。表示.NET Core是 SecurityCritical(安全關鍵)的啊。
而且從異常來看,是Attribute.GetCustomAttributes這個底層方法報錯的。
難道是“不能用NuGet包,只能用標準範圍內”的嗎?
3.3 最終解決
整理一下目前的調用關係——
- XmlIgnoreAttribute 位於 NuGet包System.Xml.XmlSerializer。雖然MSDN上說該類是安全透明的,但NuGet包中的該類是 SecurityCritical(安全關鍵) 的。
- .NET Standard項目中有一個 UnionShort 結構,它其中的欄位用到了 XmlIgnoreAttribute。因本程式集配置了AllowPartiallyTrustedCallers,故該結構是安全透明的。
- .NET Core 項目引用了 .NET Standard項目中的UnionShort結構,但根據該結構構造XmlSerializer時,底層的 CustomAttribute.GetCustomAttributes 拋“violation of security transparency rules failed”異常了。雖然.NET Core 項目是SecurityCritical的。
難道是安全透明類(或結構)裡面不能引用SecurityCritical(安全關鍵)的特性?
編譯時沒報錯啊,創建該類、調用方法也正常啊。現在只是在序列化時使用了該特性。
於是將 .NET Standard項目程式集的 AllowPartiallyTrustedCallers 特性去掉。發現能正常運行了。看來真的是“安全透明類(或結構)裡面不能引用SecurityCritical(安全關鍵)的特性”。
去掉“AllowPartiallyTrustedCallers”不利於安全透明規則,於是將它恢復了。隨後嘗試給該結構體加上 SecuritySafeCriticalAttribute 特性。發現能正常運行了。
四、心得
經過這件事後,有了以下心得——
- 對於 .NET Framework 已有但 .NET Core/Standard 沒有的功能。可嘗試搜索NuGet包,很多功能其實已經有官方包了。
- 安全透明類(或結構)裡面不能引用SecurityCritical(安全關鍵)的特性。需給類(或結構)加上 SecuritySafeCriticalAttribute 特性。
- 使用NuGet包時容易遇到SecurityCritical(安全關鍵)的類,即使是官方的包也有時會有這種情況,故需註意加上 SecuritySafeCriticalAttribute 特性。
同理,遇到缺少 DataContractAttribute 特性時,可引用 NuGet上的 System.Runtime.Serialization.Primitives 包。測試通過。
參考文獻
- MSDN《安全透明的代碼,級別 2》. https://msdn.microsoft.com/zh-cn/library/dd233102.aspx
- https://docs.microsoft.com/zh-cn/dotnet/api/?view=netstandard-1.0
- https://www.nuget.org/packages/System.Xml.XmlSerializer/4.0.11
- https://www.nuget.org/packages/System.Runtime.Serialization.Primitives/4.1.1