Ever been in a situation where you notice that one of the team-members used classes from an assembly that really shouldn't be used in that part of the code? For instance, accessing the data layer from the presentation layer. It's not always easy to keep an eye on improper assembly usage. You could keep an eye on the referenced assemblies manually. You could write an FxCop rule that checks for disallowed assembly references. There's a lot of stuff you can do, but it'll always be an after-the-facts check. By that time, the 'illegal' code is already there.
Wouldn't it be cool if you could break the compilation whenever a developer tries to build code that has improper assembly usage? Actually, with PostSharp, we can do just that. You can simply create an aspect like this:
[Serializable]
[AttributeUsage(AttributeTargets.Assembly)]
public class SanityCheck : OnMethodInvocationAspect
{
public override bool CompileTimeValidate(System.Reflection.MethodBase method)
{
var methodName = method.DeclaringType.FullName + "." + method.Name;
var message = new Message(SeverityType.Fatal, "ProhibitedMethodCall", String.Format(
"Sorry, but we can't allow you to call {0} from the current assembly", methodName),
"SanityCheck");
MessageSource.MessageSink.Write(message);
return false;
}
}
First of all, we declare that the SanityCheck attribute can only be applied on the assembly level. Notice that we inherit from OnMethodInvocationAspect. This aspect is applied on events, properties, and methods. So basically, whenever you call a property or method or try to hook to an event in an assembly that you've applied this attribute to, our SanityCheck aspect will run. Well, actually we won't get that far. We override the virtual CompileTimeValidate method where we display a message and then we return false. Meaning that this compilation is invalid.
Suppose you've used the attribute in the AssemblyInfo.cs file of your presentation layer like this:
[assembly: SanityCheck(AttributeTargetAssemblies = "System.Data")]
If you try to compile the following code:
DataTable table = new DataTable();
table.NewRow();
You'll see the following line in your build output:
EXEC : error ProhibitedMethodCall: Sorry, but we can't allow you to call System.Data.DataTable.NewRow from the current assembly
And most importantly:
========== Build: 0 succeeded or up-to-date, 1 failed, 0 skipped ==========
Obviously, this will only cause compile errors when you try to touch the prohibited parts within the assembly where you used the SanityCheck attribute. If you call a method in another assembly that is allowed to touch System.Data, it will not cause a compile error.
Pingback: NHibernate and virtual methods/properties - NHibernate blog - NH Forge