Say I have an instance variable that is declared to be a given base class. I want to find what the original, upper-most type that object actually is instead of the base class. How do I do this?
I have a validation attribute a la PostSharp so the reflection hit is at compile-time and is therefore moot thanks to the CompileTimeValidation
method. I'm simply at a loss of how I'd do this. Doing an IsSubclassOf
scan isn't helpful as I'd get multiple false positives.
To give context, I have a base Entity
type. I derive from this type all over defining all kinds of Entity
types. I decorate these types, including the base Entity
, with a policy attribute. The PostSharp aspect verifies at compile-time certain policy constraints. I would like to be able to only decorate the base Entity
type with the aspect validation so that Entity
and all its derived types will be validated. I can see the validation already happen. However, it's handled as an Entity
and not DerivedEntity
. In order to evaluate the policy on DerviedEntity
, I need to decorate that class specifically. I would like to not do this if possible and only decorate Entity
.
Essentially, I want to centralize my validation on the base class as the validation schema is the same for all derived classes. However, the values in the derived classes can change and I need to do some bounds checking.
Edit: Let's add some code.
[EnforceMaxLifetimePolicy]
[LifetimePolicy]
public class Entity<T>
{
public string Key { get; set; }
public T Object { get; set; }
public TimeSpan EntityLifetime
{
get
{
var lifetimePolicy =
Attribute.GetCustomAttribute(GetType(), typeof(LifetimePolicyAttribute)) as LifetimePolicyAttribute;
return new TimeSpan(lifetimePolicy.Hours, lifetimePolicy.Minutes, 0);
}
}
}
[Serializable]
[AttributeUsage(AttributeTargets.Class)]
internal class LifetimePolicyAttribute : Attribute
{
public readonly short Hours;
public readonly short Minutes;
public LifetimePolicyAttribute(short hours, short minutes)
{
Hours = hours;
Minutes = minutes;
}
public LifetimePolicyAttribute()
{
Minutes = 1;
}
}
[Serializable]
[AttributeUsage(AttributeTargets.Class)]
internal class EnforceMaxLifetimePolicyAttribute : OnMethodBoundaryAspect
{
public override bool CompileTimeValidate(MethodBase method)
{
var type = method.GetType();
var lifetimePolicy = GetCustomAttribute(type, typeof(LifetimePolicyAttribute)) as LifetimePolicyAttribute;
if (lifetimePolicy != null && lifetimePolicy.Hours + lifetimePolicy.Minutes / 60 > 24)
{
throw new InvalidAnnotationException($"Lifetimes can not exceed 24 hours. The lifetime on {type.FullName} is invalid.");
}
return true;
}
}
[LifetimePolicy(hours: 24, minutes: 0)]
internal class ShoppingCartEntity : Entity<ShoppingCart>
{
}
As you can see, ShoppingCartEntity does not have the [EnforceMaxLifetimePolicy]
on it. It's on the base class Entity
. However, I still want the Enforce attribute entity to also apply to derived types which is why I leave the Inherited
flag to its default (true).
Apparently, PostSharp obviates this need. It supports automatic application to derived types via Multicasting. So, the lifetime attribute looks like so:
[Serializable]
[AttributeUsage(AttributeTargets.Class)]
[MulticastAttributeUsage(Inheritance = MulticastInheritance.Multicast)]
internal class EnforceMaxLifetimePolicyAttribute : OnMethodBoundaryAspect
{
public override bool CompileTimeValidate(MethodBase method)
{
var type = method.DeclaringType;
var lifetimePolicy = GetCustomAttribute(type, typeof(LifetimePolicyAttribute)) as LifetimePolicyAttribute;
if (lifetimePolicy.Hours + lifetimePolicy.Minutes / 60 > 24)
{
throw new InvalidAnnotationException($"Lifetimes can not exceed 24 hours. The lifetime on {type.FullName} is invalid.");
}
return true;
}
}
The [MulticastAttributeUsage(Inheritance = MulticastInheritance.Multicast)]
line will have PostSharp automatically apply it to the ShoppingCartEntity
and others.