Run time is design time for AGT[8]

by Administrator 21. September 2008 12:32

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

Code Refactoring

I’m eager to get on to some of the cool next steps like the ability to move and resize things on the Design Surface, but my TODOs are starting to pile up.  I’d rather not get too far into this and find that some fundamental thing that was bothering me is now a real issue.  Using the Task List feature in VS2008, I’ve got 20 things I should at least look at.

Namespaces

I’ve been thinking lately about some other Silverlight projects I have on the back burner, and how nice it would be to keep them in mind for the IoC stuff.  Therefore, I should rename DamonPayne.AGT.IoC to just DamonPayne.AG.IoC.  I like the fact that VS2008 makes the default namespace of a project match the project name, but the tooling stops there.  I have to rename the project, edit the default namespace, edit the assembly name, and do a global find/replace on the top level namespace.  Lame.  58 occurances replaced, and the Design and HTLayout projects reflect the new name; it even built the first time.

Events

While sitting in traffic I had the idea to overload += for my AggregateEvent<T> and make it feel more like a built in .NET event type.  As it turns out this won’t work the way I wanted it too so I erased that TODO.  I also got rid of the static “Test” method in EventAggregator.

Reflection

I have several TODOs related to reflection code.  The first one suggests Caching Type and Property information to make reflection go faster.  I’ll leave that alone for now and come back to it.

Tidy up: I found that there is a Type.EmptyTypes static var, similar to EventArgs.Empty.  I found three places where I could substitute Type.EmptyTypes.

I also found that I had some duplicate interface code and I could add some things to my TypeExtensions static class:

       public static bool ImplementsInterface(this Type t, Type intefaceType)

        {

            if (intefaceType.IsInterface)

            {

                Type[] interfaces = t.GetInterfaces();

                if (interfaces.Contains<Type>(intefaceType))

                {

                    return true;

                }

            }

            return false;

        }

 

        public static bool ImplementsInterface(this object o, Type intefaceType)

        {

            return ImplementsInterface(o.GetType(), intefaceType);

        }  

I was able to go through ServiceManager and ComponentBuilder and clean up some code using these Extension methods.

Several calls to invoking a Type’s default constructor can be replaced with Activator.CreateInstance; this was done throughout.  The 1-arg constructor for Presenter types was also replaced with a call to Activator.

Control Extensions

On suspicion that I would need the ability to scale controls without distorting their aspect ratio in the future, I left myself a note to create an extension method to determine an aspect ratio preserving method:

    public static class ControlExtensions

    {

        public static double UniformScaleAmount(this Control c, double maxDimension)

        {

            double originaltWidth = c.Width;

            double originalHeight = c.Height;

            double uniformScaleAmount = Math.Min(maxDimension / originaltWidth, maxDimension/ originalHeight);

            return uniformScaleAmount;

        }

    }

At this point, I am staring sideways with some dread at the remaining TODO items.  The remaining items involve:

1.       Revisit the relationship between ServiceManager and ComponentBuilder to see what, if anything, should be done about the crosstalk.

2.       The ToolboxCategory code doesn’t work how I’d like, and fixing it will involve a lot of digging deeper on the Silverlight control templating framework.

3.       There are several visual notes I’ve made to myself, I will address those in the next article.

4.       Something is amuck with InterfaceMocker, it only works once.

5.       I need to decide how I want configuration to work.

Configuration can have its own article I think, for now I’ll at least look into ServiceManager, ComponentBuilder, and InterfaceMocker.

Reflection Emit Redux

Inside ServiceManager.GenerateProxy, I did not take into account the fact that more than one client may ask for a given interface implementation.  In my startup code, I was able to get “Duplicate Type name in assembly” from time to time.  I just needed to add a check before building the type mock:

private static Dictionary<string, object> _proxies;

            string proxyName = t.Name+"TempProxy";

            if (_proxies.ContainsKey(proxyName))

            {

                return _proxies[proxyName];

            }

 

            object o = null;

            ModuleBuilder module = GetDynamicModuleBuilder();

            //

            TypeBuilder typeBuilder = module.DefineType(proxyName,

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

            typeBuilder.AddInterfaceImplementation(t);

No more proxy errors.

ServiceManager loves ComponentBuilder

As soon as I introduced ComponentBuilder I should have moved all object creation code to it.  But I didn’t, so now here I am looking at several TODO items.  From ServiceManager.ObtainDefaultInstance:

                o = Activator.CreateInstance(t);

                //TODO: maybe use the ComponentBuilder to create the damn thing in the first place

                ComponentBuilder.ResolveServiceDependencies(o);

                ComponentBuilder.BuildPresenters(o);

ComponentBuilder.Create:

        public static T Create<T>() where T :new ()

        {

            T instance = new T();

 

            ResolveServiceDependencies(instance);

            BuildPresenters(instance);

            ServiceManager.ManageInstance(instance);//TODO: verify this was the right design choice later

 

            return instance;

        }

Furthur, ServiceManager.ManageInstance (which might have been a hairy issue otherwise) is used from only a single place, ComponentBuilder.Create.  Creating a non-generic override of Create is step 1:

        public static T Create<T>() where T :new ()

        {

            T instance = default(T);

            instance = (T)Create(typeof(T));          

            return instance;

        }

 

        public static object Create(Type t)

        {

            object instance = Activator.CreateInstance(t);

            ResolveServiceDependencies(instance);

            BuildPresenters(instance);

            return instance;

        }

I hate having to go back and write code that involves casting and Type, when generics are so much cleaner, but such is life.  While chasing down the resulting bugs, I realized Manage(List<Type> types) and ManageInstance(object provider) were almost the same method.  The Manage method is refactored to call ManageInstance:

        public static void Manage(List<Type> types)

        {

            Type baseServiceType = typeof(IService);

 

            foreach (Type t in types)

            {

                object instance = ObtainDefaultInstance(t);

                ManageInstance(instance);

            }

        }

Lovely, more TODOs off my list and some unintended good side effects as well.

Unit Testing

Unit Tests allow us to refactor with impunity.  The fact that this project was ~500 loc doesn’t hurt either.

I Google “Silverlight 2 beta 2 unit test” and look what I find: http://code.msdn.microsoft.com/silverlightut/Release/ProjectReleases.aspx?ReleaseId=1543

I have to admit I had been thinking about making my own or Porting XUnit, but now I don’t have to.  I’ve grown to like the minimalistic XUnit interface, and I feel blind without NCover, but this will do for now.

Source code: DamonPayne.AGT[8].zip (622.87 KB)

Tags:

Pingbacks and trackbacks (3)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

Damon Payne is a Microsoft MVP specializing in Smart Client solution architecture. 

INETA Community Speakers Program

Month List

Page List

flickr photostream