Stuck trying to figure out how to do this with StructureMap

232 views
Skip to first unread message

BellyB00

unread,
Oct 28, 2009, 5:41:12 PM10/28/09
to structuremap-users
Hello everyone :)

I've been using SM for most of '09 and love it :) (Cheers Jeremy and
co..)

Today I tried to get really tricky and i'm stuck. I've got an
ILoggingService that i've been using for ages and works really well.
Initially i was using NLog but today I switched over to Log4Net.

Now, when SM creates the l4n instance via greedy constructors, I wish
the constructor could accept a type (ie. the type of the class that is
using this instance). But i have no idea how to define/set this up.

My application is ASP.NET MVC, so i'll use that as an example. Imagine
i have two controllers.

a) HomeController
b) AccountController

and this is their (current) greedy constructors

public HomeController(ILoggingService loggingService){ .. }
public AccountController(ILoggingService loggingService,
IAccountService accountService){ .. }

nothing too complex there. But, when the HomeController gets the
instance, i was hoping the loggingService would then accept via a
constructor or a property or whatever, the current class type.

I was hoping that i could use reflection when i setup the Injection in
my bootstrapper file?

It was also suggested that I use generics

eg. public HomeController(ILoggingService<HomeController>
loggingService) { .. }

but i feel that this is not a really good solution.

please help :)
Message has been deleted

Kyle Malloy

unread,
Oct 28, 2009, 9:20:54 PM10/28/09
to structuremap-users
The generic solution is the simpliest. All you have to do is register
the default for the open generic like this in your registry.

this.ForRequestedType(typeof(ILoggerService<>))
.TheDefaultIsConcreteType(typeof(LoggerService<>));

The non generic solution may take more configuration then you are
willing to commit to.

this.ForRequestedType<IController>()
.AddInstances(instanceExp =>
{
instanceExp.OfConcreteType<HomeController>()
.CtorDependency<ILoggerService>()
.Is(loggerExp =>
{
loggerExp.OfConcreteType<LoggerService>()
.CtorDependency<Type>()
.Is(typeof(HomeController));
});
});

If you still really want to use the non generic solutition I would
make a type scanner to do it for me by default something like the
following.

public class DefaultMvcControllerConfiguration : ITypeScanner
{
private Type _controllerType = typeof(IController);

#region ITypeScanner Members

public void Process(Type type, PluginGraph graph)
{
if (type.CanBeCastTo(_controllerType))
{
var name = type.Name.Replace("Controller", "")
.ToLowerInvariant();
var instance = new ConfiguredInstance(type, name);
Plugin plugin = PluginCache.GetPlugin(type);
var loggerArgName =
plugin.FindArgumentNameForType<ILoggerService>();
var loggerInstance = new SmartInstance<LoggerService>()
.CtorDependency<Type>().Is(type);
instance.CtorDependency<ILoggerService>(loggerArgName)
.Is(loggerInstance);
graph.FindFamily(_controllerType).AddInstance(instance);
}
}

#endregion
}

Hopefully this helps,
~Kyle

BellyB00

unread,
Oct 28, 2009, 10:31:35 PM10/28/09
to structuremap-users
Hi Kyle - thanks so so so much for replying :) I've been madly
pressing refesh all morning/lunch hoping someone can help :)

> The generic solution is the simpliest. All you have to do is register
> the default for the open generic like this in your registry.
>
> this.ForRequestedType(typeof(ILoggerService<>))
>     .TheDefaultIsConcreteType(typeof(LoggerService<>));

No way! :) that's it?? When i was talking about 'using generics', i
thought it would be...

public class Log4NetService<T> : ILoggingService
{ .. }

and then in the greedy constructor

public HomeController(ILoggingService<HomeController> loggingService)
{ .. }

which i guess would work, but that means i have to do that
_everywhere_ and it just doesn't feel 'right'. It feels a bit too
hardcoded, against the IoC type of concept.

but with your code above, what would a HomeController look like, then?
Just what I did, above?




> The non generic solution may take more configuration then you are
> willing to commit to.
>
> this.ForRequestedType<IController>()
>     .AddInstances(instanceExp =>
>     {
>         instanceExp.OfConcreteType<HomeController>()
>             .CtorDependency<ILoggerService>()
>             .Is(loggerExp =>
>             {
>                 loggerExp.OfConcreteType<LoggerService>()
>                     .CtorDependency<Type>()
>                     .Is(typeof(HomeController));
>             });
>     });
>

So with this code, this means i would have to declare _every_ instance
that uses the ILoggingService ... which sorta defeats the purpose.
It's just a really really really long way of doing the first option?
(I need to see how you define the constructor for a Controller, to see
if i'm on the right path, here)...


> If you still really want to use the non generic solutition I would
> make a type scanner to do it for me by default something like the
> following.
>
> public class DefaultMvcControllerConfiguration : ITypeScanner
> {
>     private Type _controllerType = typeof(IController);
>
>     #region ITypeScanner Members
>
>     public void Process(Type type, PluginGraph graph)
>     {
>         if (type.CanBeCastTo(_controllerType))
>         {
>             var name = type.Name.Replace("Controller", "")
>                 .ToLowerInvariant();
>             var instance = new ConfiguredInstance(type, name);
>             Plugin plugin = PluginCache.GetPlugin(type);
>             var loggerArgName =
> plugin.FindArgumentNameForType<ILoggerService>();
>             var loggerInstance = new SmartInstance<LoggerService>()
>                 .CtorDependency<Type>().Is(type);
>             instance.CtorDependency<ILoggerService>(loggerArgName)
>                 .Is(loggerInstance);
>             graph.FindFamily(_controllerType).AddInstance(instance);
>         }
>     }
>
>     #endregion
>
> }
>

This scanner thing is interesting. I'm very new to all this hardcore
StructureMap settings. I might have been using it for nearly all of
this year, but not all this hardcore setting stuff :) I noticed in
this scanner, you're searching for IController types. For myself, I
pass the ILoggingService to all types of constructors, not just
Controllers. So i'm not sure how this would apply to myself also.

Is there any way to mix reflection with the ForRequestedType setting?

something like (and this is very pseduo code, brainstorming...)

this.ForRequestedType(typeof(ILoggerService<>))
.TheDefaultIsConcreteType(typeof(LoggerService<>))
.CtorDependency(newInstance => newInstance.GetType());

?

Kyle Malloy

unread,
Oct 29, 2009, 7:05:19 AM10/29/09
to structuremap-users
After reading your response I though of another way to acheive the
same outcome without generics.

Instead of using Type as a dependency use the Log4Net ILog interface.

public class LoggerService : ILoggerService
{
public LoggerService(ILog log)
{

}
}

Then configure the default instance for ILoggerService like the
following. If you have other dependencies you can obtain them from the
IContext.

this.ForRequestedType<ILoggerService>()
.TheDefault.Is.ConstructedBy(context =>
{
return new LoggerService(LogManager.GetLogger
(context.ParentType ?? typeof(ILoggerService)));
});

The only problem with the solution is that when you directly ask for
an ILoggerService from the Container there is no way of determining
the proper Type.

Just for reference my constructor for HomeController is this.

public HomeController(ILoggerService logger)

~Kyle
> ?- Hide quoted text -
>
> - Show quoted text -- Hide quoted text -
>
> - Show quoted text -

BellyB00

unread,
Oct 29, 2009, 8:25:55 AM10/29/09
to structuremap-users
Thanks again Kyle for having a look at this.

> After reading your response I though of another way to acheive the
> same outcome without generics.
>
> Instead of using Type as a dependency use the Log4Net ILog interface.
>
> public class LoggerService : ILoggerService
> {
>     public LoggerService(ILog log)
>     {
>
>     }
>
> }
>
> Then configure the default instance for ILoggerService like the
> following. If you have other dependencies you can obtain them from the
> IContext.
>
> this.ForRequestedType<ILoggerService>()
>     .TheDefault.Is.ConstructedBy(context =>
>     {
>         return new LoggerService(LogManager.GetLogger
> (context.ParentType ?? typeof(ILoggerService)));
>     });

Hmm. ok. Lets try that..

/me runs off.
/me returns.

context.ParentType is throwing an exception. This is because
context.BuildStack.Parent is null.

So, I then tried to refactor it like...

context.BuildStack.Parent == null ? typeof (ILoggingService) :
context.ParentType)

and that sorta works.

This is how i bootstrap it..

protected void Application_Start()
{
// Wire up Dependency injection & define our controller factory
before we can do _anything_
Bootstrapper.ConfigureStructureMap();
ControllerBuilder.Current.SetControllerFactory(new
StructureMapControllerFactory());

// Ok! now we can do stuff :)
ObjectFactory.GetInstance<ILoggingService>().Info("Test");
...
}

and it displays the name of the logger as:
MyProject.Services.Logging.ILoggingService

hmmm.... okay...

what about when i'm in a controller (eg. HomeController)

namespace MyProject.Mvc.Controllers
{
public clas HomeController : Controller
{
private readonly ILoggingService _log;
public HomeController(ILoggingService loggingService)
{
_log = loggingservice;
}

public ActionResult Index()
{
_log.Info("Starting Index action.");
......
}

the name of the logger is now another assembly/class/namespace in my
solution ... not the namespace of the controller or anything close to
it :(

>
> The only problem with the solution is that when you directly ask for
> an ILoggerService from the Container there is no way of determining
> the proper Type.

hmm. Sorry, I don't understand this :(

---
I feel like we're so close!

BellyB00

unread,
Oct 30, 2009, 9:17:17 AM10/30/09
to structuremap-users
Hmm... Jeremy, are you around to add some ideas to this thread?

Brian Chiasson

unread,
Nov 4, 2009, 10:59:00 AM11/4/09
to structure...@googlegroups.com
The simplest thing I can come up with here is to base class your Controllers that accept the ILoggingService. You will have to expose a property/method on the ILoggingInterface to accept the logging class's type (property injection versus constructor injection). Then the base class's constructor can perform the operation once in its constructor and you get it across all of your logging controllers.

You could also define an interface for your controllers (avoiding inheritance above) with something like ILoggingController. The interface would contain a property/method that exposes the ILoggingService instance. This will allow you to use StructureMap's Interception mechanisms to perform the operation on construction of the controller instances (I think you are looking for OnCreation here). Click on the link to the documentation for more details http://structuremap.sourceforge.net/Interception.htm#section5.

Finally, you could do the following (I am not sure that you want to rely on the Parent property like this (Jeremy or another contributor should confirm):

                c.ForRequestedType<ILoggingService>().
                    TheDefault.Is.OfConcreteType<ConcreteType>().
                    OnCreation((context, i) => {
                        if (context.ParentType == null) return;
                        i.SetInstanceBeingLogged(context.ParentType);
                    });
            });

HTH,
Brian Chiasson
Reply all
Reply to author
Forward
0 new messages