Monday, January 25, 2010

.NET hacking made easy

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

image

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

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

Monday, January 18, 2010

Collaborating with Xaf and IO module

.NET provides a lot of ways to collaborate between systems. All of them are based on data serialization. XPO is missing that, and have a client project that could not live without some generic type of serialization. So I create a simple but powerful IO engine

  • It can serialize any object that inherits DevExpress.Xpo.XPBaseObject.
  • It will also serialize any object that is related to the root object of serialization.
  • To control the export engine A serialization graph can be provided .
  • Object's properties can be serialized according to Serialization Strategy enumeration 

    public enum SerializationStrategy

    {

        SerializeAsValue = 0,

        SerializeAsObject,

        DoNotSerialize

    }

  • Friendly keys support. For importing data from other RDBMS systems that do not have a GUID primary key

Scenario

  1. From a non xaf application I need to import customers based on some filtering taken from a xaf application.
  2. The design of Customer object differs between applications. In the Xaf application Customer inherits user but in the non Xaf does not. Property names also differ

The above problems can be solved with IO module with easy

2 apps communicating
Lets try to build a WCF service to enable communication. We are going to host it on IIS so create a new WCF Service Application

 image
reference the following assemblies take a not that I also reference Solution3.Web which is my client front end

 image

add an HttpModule to setup an application instance at HttpApplication init. We need to setup an application instance to be sure that the XPDictionary modifications through controllers take place

image

    public class InitXafAppModule:IHttpModule

    {

        public void Init(HttpApplication context) {

            WebApplication.SetInstance(context.Session, new Solution3AspNetApplication());

 

            if (ConfigurationManager.ConnectionStrings["ConnectionString"] != null) {

                WebApplication.Instance.ConnectionString =

                    ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

            }

 

            WebApplication.Instance.Setup();

            WebApplication.Instance.Start();

 

        }

 

        public void Dispose() {

 

        }

    }


Our service is going to have the following contract

    [ServiceContract]

    public interface IIOService {

        [OperationContract]

        string Export(string typeName, string criteria);

 

        [OperationContract]

        void Import(string xml);

    }

and implemented as

    public class IOService : IIOService

    {

        public string Export(string typeName, string criteria)

        {

            var session = WebApplication.Instance.ObjectSpaceProvider.CreateUpdatingSession();

            var xpBaseObjects = new XPCollection(session, ReflectionHelper.GetType(typeName), criteria).OfType<XPBaseObject>();

            XDocument xDocument = new ExportEngine().Export(xpBaseObjects);

            var stringWriter = new StringWriter();

            xDocument.Save(stringWriter);

            return stringWriter.GetStringBuilder().ToString();

        }

 

        public void Import(string xml)

        {

            var unitOfWork = new UnitOfWork(WebApplication.Instance.ObjectSpaceProvider.CreateUpdatingSession().DataLayer);

            var importEngine = new ImportEngine();

            importEngine.ImportObjects(XDocument.Parse(xml), unitOfWork);

            unitOfWork.CommitChanges();

        }

    }

Now we are able to call the WCF service to get the xml of Customers of the Xaf application and transform it using extension methods defined in IO module like

var xElement = XDocument.Parse(xml).Root;


IEnumerable<NonXafCustomer> customers =xElement.SerializedObjects("Customer").Select(element => new NonXafCustomer {Name = element.Property("UserName").Value});



ps:Import/Export action within a xaf application are inside export predefined category and they are available for all objects



image

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

Monday, January 11, 2010

WorldCreator2 source code is out and stable

 

I have never really release a stable version through this blog of the module that allows you to create dynamic types through templating and scripting, so I am doing it now that the code is stable enough.

To get the code read http://apobekiaris.blogspot.com/2009/10/how-to-get-expand-latest-version.html

New Features

eXpand Application Solution project item template

John Pouliezos created a better design experience for all eXpand users by adding a New Project Item template.

10-1-2010 8-28-35 μμ

Using the above template you do not have to worry any more about setting up assembly references or registering modules/ persistent classes

To install the template run the vs_setup.cmd batch file located at the root of eXpand folder after running buildall.cmd. You may want to uncomment the x86 lines if your system is not x64

10-1-2010 8-48-30 μμ

or change your VS path if its not installed at the default location.

Runtime assembly validation

Your dynamic assemblies may not compile any more if you mess your scripts, up to now compilation happen every application startup, so to save that startup time I have added a new validate action that can validate your scripts and display any errors at persistent assembly compile errors property without restart

10-1-2010 8-57-11 μμ

Auto generation of associated collection

Again to speed things up and since xpo requires to add a collection property to the other part of association, when u create a new referenced object association you can automatically create the other part as well.

image

take a note that the “Auto Generate Other Part Member” now works only on reference properties and not the other way around, also the generation takes place at persistent reference memberInfo saving only if member info has a persistent association attribute

ps:if you do not use the eXpand new project item template I suggest you add the following code at your module.designer.cs file that is responsible for loading all persistent classes

image image

Subscribe to XAF feed
Subscribe to community feed

DiggIt!