I am trying to understand how to use Dependency Injection with Dapper (IDbConnection) and still being able to use built in dispose.
I have found a couple of articles on the web but non that I think is easy to understand.
What I am trying to figure out is how to make this simple class be testable:
public class UserProfileRepository : IUserProfileRepository
{
private readonly IConfigRepository _configRepository;
public UserProfileRepository(IConfigRepository configRepository)
{
_configRepository = configRepository;
}
public UserProfile GetUserProfile(string userId)
{
const string query = @"Select UserId, UserName
From Users
Where UserId = @UserId";
using (var conn = new SqlConnection(_configRepository.GetConnectionString("MyConnectionString")))
{
conn.Open();
return conn.Query<UserProfile>(query, new { UserId = userId }).SingleOrDefault();
}
}
}
I have a config repository that looks like this so I can mock the request to web.config away:
public class ConfigRepository : IConfigRepository
{
public string GetConnectionString(string key)
{
var conString = ConfigurationManager.ConnectionStrings[key];
if (conString != null)
{
return conString.ConnectionString;
}
return string.Empty;
}
}
I have read that you could use ConnectionFactory but has not figur out how to implement it and still know I am disposing it correct.
Can anyone point me in the right direction?
Best connection creation mechanism as per my experience is the combination of DependencyInjection
and ConnectionFactory
. I am getting rid of IConfigRepository
, since here all the work is done using factory
Advantages are Multi fold:
- Create a connection object at runtime in transaction or thread scope
- At runtime change the data provider and thus database of the system ( using Connection Factory)
What you shall do (in Code):
Declare the IDBConnection
object in the Data access Layer:
[Inject] // Property Injection
public IDBConnection Connection {get; set;}
Declare the binding using a DI framework like Ninject:
Bind<IDBConnection>().ToMethod(ctx =>
ConnectionFactory.CreateDbConnection("DefaultConnection"));
Create the DBConnection Factory as follows:
Connection factory fetches the Connection provider and connection string from the config file as follows:
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=<Value>;Initial Catalog=<Value>;User Id=<Value>;Password=<Value>" providerName="System.Data.SqlClient" />
</connectionStrings>
Identifier is DefaultConnection
, which is using the SqlClient provider, but at run time can be changed to the different client like Oracle, MySql
using System;
using System.Data.Common;
public static class ConnectionFactory
{
/// <summary>
/// Create DBConnection type based on provider name and connection string
/// </summary>
/// <param name="connectionIdentifier"></param>
/// <returns></returns>
public static DbConnection CreateDbConnection(string connectionIdentifier)
{
// Provider name setting
var providerNameValue = ConfigurationManager.ConnectionStrings[connectionIdentifier].ProviderName;
// Connection string setting
var connectionStringValue = ConfigurationManager.ConnectionStrings[connectionIdentifier].ConnectionString;
// Assume failure.
DbConnection connection;
// Null connection string cannot be accepted
if (connectionStringValue == null) return null;
// Create the DbProviderFactory and DbConnection.
try
{
// Fetch provider factory
var factory = DbProviderFactories.GetFactory(providerNameValue);
// Create Connection
connection = factory.CreateConnection();
// Assign connection string
if (connection != null)
connection.ConnectionString = connectionStringValue;
}
catch (Exception ex)
{
connection = null;
}
// Return the connection.
return connection;
}
}
How to use it:
For a single call and dispose
using(Connection)
{
...
}
For a Transaction context, use as-is, no using
required
Regarding Mocking:
Which ever Mock framework you use for the Unit testing you have to mock the result of the UserProfileRepository :: GetUserProfile(string userId)
, this would be easier instead of filling a MockConnection
using dependency Injection, which will make it complex. DI is good for real use case, for filling connection object at runtime