MikeW
MikeW

Reputation: 1620

Inject object using MEF with information about destination object

I am looking for a way to inject a logging object which wraps a log4net logger into various objects using MEF. The problem I currently have is that the logging object requires the type of the object it is a member of. I could get round this problem by setting the type property on the logging object within the constructor of the containing object, however this leaves the onus of setting the type on the developer, and there is no compile time restriction I can think of to enforce this.

I there a way to specify that when the logging object is generated by MEF and injected, its constructor parameter is set to the type of the target class of the injection?

My logger implements an interface

public interface ILogger
{
    Type Type { get; }
}

An Example of the concrete implementation of this is

[Export(typeof(Ilogger))]
public class SimpleLogger : ILogger
{
    public SimpleLogger(Type typeOfObjectToLogFor)
    {
         this.Type = typeOfObjectToLogFor
    }

    public Type Type { get; }

    public void Info(string message)
    {
        //log the messsage including the type information
    }
}

and it is currently consumed not using MEF as:

public class ExampleObject
{
    private readonly ILogger logger = new SimpleLogger(typeof(ExampleObject));

    public ExampleObject(){}

    public void MethodThatLogs()
    {
        logger.Info("MethodThatLogs called");
    }
}

and what I would like to do is inject it using constructor injection:

public class ExampleObject
{
    private readonly ILogger logger;

    [ImportingConstructor]
    public ExampleObject(Ilogger injectedLogger)
    {
        logger = injectedLogger;
    }

    public void MethodThatLogs()
    {
        logger?.Info("MethodThatLogs called");
    }
}

I could do all this with lazy evaluated reflection, but it feels like something that should be possible from a decent DI container, and hopefully that means that MEF will support it, cam anyone help?

Upvotes: 3

Views: 329

Answers (1)

dymanoid
dymanoid

Reputation: 15197

By default, specifying an [Export] attribute, you're setting the PartCreationPolicy to Shared, which means that the container will create a singleton for your export - your logger.

But I suggest you to export not a class but a factory method that will accept one argument and create the logger for you.

class LoggerFactory
{
    [Export("GetLogger")]
    public ILogger GetLogger(Type type)
    {
        return new SimpleLogger(type);
    }
}

class ExampleObject
{
    private readonly ILogger logger;

    [ImportingConstructor]
    public ExampleObject([Import(ContractName = "GetLogger", AllowDefault = true)]Func<Type, ILogger> loggerCreator)
    {
        logger = loggerCreator?.Invoke(this.GetType());
    }
}

Upvotes: 3

Related Questions