1

In Tiny IOC how does one get the type that it is being resolved for. For instance logging:

    container.Register<ILogger>((c, p) =>
                                        {
                                            return LoggerFactory.CreateLog(typeofCALLER);
                                        });

I'm trying to add a factory to build a ILogger, but have no injection Information.

TeaDrivenDev
  • 6,591
  • 33
  • 50
maxfridbe
  • 5,872
  • 10
  • 58
  • 80
  • You provide little context here. What does the code of `LoggerFactory` look like, and what kind of "injection information" do you mean? As a shot in the dark, I would think not having a static `LoggerFactory` (or at least not calling it directly here) might help. – TeaDrivenDev Mar 14 '13 at 13:06
  • LoggerFactory.CreateLog just returns an Ilogger, It is a static or however it's done. I would need the Injection Information of the injectee to fill in typeofCALLER – maxfridbe Mar 14 '13 at 13:25
  • I think it's dawning on me what you mean - the implementation that `LoggerFactory` returns depends on the class that you want to inject the `ILogger` instance into, right? – TeaDrivenDev Mar 14 '13 at 14:00
  • So and ILogger.Log("blah") will literally append to a log.txt "typeofCALLER time blah", so when i construct an ILogger it needs to know what the typeofCALLER is in order to log. – maxfridbe Mar 14 '13 at 15:36

2 Answers2

2

In that case, simple use of the Open Generics capabilities of TinyIoC might be enough.

public class Consumer
{
    public Consumer(ILogger<Consumer> logger) {}
}

public interface ILogger<T>
{
    string Log(string message);
}

public class ConcreteLogger<T>:ILogger<T>
{
    public string Log(string message)
    {
        return string.Format("{0}: {1}", typeof (T), message);
    }
}

and then do

container.Register(typeof (ILogger<>), typeof (ConcreteLogger<>));

That will cause an instance of ConcreteLogger<Consumer> to be injected into the Consumer constructor.

Edit

If you need to have an ILogger reference, you can simply make ILogger<T> a "marker interface" only and have it "derive" from ILogger:

public interface ILogger<T> : ILogger
{
}

The Consumer class would still need to take a dependency on ILogger<Consumer>, but ConcreteLogger<T> would directly implement ILogger members.

I see no better way, especially if the ILogger interface is fixed because it is not your own. I don't think there is a way to ask TinyIoC for the type it is currently trying to resolve to then be able to act on that, so if you need type specific implementations, the types must be made known by other means - and Generics are often (not always) helpful with that.

TeaDrivenDev
  • 6,591
  • 33
  • 50
  • That's awesome, Is there any way to do it without turning ILogger into a generic type. I do not want this because I want to stay compatible with log4net's logger impl. – maxfridbe Mar 14 '13 at 16:21
  • You don't need to change `ILogger`, but you can't do it completely without Generics. See updated answer. – TeaDrivenDev Mar 14 '13 at 16:50
  • If you tried using this, you may have noticed that it doesn't actually work. This is because TinyIoC has a bug with regards to Open Generics: http://stackoverflow.com/questions/15450518/how-to-register-a-generic-interface-using-tinyioc/15714045 – TeaDrivenDev Mar 30 '13 at 04:24
1

Note: I'm using ILog in my example, which is what log4net calls it's type-specific logger class. But it seems to be created and used in exactly the same way as the ILogger in your example, and log4net's LogManager.GetLog(Type t) is analagous to your LoggerFactory.CreateLog(typeofCALLER).

It's a little more work, but you can create a generic factory class that generates your type-specific logger. Inject this factory class into the constructor instead of Ilog.

TinyIoC's registration API doesn't allow you to specify a factory that is aware of the containing type that the currently resolving type is being injected into. The below workaround works because TinyIoC injects the correct generic instance of LogFactory<Foo> or LogFactory<Bar>, and then we capture the correctly generated ILog instance returned by logFactory.Get(). A few extra keystrokes than if we did it using autofac -- which does support what you are after -- but they don't require much thought.

Say you want to do some logging in classes Foo, and Bar. You'd define LogFactory<T> just once, and use it in Foo, Bar, and any other classes that require logging with a type-specific ILog instance:

public class LogFactory<T> {
    public LogFactory() {} //empty
    public ILog Get() { return LogManager.GetLogger(typeof(T)); }
}

public class Foo {
    private readonly ILog _log; 

    public Foo(LogFactory<Foo> logFactory, ...) {
        _log = logFactory.Get();
    }
}

public class Bar{
    private readonly ILog _log; 

    public Bar(LogFactory<Bar> logFactory, ...) {
        _log = logFactory.Get();
    }
}
Giscard Biamby
  • 4,569
  • 1
  • 22
  • 24