ShurikEv
ShurikEv

Reputation: 170

How map generic types with Unity?

I am trying to add a dependency to a class property using Unity Configuration and also the types I am trying to inject are generic.

I have interface

public interface ISendMessage
{
    void Send(string contact, string message);
}

class

public class EmailService : ISendMessage
{
    public void Send(string contact, string message)
    {
        // do
    }
}

class

public class MessageService<T> where T : ISendMessage
{          
}

I try use it via constructor injection in other class

public MyService(MessageService<ISendMessage> messageService)
{
}

How I inject MessageService<EmailService> instead of MessageService<ISendMessage>?

I try do it via app.config

<alias alias="MessageService'1" type="MyNamespace.MessageService'1, MyAssembly" />
    <alias alias="EmailMessageService'1" type="MyNamespace.MessageService'1[[MyNamespace.EmailService, MyAssembly]], MyAssembly" />

I get error

The type name or alias MessageService'1 could not be resolved. Please check your configuration file and verify this type name.

And how I can pass in MessageService<T> implement paramenter MessageService<EmailService>?

Thanks

Update

I modified my class to the following:

public class MessageService<T> where T : ISendMessage
    {
        private T service;

        [Dependency]
        public T Service
        {
            get { return service; }
            set { service = value; }
        }
}

and use configuration

<alias alias="ISendMessage" type="MyNamespace.ISendMessage, MyAssembly" />
    <alias alias="EmailService" type="MyNamespace.EmailService, MyAssembly" />

<register type="ISendMessage" mapTo="EmailService">
        </register>

It works :-)

Upvotes: 2

Views: 5239

Answers (1)

Steven
Steven

Reputation: 172646

You can't simply cast a MessageService<ISendMessage> to a MessageService<EmailService>. For this to work you need the MessageService<T> to be variant. Variance is only supported for interfaces (and delegates). This isn't a Unity thing, this is a 'limitation' of the .NET framework (and support in C# since 4.0). So you need to implement the following interface:

// note the 'out' keyword!!
public interface IMessageService<out T> 
    where T : ISendMessage
{
    T GetSendMessage();
}

The MessageService<T> class will have to implement this interface. But even with this code, Unity will not inject this automagically. You will have to make a mapping between the two types. This for instance is a possible registration:

container.Register<MessageService<ISendMessage>>(
    new InjectionFactory(c =>
        c.Resolve<MessageService<EmailService>>())); 

Note that I use the code based configuration. Prevent using the XML based configuration as much as you can, since XML configuration is brittle, error prone, less powerful, and hard to maintain. Only register types that actually need to during or after deployment (and personally, even then I wouldn't use the XML API of your DI container).

Upvotes: 6

Related Questions