如何在 .net 4.7.[x]+ 和 .net Standard 2.[x]+ 中正确序列化异常

How to correctly serialize Exceptions in .net 4.7.[x]+, and .net Standard 2.[x]+

我有一些 Exception 派生类型,它们为 Exception 添加了额外的属性。在网络上搜索有关如何处理此类基于 Exception 的类型的序列化的示例和指南会导致相当陈旧的描述和代码示例。尝试这些样本总是会导致安全错误。 为了让它工作,我不得不用 System.Security.SecurityCritical 属性额外装饰 GetObjectData-Method。

有趣的是,SecurityPermission-Attribute 包含在所有示例中,但以各种方式出现,似乎不是必需的。一些示例仅将其添加到 GetObjectData,一些示例还将其添加到反序列化构造函数。一些示例使用了强 SecurityAction.Demand-action,大多数示例使用了 SecurityAction.LinkDemand-action,其他示例使用了 SecurityAction.RequestMinimum

我的问题是,下面的 Exception 派生类型(对于它的序列化部分)是否适合用于当今的 .net 框架(4.7.[x]+,Core[x] , Standard2.[x]+) 或者是否缺少某些东西,或者我什至可以删除一些部分?请注意,我不想密封类型。其他 Exception 类型应该可以从它派生。

[Serializable]
public class FooException : Exception{

    readonly int m_fooValue;

    public FooException(string message,int fooValue,Exception innerException=null) : base(message,innerException){
        m_fooValue = fooValue;
    }
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    protected FooException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_fooValue = info.GetInt32(nameof(FooValue));            
    }

    [SecurityCritical]
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(FooValue), m_fooValue);            
        base.GetObjectData(info, context);
    }

    public int FooValue => m_fooValue;
}

派生类型 (FooBarException) 还必须在反序列化构造函数上设置 SecurityCritical 属性以满足 LinkDemand:

[Serializable] 
public class FooBarException : FooException{

    readonly int m_barValue;

    public FooBarException(string message,int fooValue, int barValue, Exception innerException = null) : base(message, fooValue, innerException) {
        m_barValue = barValue;
    }
    [SecurityCritical] // Additional for satisfying the LinkDemand on the base constructor
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    protected FooBarException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_barValue = info.GetInt32(nameof(BarValue));
    }

    [SecurityCritical]
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(BarValue), m_barValue);
        base.GetObjectData(info, context);
    }

    public int BarValue => m_barValue;


}

来源

https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.iserializable.getobjectdata?view=netframework-4.8

What is the correct way to make a custom .NET Exception serializable?

https://blog.gurock.com/articles/creating-custom-exceptions-in-dotnet/

深入挖掘后,我得出以下模式:

[Serializable]
public class FooException : Exception{

    readonly int m_fooValue;

    public FooException(string message, int fooValue, Exception innerException = null) : base(message, innerException) {
        m_fooValue = fooValue;
    }

    [SecuritySafeCritical]
    protected FooException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_fooValue = info.GetInt32(nameof(FooValue));
    }

    [SecurityCritical]      
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(FooValue), m_fooValue);
        base.GetObjectData(info, context);
    }

    public int FooValue => m_fooValue;
}

[Serializable]
public class FooBarException : FooException
{

    readonly int m_barValue;

    public FooBarException(string message, int fooValue, int barValue, Exception innerException = null) : base(message, fooValue, innerException) {
        m_barValue = barValue;
    }
    [SecuritySafeCritical]        
    protected FooBarException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_barValue = info.GetInt32(nameof(BarValue));
    }

    [SecurityCritical]        
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(BarValue), m_barValue);
        base.GetObjectData(info, context);
    }

    public int BarValue => m_barValue;


}

对于问题的 SecurityAction.LinkDemand 部分,不应再使用此值 (.net 4+)。要么使用 SecurityAction.Demand,要么甚至整个 SecurityPermission 声明都可以替换为 SecurityCritical(视情况而定,例如上述情况)。