Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Open Generic Types in StructureMap
This is taken from the StructureMap docs at http://structuremap.sourceforge.net/Generics.htm.  The code samples will probably be more readable there.  If you listen to Chad & I's appearance on Herding Code, this is the example of IoC usage I was describing (poorly).

 

StructureMap directly supports open generic types*.  This is most easily explained by a demonstration.  I'm currently working on an MVC application that uses a lot of semi-RESTful services that return Json.  These Json service methods are implemented by methods on a Controller class that by convention, all return a single object:

    public class FindAddressController

    {

        public Address FindAddress(long id){}

        public Continuation WhatShouldTheUserDoNext() {}

    }

When these methods are executed in our MVC applications, the output objects (Address or Continuation) are serialized to Json by other code and sent down to the client.  It's worked out well, but there's one little wrinkle from the code above that we stumbled upon:

  1. Continuation is basically a dumb Data Transfer Object class that's perfectly suited for being Json serialized and consumed by JavaScript on the client
  2. Address is an Entity class in our Domain Model, and very poorly suited to Json serialization.  Besides the fact that we probably wouldn't want the shape of our Domain Model classes to be coupled to the client JavaScript, the Entity classes are jagged, have lots of lazy loaded members, and contain a fair amount of data we may not want to send down to the client (plus the Json serialization does poorly with bidirectional dependencies).  Direct Json serialization is out.

What we needed to do was convert the Entity objects (and any other object that wasn't suited to Json serialization and client usage) into a UI-friendly Data Transfer Object.  Other objects like Continuation that were already Json friendly, we could just use as is.  The solution we came up with was the "ObjectFlattener:"

    public class ObjectFlattener

    {

        private readonly IContainer _container;

 

        // You can inject the IContainer itself into an object by the way...

        public ObjectFlattener(IContainer container)

        {

            _container = container;

        }

 

        // This method can "flatten" any object

        public object Flatten(object input)

        {

            var flattener = _container.ForGenericType(typeof (IFlattener<>))

                .WithParameters(input.GetType())

                .GetInstanceAs<IFlattener>();

 

            return flattener.ToDto(input);

        }

    }

The ObjectFlattener can take in any object, select a strategy for "flattening" that object into something Json friendly, and return the Json friendly object.  ObjectFlattener is dirt simple.  It simply finds the correct IFlattener for the object type passed into the Flatten(object) method:

    public interface IFlattener

    {

        object ToDto(object input);

    }

ObjectFlattener needs to find the corrent IFlattener for the object Type passed in, so it's really looking for the type:

    public interface IFlattener<T> : IFlattener

    {

    }

where "T" is the type of object passed into the ToDto(object) method.  When an Address object is passed into ToDto(), ObjectFlattener finds the default instance of IFlattener<Address>.  The "ForGenericType().WithParameters().GetInstanceAs<T>()" syntax is a helper expression to create and request a closed generic type from an open generic template and the appropriate generic parameter types.

Now, we said that many objects like Continuation in our system are DTO's to begin with and don't need to be "flattened."  For those objects, we use a "Nullo" implementation of IFlattener that just returns the object passed in without any transformation:

    public class PassthroughFlattener<T> : IFlattener<T>

    {

        public object ToDto(object input)

        {

            return input;

        }

    }

The Address class needs some transformation, so we'll create an AddressFlattener class:

    public class AddressFlattener : IFlattener<Address>

    {

        public object ToDto(object input)

        {

            var dto = createDTO((Address) input);

            return dto;

        }

 

        private object createDTO(Address input)

        {

            // creates the AddressDTO object from the

            // Address object passed in

        }

    }

Great, but now let's move on to registering these classes with a Container.  This unit test fixture from the code illustrates this very scenario:

        [SetUp]

        public void SetUp()

        {

            container = new Container(x =>

            {

                // Define the basic open type for IFlattener<>

                x.ForRequestedType(typeof (IFlattener<>)).TheDefaultIsConcreteType(typeof (PassthroughFlattener<>));

 

                // Explicitly Register a specific closed type for Address

                x.ForRequestedType<IFlattener<Address>>().TheDefaultIsConcreteType<AddressFlattener>();

            });

        }

In the code above we registered a specific concrete type for IFlattener<Address>, and the open generic type PassthroughFlattener<T> for requests to IFlattener<T>.  When we request an instance of IFlattener<Address>, StructureMap behaves as expected and returns a AddressFlattener object:

        [Test]

        public void asking_for_a_closed_type_that_is_explicitly_registered_returns_the_explicitly_defined_type()

        {

            container.GetInstance<IFlattener<Address>>()

                .ShouldBeOfType<AddressFlattener>();

        }

Now, we'll ask for an instance of IFlattener<Continuation>.  You'll notice that we didn't explicitly register that specific type.  When the Container gets the request for IFlattener<Continuation>, it first looks to see if it already knows how to build that specific type.  In this case the Container doesn't already know about IFlattener<Continuation>, but it does know about the open type IFlattener<T> template, so it can use that configuration to create the closed type IFlattener<Continuation>:

        [Test]

        public void asking_for_a_closed_type_that_is_not_explicitly_registered_will_close_the_open_type_template()

        {

            container.GetInstance<IFlattener<Continuation>>()

                .ShouldBeOfType<PassthroughFlattener<Continuation>>();

        }


 

* Anytime I read one of those "look I'm so clever I can write my own IoC container in 50 lines of code" blog posts, my immediate response is "oh yeah, can you handle open generic types smart guy?"


Posted Tue, Jan 13 2009 9:24 AM by Jeremy D. Miller
Filed under:

[Advertisement]

Comments

Jeremy D. Miller -- The Shade Tree Developer wrote Chad & I Talk FubuMVC / SOLID on Herding Code
on Tue, Jan 13 2009 9:49 AM

The Herding Code guys just posted an interview with Chad &amp; I on FubuMVC and SOLID principles . The

Community Blogs wrote Chad & I Talk FubuMVC / SOLID on Herding Code
on Tue, Jan 13 2009 10:23 AM

The Herding Code guys just posted an interview with Chad &amp; I on FubuMVC and SOLID principles . The

Koistya `Navin wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 12:29 PM

Nice nice.. I like the idea.

Will wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 1:27 PM

Excellent!  This is a far more elegant approach than I've been using for the last couple of years.

zihotki wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 2:26 PM

Thank you, your way is very cool. Previously I've used anonymous objects for this but at the moment I think I need to refactor that code.

Chris Patterson wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 3:40 PM

I love the way you can return the instance as a non-generic interface yet resolve based on the open generic and a type. That rocks.

Jeremy D. Miller wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 3:50 PM

@Chris,

I'm pretty sure Windsor has that too.  I got that from something I saw Ayende do one time.

Enrique Allegretta wrote re: Open Generic Types in StructureMap
on Tue, Jan 13 2009 5:51 PM

TOUCHE!!!!

Reflective Perspective - Chris Alcock » The Morning Brew #264 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #264
on Wed, Jan 14 2009 3:37 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #264

DotNetShoutout wrote Open Generic Types in StructureMap
on Thu, Jan 15 2009 5:48 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

ThisGlobe.com Blogs » Chad & I Talk FubuMVC / SOLID on Herding Code wrote ThisGlobe.com Blogs &raquo; Chad &amp; I Talk FubuMVC / SOLID on Herding Code
on Fri, Jan 16 2009 10:10 PM

Pingback from  ThisGlobe.com Blogs &raquo; Chad &amp; I Talk FubuMVC / SOLID on Herding Code

#.think.in wrote #.think.in infoDose #16 (12th Jan - 16th Jan)
on Sun, Jan 18 2009 5:31 PM

#.think.in infoDose #16 (12th Jan - 16th Jan)

Jon Erickson wrote re: Open Generic Types in StructureMap
on Fri, Sep 18 2009 2:51 PM

Is that an ITypeScanner implementation that can handle the generics automatically, so we don't have to explicitly register each one?

Alex Meyer-Gleaves wrote Registering open generic interface types in Autofac
on Sun, Dec 20 2009 4:22 AM

Registering open generic interface types in Autofac

Paul Linton wrote re: Open Generic Types in StructureMap
on Wed, Jan 20 2010 11:24 PM

Is there a way to have a nested open generic.  Something like

ForRequestedType(typeof(IHandle<Event<>>))

The syntax IHandle<Event<>> seems to be illegal C#

Jeremy D. Miller wrote re: Open Generic Types in StructureMap
on Thu, Jan 21 2010 9:15 AM

@Paul,

I don't think so, but even if you could, I think you'd really want to just go:

interface IEventHandler<T> : IHandle<Event<T>>

Add a Comment

(required)  
(optional)
(required)  
Remember Me?