31

I'm new to Unity and am trying to write some Unity logic which initialises and register/resolves a singleton instance of the Email object so that it can be used across several other objects, one example below being OperationEntity.

So when it's registered it populates the Email singleton with some values from a config file, then whenever an instance of OperationEntity is created (in my case it's being deserialized) it uses that same Email singleton. So all my client logic needs to do is deserialize OperationEntity and call PerformAction() - with the email instance taken care of by Unity.

public interface IEmail
{
    string FromName { get; set; }
    string FromEmailAddress { get; set; }
}

public class Email : IEmail
{
    public string FromName { get; set; }
    public string FromEmailAddress { get; set; }

    public Email(string fromName, string fromEmailAddress)
    {
        FromName = fromName;
        FromEmailAddress = fromEmailAddress;
    }
}

public class OperationEntity
{
    private readonly IEmail _email;

    public int OperationId { get; set; }
    public string OperationName { get; set; }
    public string ToAddress { get; set; }

    public OperationEntity(IEmail email)
    {
        _email = email;
    }

    public void PerformAction()
    {
        _email.ToAddress = ToAddress;
        _email.Body = "Some email body";
        _email.Deliver();
    }
}

Any help would be appreciated in getting this Unity code to work

    public static void Register(IUnityContainer container)
    {
        container
            .RegisterType<IEmail, Email>(
            new InjectionFactory(c => new Email(
                "To Name", 
                "to@email.com")));

        var email = container.Resolve<IEmail>();  

        container.RegisterType<OperationEntity>(
            "email", new ContainerControlledLifetimeManager(),
            new InjectionConstructor(email));
    }
Bern
  • 7,808
  • 5
  • 37
  • 47
  • I was looking for some help with the C# Unity code required to wire this up. – Bern May 30 '13 at 12:28
  • Maybe the [wiki about Singleton](http://wiki.unity3d.com/index.php/Singleton) could have been of some help. – cregox Sep 18 '13 at 20:05
  • @cregox That is a link to Unity3d, a graphics library. This is a question about the [Unity container](https://msdn.microsoft.com/en-us/library/ff647202.aspx), a DI framework. – Andy Jan 19 '17 at 13:09
  • @Andy are you saying the link could've been of absolutely no help? Up to this day I'm still learning about singletons and DI, so if this was indeed completely irrelevant it'd be good to know. ;) – cregox Jan 23 '17 at 18:11
  • @cregox In this case I think it's less useful; with most DI containers, you can configure a lifetime of a plug-in type so that there is only a single instance, but consumers aren't actually aware they're using a singleton. In .Net for instance, I've used an interface to abstract reading from the .config file. Instances which need configuration take a reference to the interface via ctor injection like normal, but because the implementation just wraps a static, threadsafe methods, I configure it as a singleton so the DI framework in fact only ever creates one instance of the object. – Andy Jan 23 '17 at 22:39
  • @cregox That can be useful sometimes, since you have the implementation instance from being newed up every use only to be thrown away, reducing the number of objects waiting to be garbage collected which may improve performance. – Andy Jan 23 '17 at 22:40

5 Answers5

52

First, you need a proper lifetime manager the ContainerControlledLifetimeManager is for singletons.

For custom initialization, you could probably use InjectionFactory

This lets you write any code which initializes the entity.

Edit1: this should help

public static void Register(IUnityContainer container)
{
    container
        .RegisterType<IEmail, Email>(
        new ContainerControlledLifetimeManager(),
        new InjectionFactory(c => new Email(
            "To Name", 
            "to@email.com")));
}

and then

var opEntity = container.Resolve<OperationEntity>();

Edit2: To support serialization, you'd have to rebuild dependencies after you deserialize:

public class OperationEntity
{
   // make it public and mark as dependency   
   [Dependency]
   public IEmail _email { get; set;}

}

and then

OperationEntity entity = somehowdeserializeit;

// let unity rebuild your dependencies
container.BuildUp( entity );
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • I used the logic from the second link to resolve an email instance, but how do I go about ensuring the OperationEntity will use that instance in it's constructor? I tried container.RegisterType(new InjectionConstructor(email)); but no luck. – Bern May 30 '13 at 12:38
  • If you use the ContainerControlledLifetimeManager, the only singleton entity will always be injected into constructors of other classes. – Wiktor Zychla May 30 '13 at 12:41
  • I included my code above using your suggestions, unfortunately I still get a null value for the email instance in the OperationEntity constructor so I'm guessing I've missed something. Could I trouble you for your thoughts. – Bern May 30 '13 at 12:49
  • Thanks thus far Wiktor, the only issue I'm having with the updated code you gratefully supplied is when I deserialise an instance of OperationEntity, and its constructor is called, the email instance is still null. As a result a subsequent call to PerformAction() method has a null _email. Any thoughts are appreciated. – Bern May 30 '13 at 12:59
  • Excellent, thanks for your patience and help Wiktor I managed to get it working with your assistance. – Bern May 30 '13 at 13:19
15

You could use:

container.RegisterType<IEmail, Email>(new ContainerControlledLifetimeManager());
aquaraga
  • 4,138
  • 23
  • 29
10

If IEmail is a singleton with no dependencies (just custom arguments), you can new it up yourself:

container.RegisterInstance<IEmail>(new Email("To Name", "to@email.com"));

That will register the supplied instance as a singleton for the container.

Then you just resolve the service:

container.Resolve<OperationEntity>();

And because you are resolving a concrete type, there is no registration required. Nevertheless, if you would like that service to also be a singleton, you can register it using ContainerControlledLifetimeManager and then all calls to resolve (or when injecting it as a dependency to another class) will return the same instance:

container.RegisterType<OperationEntity>(new ContainerControlledLifetimeManager());
Julian Dominguez
  • 2,573
  • 21
  • 15
  • 1
    Thanks for this. In the end it was the deserialization of the OperationEntity that was the main issue, and I'm not sure this approach addresses that. The code need after deserializing was unityContainer.BuildUp(OperationEntity); – Bern Jun 06 '13 at 08:43
  • 1
    Actually, this is working but you have to add `using Microsoft.Practices.Unity;` in the Unity 4.0.1, but when updated to the version 5.8.11 even adding this namespace is not working anymore and I can not find the method. if you have any idea can you please provide it – Hakan Fıstık Sep 10 '18 at 17:45
0

You can, for example, use this code:

public class example : MonoBehaviour
{
    public static example instance;

    public void Start()
    {
        (!instance)
            instance = this;
    }
}
  • Investing a bit more time when answering would be good. I'm, for example, quite sure that the base class is called `MonoBehaviour` instead of `monobehaviour`. – BDL Oct 09 '20 at 14:16
0

You could implement your own singleton class and extend any class form it.

public class MyClass : MonoBehaviour {

    private static MyClass _instance;

    public static MyClass Instance { get { return _instance; } }

    private void Awake()
    {
        if (_instance != null && _instance != this)
        {
            Destroy(this.gameObject);
        } else {
            _instance = this;
        }
    }
}