Archive for April 2009


NInject vs. StructureMap vs. I Made This

April 24th, 2009 — 1:10pm

As I’ve mentioned before my team is working on a large scale ASP.NET MVC application and we’re using dependency injection to decouple our data access layer from the application. Initially we were using NInject as our IoC tool of choice. It’s lightweight and has a really nice configuration DSL and it’s pretty fast.

The problem arose when trying to get NInject’s scope caching to work. It has this nifty feature where it will cache the objects it creates and you could specify the scope of that caching…for example SingletonBehavior, or OnPerSessionBehavior. That’s great but I couldn’t make it work, regardless of the configuration it would always return a brand new object every time.

So then I went and checked out StructureMap. It’s a little more complex to get set up and I personally don’t think it’s registration DSL is as nice as NInject’s but it works. It has a similar caching feature where you can specify an Instance Scope of Application, Session, Request, Thread, etc. Same problem though, no matter how I configured it the caching was always at the Application level. So every session in the app would get back the same objects…not very secure on the data front.

Now, I am completely open to the possibility that I don’t know enough about either of these tools and that the roadblocks I ran into were user error. However, I realized I was spending way too much time on something that is essentially a very simple problem. Here’s what I needed out of an IoC tool:

1. Constructor Injection – I don’t really care about property or method injection, just constructors
2. Cache the Output at Session Level
3. Easy Registration with Constructor Args

So the following is what I came up with to solve the problem:

namespace DI {
 
    internal class InjectionBinder {
        public Type Abstract { get; set; }
        public Type Concrete { get; set; }
        public object[] CtorArgs { get; set; }
    }
 
    /// <summary>
    /// A lightweight dependency injection tool that supports constructor injection and HttpContext.Session caching.
    /// </summary>
    public static class Injector {
 
        private const string SessionBindingKey = "__InjectorBindings__";
        private const string SessionCacheKey = "__InjectorCache__";
 
        private static List<InjectionBinder> _bindings {
            get {
                if (HttpContext.Current.Session[SessionBindingKey] == null)
                    HttpContext.Current.Session[SessionBindingKey] = new List<InjectionBinder>();
                return (List<InjectionBinder>)HttpContext.Current.Session[SessionBindingKey];
            }
            set { HttpContext.Current.Session[SessionBindingKey] = value; }
        }
 
        private static List<KeyValuePair<Type, object>> _cache {
            get {
                if (HttpContext.Current.Session[SessionCacheKey] == null)
                    HttpContext.Current.Session[SessionCacheKey] = new List<KeyValuePair<Type, object>>();
                return (List<KeyValuePair<Type, object>>)HttpContext.Current.Session[SessionCacheKey];
            }
            set { HttpContext.Current.Session[SessionCacheKey] = value; }
        }
 
        /// <summary>
        /// Returns a concrete instance of the specified abstract type using the provided bindings and 
        /// attempts to satisfy that type's constructor arguments.
        /// </summary>
        /// <param name="abstractType">The abstract type of the object to initialize.</param>
        /// <returns></returns>
        public static object GetInstance(Type abstractType) {
 
            // Return the item from the cache if it's in there
            var cache = _cache.ToDictionary(o => o.Key, o => o.Value);
            if (cache.ContainsKey(abstractType))
                return cache[abstractType];
 
            // If the type is in the bindings, return the concrete
            var foundBinding = _bindings.Find(b => b.Abstract == abstractType);
            if (foundBinding != null)
                return Activator.CreateInstance(foundBinding.Concrete, foundBinding.CtorArgs);
 
            object obj = null;
            var ctors = abstractType.GetConstructors().ToList();
 
            // If the type has no constructor, then just create one
            if (ctors.Count == 0)
                return Activator.CreateInstance(abstractType);
 
            // If there are constructors, find one which takes arguments of the types we have binders for
            ctors.ForEach(c => {
                var prms = c.GetParameters().ToList();
                var args = new List<object>();
                var haveBindings = true;
                prms.ForEach(p => {
                    var binding = _bindings.Find(b => b.Abstract == p.ParameterType);
                    if (binding == null) { haveBindings = false; }
                    else { args.Add(Activator.CreateInstance(binding.Concrete, binding.CtorArgs)); }
                });
                if (haveBindings) { obj = Activator.CreateInstance(abstractType, args.ToArray()); }
            });
 
            return obj;
        }
 
        /// <summary>
        /// Returns a concrete instance of the specified abstract type using the provided bindings and 
        /// attempts to satisfy that type's constructor arguments.
        /// </summary>
        /// <typeparam name="TAbstract">The abstract type of the object to initialize.</typeparam>
        /// <returns></returns>
        public static TAbstract GetInstance<TAbstract>() where TAbstract : class {
            return GetInstance(typeof (TAbstract)) as TAbstract;
        }
 
        /// <summary>
        /// Creates a binding rule between an abstract type and a concrete type.
        /// </summary>
        /// <typeparam name="TAbstract">The abstract type for the binding.</typeparam>
        /// <typeparam name="TConcrete">The concrete type for the binding</typeparam>
        /// <param name="ctorArgs">The list of constructor arguments to use when initializing the instance of the concrete type.</param>
        public static void Bind<TAbstract, TConcrete>(object[] ctorArgs) {
            var binding = _bindings.Find(b => b.Abstract == typeof(TAbstract));
            if (binding != null) {
                binding.Concrete = typeof(TConcrete);
                binding.CtorArgs = ctorArgs;
            } else {
                _bindings.Add(new InjectionBinder {
                    Abstract = typeof(TAbstract),
                    Concrete = typeof(TConcrete),
                    CtorArgs = ctorArgs
                });
            }
        }
 
        /// <summary>
        /// Clears all Injector binding rules and empties the internal cache.
        /// </summary>
        public static void Kill() {
            HttpContext.Current.Session[SessionBindingKey] = null;
            HttpContext.Current.Session[SessionCacheKey] = null;
        }
    }
}

And here’s how we register the bindings:

private static void BindInjectionContainer() {
    Injector.Kill(); // Clears out previously created bindings and empties the cache
 
    var currentClient = new Client(); // Doesn't matter...just an example
    var currentUser = new User();
    var currentCulture = "en-US";
 
    Injector.Bind<IClientRepository, ClientRepository>(new object[] { currentClient, currentCulture });
    Injector.Bind<IUserRepository, UserRepository>(new object[] { currentClient, currentCulture, currentUser });
    Injector.Bind<ISecurityRepository, SecurityRepository>(new object[] {});
}

And lastly, here’s how you request an object:

var secRepo = Injector.GetInstance<ISecurityRepository>();

So, it may be ridiculous to roll-your-own on the IoC container but as you can see it was pretty simple to get what I needed. It’s very solution specific but what’s wrong with that. In this instance it’s pretty easy to swap out for something more feature rich if necessary but for now…I call YAGNI on all that.

3 comments » | Uncategorized

Server Block Formatting in Visual Studio

April 22nd, 2009 — 1:04pm

This is one of those more-for-me-than-you posts.

If you’re working with ASP.NET MVC (or god help you ASP Classic) you’ll likely be annoyed by the way server blocks are formatted in Visual Studio. In particular closing curly braces seem to throw the formatting engine a real curve-ball. For example, you type something like this:

<% } %>

But after slapping return, you ultimately get this lovely thing:

<%
       }
     %>

Thanks Visual Studio!

So how do we fix this. Well, you’d like it if hitting Ctrl+K+D took care of this for you, but apparently that ignores server blocks. Next you’ll hunt and peck your way through the mounds of Visual Studio options in Tools > Options…but that search will ultimately prove fruitless. Finally, you’ll turn to the humble macro…I know macro can be a dirty word but in this case it’s really your only option.

Here’s how this goes:

1. Mouse over to Tools > Macros > Macro IDE
2. Notice that Visual Studio macros are in Visual Basic…really? I know…but go with it.
3. Right-click ‘My Macros’
4. Choose ‘Add New Item’
5. Choose ‘Module’ in the dialog
6. Enter the following method in the newly created Module:

Public Module FormatCurrentFile
 
    Sub FixServerBlocks()
        Dim selection As TextSelection = DTE.ActiveDocument.Selection
        Dim fixed As String = "<% } %>"
        Dim regex As String = "\<\%:Wh*\}:Wh*\%\>"
        While selection.FindPattern(regex, vsFindOptions.vsFindOptionsRegularExpression)
            selection.ReplacePattern(regex, fixed, vsFindOptions.vsFindOptionsRegularExpression)
        End While
    End Sub
 
End Module

Now you can go through Tools > Options > Keyboard and map a key command to this macro…I chose Ctrl+K+C for example since it’s close to the other magic formatting keys that you’ll want to run right before the macro. There you have it.

Thanks to some help from StackOverflow.com for this solution: http://stackoverflow.com/questions/720042

Comment » | .NET, General Programming

Back to top