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
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).