简体   繁体   中英

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

I have some Exception derived types, which add additional properties to the Exception . Searching the web for examples and guidelines on how to handle the serialization of such Exception -based types led to descriptions and code samples which were rather old. Trying these samples led always to security-errors. To make it work, I had to additionally decorate the GetObjectData -Method with the System.Security.SecurityCritical attribute.

Interestingly, the SecurityPermission -Attribute, which was contained in all of the examples, but in various ways, seemed not to be necessary. Some examples only added it to the GetObjectData , some added it also to the deserializing-constructor. Some examples used the strong SecurityAction.Demand -action, most of the examples used the SecurityAction.LinkDemand -action and others declared SecurityAction.RequestMinimum .

My question is, whether the below Exception -derived type (for the serialization part of it) is correct for being used with nowadays .net frameworks (4.7.[x]+, Core[x], Standard2.[x]+) or if there is something missing, or if I even can remove some parts? Please note that I don't want to seal the type. Other Exception -types should be able to derive from it.

[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;
}

The derived type ( FooBarException ) additionally must then also set the SecurityCritical -attribute on the deserializung-constructor for satisfying the 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;


}

Sources
https://stackoverflow.com/a/100369/340628

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/

After digging a little deeper, I came to the following pattern:

[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;


}

For the SecurityAction.LinkDemand -part of the Question, this value should not be used anymore (.net 4+). Either is SecurityAction.Demand to be used, or even the the whole SecurityPermission declaration may be replaced with SecurityCritical (depending on the case, such as for the above case).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM