1

I am having the following issue relating to Generics CoVariance / ContraVariance Conflict.

public interface IEmailTemplateInput
{
    List<string> ToEmail { get; set; }
    string Subject { get; set; }
    string FromEmail { get; set; }
}

public interface IEmailCommunicationTemplate<in TInputData> where TInputData : IEmailTemplateInput
{
    void ProcessTemplate(TInputData input);
    Task ProcessTemplateAsync(TInputData input);
}


// Below is the implementation using above interfaces

 public class RiskPeerReviewNotificationTemplate
    : IEmailCommunicationTemplate<RiskPeerReviewNotificationTemplate.Input>

{
    private readonly IEmailService _emailService;
    private readonly ITemplateService _templateService;

    public class Input : IEmailTemplateInput
    {
        public string TenantName { get; set; }
        public string FirstName { get; set; }
        public string CallToActionUrl { get; set; }

    }

    public RiskPeerReviewNotificationTemplate(IEmailService emailService, ITemplateService templateService)
    {
        _emailService = emailService;
        _templateService = templateService;
    }


    protected override Task ProcessAsync(Input input)
    {
      //... Method implementation
    }

}

Now I would like to register IEmailCommunicationTemplate and based on input parameter (EmailTemplateType in this case), want to generate different implementations.

But I run into problems during Autofac registration

 // Register email templates
        iocBuilder.Register<IEmailCommunicationTemplate<IEmailTemplateInput>>((c, p) =>
            {
                var type = p.TypedAs<EmailTemplateType>();
                switch (type)
                {
                    case EmailTemplateType.NewUserNotificationTemplate:
                        return new NewUserNotificationTemplate(c.Resolve<IEmailService>(),
                            c.Resolve<ITemplateService>());
                    case EmailTemplateType.RiskPeerReviewNotificationTemplate:
                        return new RiskPeerReviewNotificationTemplate(c.Resolve<IEmailService>(),
                            c.Resolve<ITemplateService>());
                    default:
                        throw new ArgumentException("Invalid Email Template type");
                }
            })
            .As<IEmailCommunicationTemplate<IEmailTemplateInput>>();

Now obviously I can't do this because of contraVariance. Actual issue in above registration is because I am effectively doing something like
IEmailCommunicationTemplate test2 = new RiskPeerReviewNotificationTemplate(emailService, templateService); which isn't possible...

I can't change IEmailCommunicationTemplate where TInputData : IEmailTemplateInput to IEmailCommunicationTemplate where TInputData : IEmailTemplateInput

because TInputData also comes as parameter in methods (contraVariance nature)...

One solution which was proposed on stackoverflow

Generics, Covariance/Contravariance, etc

that would resolve compilation issue : 

1) Have two interfaces 

interface IEmailContravarianceCommunicationTemplate<out TInputData> where TInputData : IEmailTemplateInput

and 

interface IEmailCovarianceCommunicationTemplate<out TInputData> where TInputData : IEmailTemplateInput

2) Have RiskPeerReviewNotificationTemplate implement both.

3) Use interface depending on their context

0 Answers0