Tuesday, November 10, 2015

Constructor Inject for abstract implementation on Dependency Injection

When dependency injection pattern with constructor injection for some cases you won't be able to inject some interface implementation to constructor ( ex: IDbConnection) because it having abstract implementation which cannot invoke iOC container. I normally use simple injector because light weight and well documented.

so as my first try i tried to inject IDbConnection in to my repository class as below
public class Repository : IRepositoryAsync where TEntity : class
{
    public Repository(IDbConnection connection)
    {
        Connection = connection;
    }

    public IDbConnection Connection { get; }
}

and i registered to container


container.Register(typeof(IDbConnection), typeof(Repository));

but ended up with exception like this

[ArgumentException: The supplied type Repository does not implement IDbConnection.Parameter name: implementationType]SimpleInjector.Requires.ThrowSuppliedTypeDoesNotInheritFromOrImplement(Type service, Type implementation, String paramName) +63SimpleInjector.Requires.ServiceIsAssignableFromImplementation(Type service, Type implementation, String paramName) +47SimpleInjector.Container.Register(Type serviceType, Type implementationType, Lifestyle lifestyle, String serviceTypeParamName, String implementationTypeParamName) +159SimpleInjector.Container.Register(Type serviceType, Type implementationType) +52TimeTrackerService.Service.DependencyConfig..cctor() in D:\TimeTracking\TimeTrackerService\libs\TimeTrackerService.Service\DependencyConfig.cs:47[TypeInitializationException: The type initializer for 'TimeTrackerService.Service.DependencyConfig' threw an exception.]TimeTrackerService.Service.DependencyConfig.get_Container() in D:\TimeTracking\TimeTrackerService\libs\TimeTrackerService.Service\DependencyConfig.cs:40 TimeTrackerService.WcfServiceFactory.CreateServiceHost(Type serviceType, Uri[] baseAddresses) in D:\TimeTracking\TimeTrackerService\TimeTrackerService\WcfServiceFactory.cs:15 System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses) +524System.ServiceModel.HostingManager.CreateService(String normalizedVirtualPath, EventTraceActivity eventTraceActivity) +1420System.ServiceModel.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity) +52System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity) +641[ServiceActivationException: The service '/TrackerService.svc' cannot be activated due to an exception during compilation. The exception message is: The type initializer for 'TimeTrackerService.Service.DependencyConfig' threw an exception..]System.Runtime.AsyncResult.End(IAsyncResult result) +489035System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +174System.ServiceModel.Activation.ServiceHttpModule.EndProcessRequest(IAsyncResult ar) +350322System.Web.AsyncEventExecutionStep.OnAsyncEventCompletion(IAsyncResult ar) +9747521

so i did work around created my own interface factory class to return IDbConnection and it's implementation.


private readonly string _providerName;
private readonly DbProviderFactory _provider;
public string ConnectionString { get; set; }

public DataFactory()
{
    var con = ConfigurationManager.ConnectionStrings["TrackerConnection"];
    if (con == null)
        throw new Exception("Failed to find connection");
    ConnectionString = con.ConnectionString;
    _providerName = con.ProviderName;
    _provider = DbProviderFactories.GetFactory(con.ProviderName);
}
public IDbConnection Connection
{
    get
    {
        var connection = _provider.CreateConnection();
        if (connection == null)
            throw new Exception($"Failed to create a connection using the connection string named '{_providerName}' in app.config or web.config.");
        connection.ConnectionString = ConnectionString;
        return connection;
    }
}

It's work like charm once i registered IDataFactory in to dependency container

also there's another solution overriding SimpleInjecor Register method without creating own implementation for return IDbConnection which i believe more robust.

container.Register(() =>{ 
    var con = ConfigurationManager.ConnectionStrings["TrackerConnection"]; 
    if (con == null) 
        throw new Exception("Failed to find connection"); 

    var _provider = DbProviderFactories.GetFactory(con.ProviderName); 

    var connection = _provider.CreateConnection(); 
    if (connection == null) 
        throw new Exception($"Failed to create a connection using the connection string named '{con.ProviderName}' in app.config or web.config."); 

    connection.ConnectionString = con.ConnectionString; 
    return connection; 
});