BuildUp for singleton class

Mar 28, 2012 at 11:43 AM
I am trying to inject the dependency to the singleton class with the following code:
UnityContainer container = new UnityContainer();
container.RegisterType();

var singleton = SingletonRepository.Instance;
// Exception on this line
// container for some reason tries to find a constructor 
container.BuildUp(typeof(SingletonRepository), singleton);
Clases definition:
  interface ILogger {

    void Write(string message);
  }

  class Logger : ILogger {

    public Logger() {

    }

    public void Write(string message) {

      Debug.WriteLine(message);
      //Trace.WriteLine(message);
    }
  }

class SingletonClass {

    private ILogger logger;
    private static SingletonClass instance;

    private SingletonClass() {
    }

    public static SingletonClass Instance {
      get {
        if (instance == null)
          instance = new SingletonClass();
        return instance;
      }
    }

    [Dependency]
    public ILogger Logger {
      get {
        return logger;
      }
      set {
        logger = value;
      }
    }
}
Mar 28, 2012 at 11:49 AM

And what exactly is your problem?

Mar 28, 2012 at 11:52 AM
Edited Mar 28, 2012 at 11:52 AM
weberse wrote:

And what exactly is your problem?

 

container.BuildUp throws an exception. Is it correct behaviour?

Mar 28, 2012 at 12:06 PM

If you enclose the message and stacktrace of the exception it would be far easier to diagnose your problem.

Mar 28, 2012 at 12:17 PM

 

Message:

Resolution of the dependency failed, type = "UnityResearch.SingletonClass", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type SingletonClass cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:

 Resolving UnityResearch.SingletonClass,(none)

 Stack:

at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\UnityContainer.cs:line 516
at Microsoft.Practices.Unity.UnityContainer.BuildUp(Type t, Object existing, String name, ResolverOverride[] resolverOverrides) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\UnityContainer.cs:line 237
at Microsoft.Practices.Unity.UnityContainerExtensions.BuildUp(IUnityContainer container, Type t, Object existing, ResolverOverride[] resolverOverrides) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\UnityContainerExtensions.cs:line 642
at UnityResearch.Program.BuildUpDependencyInjection()

 

Inner Exception message:

The type SingletonClass cannot be constructed. You must configure the container to supply this value.

Inner Exception stack:

at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.GuardTypeIsNonPrimitive(IBuilderContext context, SelectedConstructor selectedConstructor) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\Creation\DynamicMethodConstructorStrategy.cs:line 289
at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.PreBuildUp(IBuilderContext context) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\Creation\DynamicMethodConstructorStrategy.cs:line 71
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\StrategyChain.cs:line 110
at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlanCreatorPolicy.CreatePlan(IBuilderContext context, NamedTypeBuildKey buildKey) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\BuildPlan\DynamicMethod\DynamicMethodBuildPlanCreatorPolicy.cs:line 48
at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\BuildPlan\BuildPlanStrategy.cs:line 37
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\ObjectBuilder\Strategies\StrategyChain.cs:line 110
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) in C:\Program Files (x86)\Microsoft Unity Application Block 2.1\UnitySource\Source\Unity\Src\UnityContainer.cs:line 512

Mar 28, 2012 at 12:34 PM

Did you register the logger implementation with the container?

container.RegisterType<ILogger, Logger>()

Using the DependencyAttribute is something that is disencouraged. For an explanation see this post. As a cleaner alternative you can do the following:

container.RegisterType(new InjectionProperty("Logger"));
Finally I don't fully understand why you use the singleton pattern when you have a DI container at hand.

Register your repository with a singleton lifetime (Unity calls that ContainerControlledLifetimeManager) and inject it into the constructor of those classes that have a dependency on it. That would look something like this:

public interface ILogger { }
public class Logger : ILogger { }
public class ConsumerA
{
  public Repository Repository { get; set; }
  public ConsumerA(Repository repository)
  {
    Repository = repository;
  }
}
public class ConsumerB
{
  public Repository Repository { get; set; }
  public ConsumerB(Repository repository)
  {
    Repository = repository;
  }
}
public class Repository
{
  public ILogger Logger { get; set; }
  public Repository(ILogger logger)
  {
    Logger = logger;
  }
}

The following test shows that you get the same instance of the repository in both consumers.

var container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<Repository>(new ContainerControlledLifetimeManager());
var a = container.Resolve<ConsumerA>();
var b = container.Resolve<ConsumerB>();
Assert.AreSame(a.Repository, b.Repository);

Mar 28, 2012 at 12:50 PM
weberse wrote:

Did you register the logger implementation with the container?

Yes, sure.

I know about the life time managers, but I have to use the singleton pattern for some reasons (old project).

Mar 28, 2012 at 12:53 PM
weberse wrote:

As a cleaner alternative you can do the following:

container.RegisterType(new InjectionProperty("Logger"));
    

Workaround doesn't help

Mar 28, 2012 at 12:59 PM

var container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<SingletonRepository>(new InjectionProperty("Logger"));
var singleton = SingletonRepository.Instance;
container.BuildUp(typeof(SingletonRepository), singleton);
Assert.IsNotNull(singleton.Logger);

Mar 28, 2012 at 1:15 PM
Edited Mar 28, 2012 at 1:15 PM
weberse wrote:

 

var container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<SingletonRepository>(new InjectionProperty("Logger"));
var singleton = SingletonRepository.Instance;
container.BuildUp(typeof(SingletonRepository), singleton);
Assert.IsNotNull(singleton.Logger);

 

Did you create a private constructor?

Mar 28, 2012 at 3:10 PM
Edited Mar 28, 2012 at 3:13 PM

Your contructor question is hitting the right mark: even though Unity is performing a BuildUp of an existing object it is still checking for a public constructor.  If it doesn't find one it throws an exception.  If we step over this check everything seems to work fine.  I'm not sure what this check is trying to prevent since it seems like it wasn't there in Unity 1.2.  

There is an open work item for this issue.

As for workarounds, you could add a public constructor that throws an exception but that is not particularly good form and it sounds like you may not be able to modify the source(?).  Another, better, option would be to create a factory which would have the Logger injected and the factory would manually set the Logger into the singleton (which avoids Unity complaining about the SingletonClass lack of a public constructor):

    public class SingletonClassFactory
    {
        public SingletonClassFactory(ILogger logger)
        {
            SingletonClass.Instance.Logger = logger;
        }

        public SingletonClass Create()
        {
            return SingletonClass.Instance;   
        }
    }

...

    var container = new UnityContainer();
    container.RegisterType<ILogger, Logger>();
    container.RegisterType<SingletonClassFactory>(new ContainerControlledLifetimeManager());

    var factory = container.Resolve<SingletonClassFactory>();
    var singleton = factory.Create();

There is always the possibility of modifying the Unity source as well (I mention it but it's not a popular option!). 

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Mar 28, 2012 at 6:29 PM

You were right, I missed the private constructor. This will work:

public class PrivateCtor : InjectionMember
{
  public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
  {
    var ctor = implementationType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(c => c.GetParameters().Length == 0);
    policies.Set<IConstructorSelectorPolicy>(new SpecifiedConstructorSelectorPolicy(ctor, new InjectionParameterValue[0]), new NamedTypeBuildKey(implementationType, name));
  }
}
var container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<SingletonRepository>(new InjectionProperty("Logger"), new PrivateCtor());
var singleton = SingletonRepository.Instance;
container.BuildUp<SingletonRepository>(singleton);
Assert.IsNotNull(singleton.Logger);

Mar 28, 2012 at 6:49 PM

@weberse, that's very nice!

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Mar 30, 2012 at 6:50 AM
Edited Mar 30, 2012 at 6:50 AM
weberse wrote:

You were right, I missed the private constructor. This will work:

 

public class PrivateCtor : InjectionMember
{
  public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
  {
    var ctor = implementationType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(c => c.GetParameters().Length == 0);
    policies.Set<IConstructorSelectorPolicy>(new SpecifiedConstructorSelectorPolicy(ctor, new InjectionParameterValue[0]), new NamedTypeBuildKey(implementationType, name));
  }
}
var container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<SingletonRepository>(new InjectionProperty("Logger"), new PrivateCtor());
var singleton = SingletonRepository.Instance;
container.BuildUp<SingletonRepository>(singleton);
Assert.IsNotNull(singleton.Logger);

  That's great! Thank you

Mar 30, 2012 at 7:29 AM
randylevy wrote:

There is an open  work item for this issue.

This guy is right and I voted for the issue.

Could you please answer one more question? Is it possible to load a type for unity when resolving but not when registering? This feature is useful when you have several editions, let's say enterprise and express. If I don't want to provide some service in express edition I just cut it off with precompilation directives. The service is still being registered with the config file in the express edition but it doesn't lead to an error.