I am in a mood this year so I decided to spend some time with a problem that I have left in the past.
The problem
I really wanted to use Runtime member for existent Types and also enable validation for those members. But for the reason i have describe in this post it was not possible, unless I change DevExpress source code and recompile (bad choice). DevExpress and any vendor could, for their own reasons not support your suggestion. Maybe for good or for bad I really not care, what I care is how to use Runtime member for existent Types with validation enable.
So I will try a different approach I will use CThru which is an AOP open source interception framework based on the Typemock Open-AOP API to “For any (past or future) object of type X in the system, make property Y of said object already return a fake value” and in my case that will be
For any (past or future) object of type RulePropertyValueProperties, make property TargetPropertyName return my version of code
Lets say for a moment that you know nothing on CThru, and even the idea of learning a “strange AOP” frameworks frightens you cause of the luck of time. I ll explore the proccess of doing the above like I know nothing about it also
Previous post of mine about AOP can be found here
1st Step
Go to their site at codeplex http://cthru.codeplex.com/ and read all stuff under Learn section. There are not many info there but very informative as you have already notice if you follow the above links I posted.
2nd Step
Download the source code and… here it is—> a CThru.Tests project. That will do all the job!!! There should be tests explaining how the framework works. After exploring the solution I came across TraceAspect class, which was mention to me at CThru site. That class/aspect can enable logging to any class within .Net framework except for classes within mscorlib. Just take a look how is is to implement it
public class TraceAspect : Aspect {
private readonly Predicate<InterceptInfo> _shouldIntercept;
private readonly string _logPath;
public TraceAspect(Predicate<InterceptInfo> shouldIntercept, string logPath) {
_shouldIntercept = shouldIntercept;
_logPath = logPath;
}
public TraceAspect (Predicate<InterceptInfo> shouldIntercept) {
_shouldIntercept = shouldIntercept;
}
public override bool ShouldIntercept (InterceptInfo info) {
return _shouldIntercept(info);
}
public override void ConstructorBehavior (DuringCallbackEventArgs e) {
DoTrace(e);
}
public override void MethodBehavior (DuringCallbackEventArgs e) {
DoTrace(e);
}
protected virtual void DoTrace (DuringCallbackEventArgs e) {
string message = GetMessage(e);
DoTrace(message);
}
protected virtual void DoTrace(string message) {
if (_logPath != null) System.IO.File.AppendAllText(_logPath, message + Environment.NewLine);
Console.WriteLine(message);
}
protected virtual string GetMessage(DuringCallbackEventArgs e) {
string message;
try {
string parameters = GetParameters(e);
message = string.Concat(e.TypeName, ".", e.MethodName, "(", parameters, ")");
}
catch (Exception ex) {
message = ex.ToString() + Environment.NewLine;
}
return message;
}
protected virtual string GetParameters (DuringCallbackEventArgs e) {
var list = new List<string>();
if (e.ParameterValues != null) {
foreach (var param in e.ParameterValues) {
var value = param != null ? param.ToString() : "null";
list.Add(value);
}
}
return string.Join(", ", list.ToArray());
}
}
Just inheriting from CThru.Aspect abstract class and implement ShouldIntercept abstract method to return a Predicate<InterceptInfo> delegate you are able to log any type you want with a very simple call like
var aspect = new TraceAspect(info => info.TargetInstance is IHttpHandler, @"C:\log.txt");
Very very powerfull and easy!!! many thnks for that wonderful API to Roy Osherove and TypeMock people.
So back to our case our aspect will look like
public class ExistentMembersEnableValidationAspect : Aspect {
static FieldInfo backingField;
public ExistentMembersEnableValidationAspect() {
backingField =
typeof (RulePropertyValueProperties).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(
info => info.Name == "propertyName").Single();
}
public override bool ShouldIntercept(InterceptInfo info) {
return info.TargetInstance is RulePropertyValueProperties;
}
public override void MethodBehavior(DuringCallbackEventArgs e) {
base.MethodBehavior(e);
if (e.MethodName == "set_TargetPropertyName") {
e.MethodBehavior = MethodBehaviors.SkipActualMethod;
backingField.SetValue(e.TargetInstance, e.ParameterValues[0]);
}
}
}
and the spec I wrote using MSpec is like
I am sure you can see why “hacking” is on my post title by now :) the above approach could be used to bypass a licensing system as well
To finalize this long post, I understand that some of you may not like that approach again for your own reasons that I would be happy to learn. So I am going to create a new platform suffix for eXpand modules the CThru platform suffix.
In order to use the above solution you have to register the eXpand.ExpressApp.WorldCreator.CThru assembly found in eXpand.DLL folder
But to those of you that like and want to use the above approach I need to remind you that eXpand has an open source lisence for CThru and TypeMock so please feel free to contact me so you can get your lisence to be used with eXpand
You may also wondering about speed implications. The following controller will fix that for you
public partial class RuleSetInitializationController : DevExpress.ExpressApp.Validation.RuleSetInitializationController
{
public RuleSetInitializationController()
{
InitializeComponent();
RegisterActions(components);
}
protected override void OnActivated()
{
if (Application != null) {
var module = (ValidationModule) Application.Modules.FindModule(typeof (ValidationModule));
if (module != null) {
CThruEngine.AddAspect(new ExistentMembersEnableValidationAspect());
CThruEngine.StartListening();
InternalMockManager.Locked = false;
module.RuleSetInitialized += (sender, args) => {
CThruEngine.StopListeningAndReset();
InternalMockManager.Locked = true;
};
module.InitializeRuleSet();
}
else {
Tracing.Tracer.LogWarning("Cannot find Validation module in Module list: {0}",typeof (ValidationModule).AssemblyQualifiedName);
}
}
}
}
since it will create the aspect only when its needed. And to prove my saying watch the next video