Damon Payne: Hand waving software architect

103db signal to noise ratio at < .03% total harmonic distortion
Solution Architect, software developer, geek
Damon Payne at Blogged
2009 Microsoft MVP - Client App Dev
2007 Microsoft MVP - Solution Architecture
 Thursday, September 18, 2008
« Run time is design time for AGT [4] | Main | AGT[4] and AGT[5] »

The AGT (Argentum Tela) series of articles is an effort to do two things.  Usually an idea is presented only in its finished form.  The first goal is to do some Reality Blogging, to show an idea evolve over time without pulling any punches.  The second goal, and the example vehicle for the evolution aspect, is an extensible design-time environment similar to what we have in Visual Studio 2008.  This type of application has all sorts of interesting uses.  My example is a Home Theater layout tool.  Read the entire saga: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Tangent: Dynamic proxy generation in Silverlight using Reflection Emit

In the last article I made mention of a tangent, an evil waste of time.  If a component asks for an IService implementation that cannot be found, I started out throwing an exception saying so.  Then I found a situation where I wanted to allow a Service to be registered after it was needed, and only have an exception be thrown if the missing service was accesses before it could be somehow injected later.  This is the solution.

While I’m not very interested in Dynamic Languages right now, I am reaping the benefits of Microsoft’s attention to them.  Without consideration for the DLR in Silverlight, I doubt they would have included such a complete Reflection Emit implementation.

ServiceManager

We started out with a ServiceManager.Resovle method that looked like this:

        public static object Resolve(Type t)

        {

            object svc = null;

            if (_services.ContainsKey(t))

            {

                svc = _services[t];

            }

            else

            {

                throw new NotSupportedException(string.Format("A provider for {0} was not found", t));

            }

            return svc;

        }

We need to replace what’s in the else clause and make ServiceManager and ComponentBuilder a little smarter.  The first step is to create a way to keep track of the situation inside ComponentBuilder:

        /// <summary>

        /// Key=IService type, value=list of objects waiting on that IService

        /// </summary>

        private static Dictionary<Type, List<object>> _lateBinding;

        static ComponentBuilder()

        {

            _lateBinding = new Dictionary<Type, List<object>>();

        }

                {

                    object svc = null;

                    try

                    {

                        svc = ServiceManager.Resolve(propType);

                    }

                    catch (NoImplementationException)//TODO: fix this so an exception is not thrown?

                    {

                        svc = SetupLateBinding(propType, component);

                    }

        private static object SetupLateBinding(Type interfaceType, object client)

        {

            object svc = InterfaceMocker.GenerateProxy(interfaceType);

 

            if (!LateBinding.ContainsKey(interfaceType))

            {

                LateBinding.Add(interfaceType, new List<object>());

            }

 

            LateBinding[interfaceType].Add(client);

 

            return svc;

        }

We change the ServiceManager to throw NoImplementationException instead of NotSupportedException.  When ServiceManager is given something new to Manage now, we check to see if anyone out there was waiting on a service of this type:

        private static void CheckLateBinding(Type interfaceType, object provider)

        {

            if (ComponentBuilder.LateBinding.ContainsKey(interfaceType))

            {

                List<object> clients = ComponentBuilder.LateBinding[interfaceType];

                foreach (object c in clients)

                {

                    var propQuery = from p in c.GetType().GetProperties().AsQueryable<PropertyInfo>()

                                    where p.PropertyType == interfaceType

                                    select p;

                    foreach (PropertyInfo propInfo in propQuery)

                    {

                        propInfo.SetValue(c, provider, null);

                    }

                }

                ComponentBuilder.LateBinding.Remove(interfaceType);

            }

        }

 

 

TypeMocker

The magic of all the code shown above is contained in TypeMocker.  Our goal is this: given an Interface type we cannot find an implementation for, make up a very simple placeholder one which could potentially do something useful if we call methods on it.  Unfortunately the most useful things raise more issues than they solve in .NET:

·         I don’t have pointers here, so I can’t very well try to replace “this”, nor get at the enclosing object instance to do any sneaky replacement.

·         If I wanted to do a delegation model, where the proxy ultimately just obtained a ‘normal’ instance and delegated to that via inheritance or something, those methods would need to be virtual and I don’t want to force that on users of this library, especially myself.

The first thing we need to be able to do is create the assembly. You may recognize the name from the Badge of Honor article.

        private static ModuleBuilder GetDynamicModuleBuilder()

        {

            if (null == _dynamicModuleBuilder)

            {

                AppDomain currentDomain = AppDomain.CurrentDomain;

                AssemblyName proxyName = new AssemblyName("DamonPayne.IoC.TempProxy");

                AssemblyBuilder aBuilder = currentDomain.DefineDynamicAssembly(proxyName, AssemblyBuilderAccess.Run);

                //

                ModuleBuilder module = aBuilder.DefineDynamicModule(proxyName.Name);

                _dynamicProxyAssembly = aBuilder;

                _dynamicModuleBuilder = module;

            }

 

            return _dynamicModuleBuilder;

        }

 

        private static AssemblyBuilder _dynamicProxyAssembly;

        private static ModuleBuilder _dynamicModuleBuilder;

Now we can work on dynamically creating a class that implements an interface in GenerateProxy:

        public static object GenerateProxy(Type t)

        {

            if(!t.IsInterface)

            {

                throw new ArgumentException(t + " Is not an interface");

            }

            //Useful:

            //http://msdn.microsoft.com/en-us/library/3y322t50(VS.95).aspx

            object o = null;

            ModuleBuilder module = GetDynamicModuleBuilder();

            //

            TypeBuilder typeBuilder = module.DefineType(t.Name+"TempProxy",

                TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Class,typeof(object) );

            typeBuilder.AddInterfaceImplementation(t);

We create a class that extends Object and implements interface “t”.  Because we are writing IL ourselves, lots of things that would ordinarily be done by the compiler have to be taken into consideration.  This includes calling the base class constructor from within our own:

            ConstructorBuilder conBuilder =

                typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName, CallingConventions.Standard, Type.EmptyTypes);//TODO: use Type.EmptyTypes elsewhere!

            //Define the reflection ConstructorInfor for System.Object

            ConstructorInfo conObj = typeof(object).GetConstructor(new Type[0]);

 

            //call constructor of base object

            ILGenerator il = conBuilder.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);

            il.Emit(OpCodes.Call, conObj);

            il.Emit(OpCodes.Ret);

This next bit is tedious but easy to understand once you know what you’re trying to do.  Create a method on my new type for each interface method I need to implement.  The implementation currently just throws an exception saying that our attempt to allow friendly late binding failed:

            List<MethodInfo> interfaceMethods = new List<MethodInfo>();

            interfaceMethods.AddRange(t.GetMethods());

 

            Type[] extraInterfaces = t.GetInterfaces();

            foreach (Type extraIface in extraInterfaces)

            {

                interfaceMethods.AddRange(extraIface.GetMethods());

            }

 

            foreach (MethodInfo method in interfaceMethods)

            {

                ParameterInfo[] mParams = method.GetParameters();

                Type[] paramTypes = new Type[mParams.Length];

                for(int i = 0; i < mParams.Length; ++i)

                {

                    paramTypes[i] = mParams[i].ParameterType;

                }

 

                MethodBuilder methodBuilder =

                    typeBuilder.DefineMethod(method.Name,

                    MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.SpecialName, method.ReturnType, paramTypes);

               

                ILGenerator ilGen = methodBuilder.GetILGenerator();

                if (method.Name.Equals("Startup") || method.Name.Equals("Shutdown")) //TODO: dehackify this

                {

                    ilGen.Emit(OpCodes.Ret);

                }

                else

                {

                    ilGen.ThrowException(typeof(NoImplementationException));

                }               

            }

 

            Type finalType = typeBuilder.CreateType();

 

            o = Activator.CreateInstance(finalType);//TODO: replace other default constructor code with Activator

 

            return o;

There’s probably some work to do here to make sure attempts at creating  a proxy for the same type more than once are handled and so forth, but I’ll get to that when I decide on a unit test platform.  For now, its solving my late order-of-creation problems and would event support a circular dependency should one arrive.  My region manager/Page startup now goes through this sequence:

1.       We ask ServiceManager to handle Page, which is an IRegionManager.

2.       Page contains a RootPresenter, RootPresenter is created and needs an IToolboxService.

3.       IToolboxService is not found, but we get a dynamically created IToolboxServiceTempProxy instead.

4.       The UserControl_Loaded event of Page is called, which creates the toolbox service.

5.       Page gets a real IToolboxService implementation and everyone is happy.

At the end of this article, I am clearly not happy with the relationship between ComponentBuilder and ServiceManager.  I’m going to have to fix that, but first I want to get some more work done on my actual problem domain.

 

The source code is the same as what I posted for AGT[4].