0
votes

I am using .Net SmtpClient class for sending email. The configuration for smtp is stored in config file. You can use the application or machine configuration files to specify default host, port, and credentials values for all SmtpClient objects. The default constructor will read all the values from configuration file. MSDN

    <configuration>
      <system.net>
        <mailSettings>
          <smtp deliveryMethod="network">
            <network
              host="localhost"
              port="25"
              defaultCredentials="true"
            />
          </smtp>
        </mailSettings>
      </system.net>
    </configuration>

Here is my interface and class that's using SmtpClient

    public interface IEmail:IDisposable
    {
         void Send(string[] to)
    }


    public class SmtpEmail : IEmail
    {
        private bool _disposed = false;
        private SmtpClient _client = null;

        public SmtpEmail(SmtpClient client)
        {
            _client = client;
        }
        public void Send(string[] to)
        {
            using (MailMessage message = GetEmailMessage())
            {
                foreach (string email in to)
                {
                    message.To.Add(email);
                }               

                _client.Send(message);               
            }
        }       

        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    if (_client != null)
                    {
                        _client.Dispose();
                        _disposed = true;
                    }
                }
            }
        }
    }

I want to dispose the SmtpClient as soon we are done sending email. Here how I'm calling it using ServiceLocator

 using(var emailClient = ServiceLocator.Current.GetInstance<IEmail>())
 {
     emailClient.Send(new string[] { "[email protected]"};
 }

Here is how I'm registering with Unity

        var container = new UnityContainer();

        container.RegisterType<SmtpClient,SmtpClient>();
        container.RegisterType<IEmail, SmtpEmail>();

        UnityServiceLocator locator = new UnityServiceLocator(container);
        ServiceLocator.SetLocatorProvider(() => locator);

However I am getting error

{"Resolution of the dependency failed, type = \"MyFramework.Core.Email.IEmail\", name = \"(none)\".\r\nException occurred while: while resolving.\r\nException is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.\r\n-----------------------------------------------\r\nAt the time of the exception, the container was:\r\n\r\n Resolving MyFramework.Core.Email.SmtpEmail,(none) (mapped from MyFramework.Core.Email.IEmail, (none))\r\n Resolving parameter \"client\" of constructor MyFramework.Core.Email.SmtpEmail(System.Net.Mail.SmtpClient client)\r\n Resolving System.Net.Mail.SmtpClient,(none)\r\n
Resolving parameter \"host\" of constructor System.Net.Mail.SmtpClient(System.String host, System.Int32 port)\r\n Resolving System.String,(none)\r\n"}

Question
How do I register SmtpClient with Unity so that SmtpClient can use its default constructor and also Unity can create new instance every time.

1

1 Answers

-1
votes

Unity container tries to create SmtpEmail class object. It has 3 constructors, and unity selects the one with the most amount of parameters:

public SmtpClient(
    string host,
    int port
)

So, you need to specifify to select consttructor wiuthout parameters:

var container = new UnityContainer();
container.RegisterType<SmtpClient>(new InjectionConstructor());
container.RegisterType<IEmail, SmtpEmail>();