4

I just started moving to Autofac from Unity and I have a problem when trying to register an instance.

public static void Register(ContainerBuilder containerBuilder,
                            CancellationToken shutDownCancellationToken)
{
    containerBuilder.RegisterType<CancellationToken>();
    containerBuilder.RegisterInstance(shutDownCancellationToken);
}

I get the following error:

The type 'CancellationToken' must be a reference type in order to use it as parameter 'T' in the generic type or method 'RegistrationExtensions.RegisterInstance<T>(ContainerBuilder, T)'

Anyone knows how to register an already created struct instance?

Cyril Durand
  • 15,834
  • 5
  • 54
  • 62
pantonis
  • 5,601
  • 12
  • 58
  • 115
  • 1
    How 'CancellationToken' is defined? – Amit Apr 05 '17 at 07:21
  • CancellationTokenSource shutDownCancellationTokenSource = new CancellationTokenSource(); – pantonis Apr 05 '17 at 07:35
  • Please provide Definition! not instance creation. – Amit Apr 05 '17 at 07:39
  • what do you mean definition? – pantonis Apr 05 '17 at 07:47
  • Class definition of 'CancellationToken'. Also what is association between `CancellationTokenSource` and `CancellationToken` – Amit Apr 05 '17 at 08:59
  • A `CancellationToken` is used to cancel a task but `Autofac` is used to manage the instance of all task. Why would you need to register a `cancellationToken` ? – Cyril Durand Apr 05 '17 at 09:30
  • @Amit I was saying about Threading CancellationToken. https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken(v=vs.110).aspx shutDownCancellationTokenSource is created using a CancellationTokenSource at my microservice root which is used to Cancel() when OnStop fires – pantonis Apr 05 '17 at 09:33
  • @Cyril Durand I have several services running and when I shut down the Microservice I need a global cancellation token (shutdown) that kills all background tasks running in my services. – pantonis Apr 05 '17 at 09:34
  • The real question is here: why do you want to inject a struct? A struct is _runtime data_, and this is not something you should inject, as explained [here](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). – Steven Apr 05 '17 at 10:43

1 Answers1

11

The RegisterInstance method as a class constraint that disallow struct registration but CancellationToken is a struct this is why you have this error message. To bypass this constraint, you can register your instance using the Register method.

builder.Register(_ => source.Token).As<CancellationToken>(); 

As long as your struct is immutable, you will have the expected behavior, but if you use a mutable struct, the instance won't be share. In your case, CancellationToken is immutable you won't have any weird side effect.


But why has RegisterInstance a class constraint ?

When you register an instance using the RegisterInstance method, the expected behavior is to share the same instance each time the object is injected.

By definition a struct acts like a copy and not like a reference. See Struct vs Class for more information.

Imagine you have the following struct and service using this struct :

public struct Foo
{
    public Int32 A { get; set; }
}
public class Service
{
    public Service(Foo foo)
    {
        this._foo = foo;
    }

    private Foo _foo;

    public void Set(Int32 a)
    {
        this._foo.A = a;
    }

    public void Print()
    {
        Console.WriteLine(this._foo.A);
    }
}

and the following code using these service :

var builder = new ContainerBuilder();

Foo f = new Foo();
builder.Register(_ => f).As<Foo>();
builder.RegisterType<Service>();
IContainer container = builder.Build();

Service service1 = container.Resolve<Service>();
Service service2 = container.Resolve<Service>();

service1.Set(3);
service1.Print();
service2.Print();

Even if you register only one instance of Foo it will display 3 and 0. I think this is the reason why RegisterInstance has a class constraint.

Community
  • 1
  • 1
Cyril Durand
  • 15,834
  • 5
  • 54
  • 62