Using Structure Map to replace an abstract type in generic type parameter

Go To StackoverFlow.com

2

I have the following class with a generic type parameter.

public class ContentService<T> : IContentService where T : DataString, new()
{       
    public ContentService(IEnvironment environment, 
                          ILogger logger)
    {
        _environment = environment;
        _logger = logger;
    }
...
}

DataString is an abstract class and I want Structure Map to use a concrete implementation (XmlDataString) whenever my application creates an instance of ContentService. Structure Map is already injecting concrete implementations of IEnvironment and ILogger when I do this:

Version 1:

_contentService = ObjectFactory.GetInstance<ContentService<DataString>>();

... but when I step through, ContentService is created with a type parameter of DataString, not XmlDataString. I have these three entries in web.config that set default instances:

  • IEnvironment -> TestEnvironment
  • ILogger -> Log4NetLogger
  • DataString -> XmlDataString

The Structure Map site has this to say about generics, but I can't seem to relate that example to my actual problem.

This approach works, but I'm losing the auto wiring of the constructor arguments and it looks quite ugly: Version 2:

Type typeOfDataString = ObjectFactory.GetInstance<DataString>().GetType();
Type genericClass = typeof(ContentService<>);
Type constructedClass = genericClass.MakeGenericType(typeOfDataString);
_contentService = (IContentService)Activator.CreateInstance(constructedClass, 
            ObjectFactory.GetInstance<IEnvironment>(), 
            ObjectFactory.GetInstance<ILogger>());

Can anyone tell me what I'm doing wrong in the first version, or how I might improve the second version?

2012-04-03 23:07
by mafue
What happens if you call ObjectFactory.GetInstance<IContentService>()? If that doesn't work, can't you add IContentService → ContentService to your web.config - svick 2012-04-04 01:15
Thanks @svick, that works. I just inject the service as a whole. I was able to do that in code: x.For<IContentService>().Use<ContentService<XmlDataString>>(); but I haven't been able to find out how to put that in web.config (encoding the < & > doesn't work - mafue 2012-04-04 16:36


1

First, when you write ObjectFactory.GetInstance<ContentService<DataString>>(), you can't get ContentService<XmlDataString> back, because that's a different, incompatible type.

But it seems you actually want IContentService, so write that: ObjectFactory.GetInstance<IContentService>(). This will return the implementation of IContentService that you registered, so you should do that: register ContentService<XmlDataString> for IContentService. The fact that you have XmlDataString registered for DataString has nothing to do with this.

If you have problems representing the generic type ContentService<XmlDataString> in web.config, I think using the .Net representation of generic types (and not that of C#) should work. In your case, that would be something like ContentService`1[[XmlDataString]], probably with added namespaces and possibly assemblies too.

2012-04-04 17:09
by svick
I was trying to break up my dependencies and inject them separately, but I see that wasn't necessary now. Thanks for the answer!

Just in case anyone else gets stuck on representing generic types in web.config, here's what worked for me:

<DefaultInstance PluginType="{namespace1}.IContentService, {assembly1}" PluggedType="{namespace2}.ContentService'1[[{namespace3}.XmlDataString, {assembly3}]], {assembly2}" />mafue 2012-04-04 17:59