NSubstitute with DbConnection - c#

I need to pass the DbConnection to my class.
For that I am using the NSubstitute.
But when I run the test the following error occurs:
"System.NullReferenceException: Object reference not set to an instance of an object. Dapper"
My Test
public class SceneApplicationServiceTest
{
private readonly DbConnection _connection = Substitute.For<DbConnection>();
ISceneApplicationService _sceneApplicationService;
public SceneApplicationServiceTest()
{
_sceneApplicationService = new SceneApplicationService(_connection);
}
[Fact]
public async Task ShouldBePossibleSuccessfullyGetByProgramScript()
{
const long programId = 1;
const string scriptId = "2";
await _sceneApplicationService.GetByProgramScript(programId, scriptId);
await _sceneApplicationService.Received(1).GetByProgramScript(programId, scriptId);
}
}
My method
public class SceneApplicationService : ISceneApplicationService
{
private readonly DbConnection _connection;
public SceneApplicationService(DbConnection connection)
{
_connection = connection;
}
public async Task<IEnumerable<LegacySceneResponse>> GetByProgramScript(long programId, string scriptId)
{
object parameters = new
{
CodigoPrograma = programId,
CodigoRoteiro = scriptId
};
if (!ObjectValidation.IsInvalidAnyNullOrEmpty(parameters)) ;
return await _connection.QueryAsync<LegacySceneResponse>(GetScenesByScriptAndProgram.Query, parameters);
}}
The error occurs when the test tries to run the "QueryAsync"
Any ideas?

look here, its the moq for dapper extensions:
https://github.com/UnoSD/Moq.Dapper
QueryAsync is the extension method from Dapper nuget.

Related

Integration testing with Events - Exceptions when running multiple tests at the same time

I am using a shared database fixture for my tests, but when running multiple tests at the same time, I get the following error message:
System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
This is my code of my Fixture:
public class SharedDatabaseFixture : IDisposable
{
public static readonly object _lock = new object();
private static bool _databaseInitialized;
private const string postgresConnectionString = "Host=localhost;Database=IntegrationTests; Username=postgres;Password=password";
public SharedDatabaseFixture()
{
Connection = new NpgsqlConnection(postgresConnectionString);
Seed();
Connection.Open();
}
public DbConnection Connection { get; }
public AppDbContext CreateContext(DbTransaction transaction = null!)
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddMediatR(typeof(IAggregateRoot).Assembly)
.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>))
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
.BuildServiceProvider();
ServiceLocator.SetLocatorProvider(serviceProvider);
DomainEvents.Mediator = () => ServiceLocator.Current.GetInstance<IMediator>();
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseNpgsql(Connection).UseInternalServiceProvider(serviceProvider);
var context = new AppDbContext(builder.Options);
if (transaction != null)
{
context.Database.UseTransaction(transaction);
}
return context;
}
private void Seed()
{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var appDbContextSeed = new AppDbContextSeed(context);
appDbContextSeed.SeedAsync().Wait();
}
_databaseInitialized = true;
}
}
}
public void Dispose() => Connection.Dispose();
}
The code I am testing uses events and those events do queries to the database. Therefore, I am registering some services and also a DbContext.
The problem is, when I run multiple tests at the same time, events are raised at the same time as well and because they are all using the same DbContext, it throws an exception when two handlers try to use the DbContext at the same time.
So, my question is: how can I instantiate a DbContext for each test (but using the same connection) or prevent it from using the DbContext at the same time?
An Example of one of my tests:
public class Project_Create : IClassFixture<SharedDatabaseFixture>
{
public SharedDatabaseFixture Fixture { get; }
public Project_Create(SharedDatabaseFixture fixture) => Fixture = fixture;
[Fact]
public void Creates_succesfully()
{
var project = new Project(SeedConstants.TEST_COMPANY_ID, "ABC", "Hallo123", "2018-123");
Assert.Equal(SeedConstants.TEST_COMPANY_ID, project.CompanyId);
Assert.Equal("ABC", project.Code);
Assert.Equal("Hallo123", project.Description);
Assert.Equal("2018-123", project.Number);
}
}
Project.cs:
public class Project : BaseEntity<Guid, ProjectValidator, Project>, IAggregateRoot
{
public Guid CompanyId { get; private set; }
public string Code { get; private set; }
public string Description { get; private set; }
public string Number { get; private set; }
public Project(Guid companyId, string code, string description, string number)
{
CompanyId = companyId;
Code = code;
Description = description;
Number = number;
Validate(this);
DomainEvents.Raise(new SetCompanyIdEvent(companyId)).GetAwaiter().GetResult();
}
}
As you can see, this project class raises an event. This event has a handler and looks like this:
public class CheckIfProjectIdExistsHandler : INotificationHandler<SetProjectIdEvent>
{
private readonly IAsyncRepository<Project> _projectRepository;
public CheckIfProjectIdExistsHandler(IAsyncRepository<Project> projectRepository)
{
_projectRepository = projectRepository;
}
public async Task Handle(SetProjectIdEvent notification, CancellationToken cancellationToken)
{
var project = await _projectRepository.GetByIdAsync(notification.ProjectId, cancellationToken);
if (project == null)
{
throw new ProjectDoesNotExistsException($"The project with ID {notification.ProjectId} does not exist.");
}
}
}
I hope this illustrates what I am testing
The answer is always simpler than you think.
When adding the DbContext in the Service Provider, I didn't specify the ServiceLifetime, so it is a singleton by default. Changing this to Transient solves the issue. Then the Connection should also be changed by the connectionString, so there are no multiple operations on the same connection.
So, this line:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
Should be change like so:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(postgresConnectionString), ServiceLifetime.Transient)
Also, The registration of the repository should be as Transient and not Scoped.

ConnectionString lost on second execute

I am using asp.net core 2.0 and dapper. I have a class that wraps an IDbConnection Interface and only exposes certain methods. Here is a short version of that class.
public class MyConnectionString : IMyConnectionString
{
private readonly IDbConnection _connection;
public int ConnectionTimeout => _connection.ConnectionTimeout;
public string Database => _connection.Database;
public string ConnectionString { get => null; set => _connection.ConnectionString = value; }
public ConnectionState State => _connection.State;
public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
{
var con = "some logic to get the connection string.";
_connection = new SqlConnection(con);
}
public int Execute(string query, object parameters = null)
{
using (var con = _connection) { return con.Execute(query, parameters); }
}
}
I am injecting this class via a constructor to my Repository services. For example, this is a method that would call it:
internal class SomeRepository
{
private readonly IMyConnectionString _connection;
public SomeRepository(IMyConnectionString connection)
{
_connection = connection;
}
public void ExecuteSomeQuery(Object params)
{
var query = "Some query...";
_connection.Execute(query, params);
}
}
Now the problem is that if I call _connection.Execute(query, params); twice in a single request (2 different services), the second time it gets called the ConnectionString value inside MyConnectionString class is empty. I have tried binding it in Transient and Request scope to see if it would preserve it, but no luck. Any idea on why this is happening or how can I preserve it so that I won't have to create the connection string every time it is requested?
wrapping the Connection inside a using disposes the Connection at his end of execution: just as #Jasen said in comments.
I would, in your case, just get the connection and execute on the Connection created in the constructor: removing the using completely.
You should not create the SqlConnection, since you are implementing dependency injection. You should:
Implement IDisposable to dispose your connection when your class is collected.
Pass a SqlConnection factory to create your SqlConnection, seperating your creation logic from your class.
Your class should resemble something like this:
public class MyConnectionString : IMyConnectionString
{
private readonly IDbConnection _connection;
public int ConnectionTimeout => _connection.ConnectionTimeout;
public string Database => _connection.Database;
public string ConnectionString
{
get => null;
set => _connection.ConnectionString = value;
}
public ConnectionState State => _connection.State;
public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
{
string con = "some logic to get the connection string.";
_connection = new SqlConnection(con);
}
public int Execute(string query, object parameters = null)
{
return _connection.Execute(query, parameters);
}
}
With IDisposable implementation:
using System;
public class MyConnectionString : IMyConnectionString, IDisposable
{
private readonly IDbConnection _connection;
public int ConnectionTimeout => _connection.ConnectionTimeout;
public string Database => _connection.Database;
public string ConnectionString
{
get => null;
set => _connection.ConnectionString = value;
}
public ConnectionState State => _connection.State;
public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
{
string con = "some logic to get the connection string.";
_connection = new SqlConnection(con);
}
public int Execute(string query, object parameters = null)
{
return _connection.Execute(query, parameters);
}
public void Dispose()
{
_connection.Dispose();
}
}
With your own ISqlConnectionFactory factory:
public class MyConnectionString : IMyConnectionString, IDisposable
{
private readonly IDbConnection _connection;
private readonly ISqlConnectionFactory _factory;
public int ConnectionTimeout => _connection.ConnectionTimeout;
public string Database => _connection.Database;
public string ConnectionString
{
get => null;
set => _connection.ConnectionString = value;
}
public ConnectionState State => _connection.State;
public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper, ISqlConnectionFactory factory)
{
_factory = factory;
_connection = _factory.CreateConnection(connProvOpts, encHelper);
}
public int Execute(string query, object parameters = null)
{
return _connection.Execute(query, parameters);
}
}
public interface ISqlConnectionFactory
{
SqlConnection CreateConnection(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper);
}
public class SqlConnectionFactory : ISqlConnectionFactory
{
public SqlConnectionFactory()
{
// Maybe initialization?
}
public SqlConnection CreateConnection(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
{
string con = "some logic to get the connection string.";
_connection = new SqlConnection(con);
}
}
Personally, I would have created and disposed the Connection each time Execute is invoked. This means that outside of Execute, your connection is closed and resources are released.

Using interface and abstract class in repository pattern in ASP.NET Core

We are currently using dapper ORM to access data by calling store procedures. The current code is having a class BusinessFunctions which inherits another class DataFunctions which are having helper methods to execute the stored procedures.
I am not happy with this code. It's just too rigid and not future proof. And above all it's not coded to an interface rather coded to an implementation. I propose an interface IRepository with an abstract class Repository which implements all helper generic methods. Then I create BusinessRepository that implements the abstract Repository class and call the generic helpers method. Again, my colleague is telling me to remove the IRepository interface and just use the Repository abstract class.
public class BusinessFunctions : DataFunctions
{
public BusinessFunctions(ConnectionManager conMgr, LogWriter logWriter, AppUser appUser) : base(conMgr, logWriter, appUser)
{
}
public async Task<Business> FindAsync(int businessId)
{
throw new NotImplementedException();
}
public async Task<Business> FindAsync(string businessGuid)
{
var lst = await StoredProcQueryAsync<Business>("spBusinessGetSetupGUID", new { BusinessGuid = businessGuid });
if (lst.Count() == 0)
throw new NotFoundInDatabaseException("Business", businessGuid);
else
return lst.Single();
}
public async Task<bool> IsHostedTokenizeCardAllowedAsync(string businessGuid)
{
var b = await FindAsync(businessGuid);
if (b.HostedPaymentEnabled)
return true;
else
return false;
}
}
public class DataFunctions : IDisposable
{
private ConnectionManager _conMgr;
private LogWriter _logWriter;
private AppUser _appUser;
public ConnectionManager ConnMgr
{
get { return _conMgr; }
}
protected LogWriter Logger
{
get { return _logWriter; }
}
protected AppUser User
{
get { return _appUser; }
}
public DataFunctions(ConnectionManager conMgr, LogWriter logWriter, AppUser appUser)
{
_conMgr = conMgr;
_logWriter = logWriter;
_appUser = appUser;
}
public void Dispose()
{
}
public async Task StoredProcExecuteNonQueryAsync(string storedProc,
List<StoredProcParameter> storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite
)
{
using (SqlConnection conn = new SqlConnection(ConnMgr.SqlConnectionString))
{
await conn.OpenAsync();
await StoredProcExecuteNonQueryAsync(conn,
storedProc,
storedProcParameters: storedProcParameters,
commandTimeout: commandTimeout,
accessType: accessType);
}
}
public async Task StoredProcExecuteNonQueryAsync(SqlConnection conn,
string storedProc,
List<StoredProcParameter> storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite,
SqlTransaction trans = null
)
{
using (SqlCommand cmd = new SqlCommand(storedProc, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = (int)commandTimeout;
if (trans != null) cmd.Transaction = trans;
if (storedProcParameters != null)
{
foreach(var p in storedProcParameters)
{
cmd.Parameters.Add(p.ToSqlParameter());
}
}
await cmd.ExecuteNonQueryAsync();
}
}
public async Task<IEnumerable<T>> StoredProcQueryAsync<T>(string storedProc,
object storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default,
SqlAccessType accessType = SqlAccessType.ReadWrite)
{
using (SqlConnection conn = new SqlConnection(ConnMgr.SqlConnectionString))
{
conn.Open();
return await StoredProcQueryAsync<T>(conn,
storedProc,
storedProcParameters,
commandTimeout);
}
}
public async Task<IEnumerable<T>> StoredProcQueryAsync<T>(SqlConnection conn,
string storedProc,
object storedProcParameters = null,
SqlCommandTimeout commandTimeout = SqlCommandTimeout.Default)
{
return await conn.QueryAsync<T>(storedProc,
commandType: CommandType.StoredProcedure,
commandTimeout: (int)commandTimeout,
param: storedProcParameters);
}
}
I think the reason you're unhappy with the code is that it seems to be intermingling service functionality into the repository layer. The repository layer should simply call the stored procedure.
public async Task<bool> IsHostedTokenizeCardAllowedAsync(string businessGuid)
{
var b = await FindAsync(businessGuid);
if (b.HostedPaymentEnabled)
return true;
else
return false;
}
This for example is a good candidate to be in the service layer.
Your repo layer should really just have your ConnectionManager or a Connection factory injected via IoC.
The trick we use is to put an attribute on data model fields that we know are going to be stored procedure parameters (usually most or all). Then we have an extension method that reflects over the attributes and pulls the fields, values, and types creating a dapper DynamicParameters object. Most of our repository calls look like this:
public async Task<User> AddUserAsync(UserAdd user)
{
using (var connection = _connectionFactory.Create()
{
var result = connection.ExecuteAsync("dbo.AddUser", user.GetParameters(), commandType: CommandType.StoredProcedure";
return result;
}
}
It's relatively quick and easy to use. Gets are very easy to test. Inserts/Deletes/Updates not so much. You get into needing to mock the SqlConnection which can be problematic.
In addition, if you get into more complex areas that are subject to change, you can use the strategy pattern to move methods into their own classes. Below would be an example of splitting your add method into its own class:
public class MyRepository
{
private readonly IAddMethod<UserAdd> _addMethod;
private readonly IConnectionFactory _connectionFactory;
public MyRepository(IAddMethod<UserAdd> userAddMethod,
IConnectionFactory connectionFactory)
{
//..guard clauses, assignments, etc.
}
public async Task<int> AddAsync(UserAdd user)
{
return _addMethod.AddAsync(user);
}
}
You can even decorate these strategy methods in IoC to hide/augment them as needed. (in structuremap it's .DecorateAllWith()
In short, move any logic to the service layer, consider a generic extension method for creating your DynamicParameters list, and inject the connection factory via IoC. I think you'll find the separation of concerns will simplify things significantly.

How to make WCF set a connection string for use with EntityFramework

Using EntityFramework, I have an auto-generated file with:
namespace Chaos.Data
{
public partial class ChaosModel : OpenAccessContext, IChaosModelUnitOfWork
{
private static string connectionStringName = #"ChaosLibraryConnection";
private static BackendConfiguration backend = GetBackendConfiguration();
private static MetadataSource metadataSource = XmlMetadataSource.FromAssemblyResource("EntitiesModel.rlinq");
public ChaosModel()
:base(connectionStringName, backend, metadataSource)
{ }
public ChaosModel(string connection)
:base(connection, backend, metadataSource)
{ }
......................
In the WCF Service, I am using:
namespace Chaos.DataService
{
[ServiceContract]
public class ChaosService
{
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
if (encountertime == null) return null;
using (var context = new ChaosModel())
{
var query = from et in context.Encountertimes
where et.Tencounter.Date == ((DateTime)encountertime).Date
select et;
var result = context.CreateDetachedCopy(query.ToList());
return result;
}
}
.............................
How can I make the WCF service on startup execute a method (once) that will return a new connection string so that I can change the calls to ChaosModel() to:
using (var context = new ChaosModel(connectionString))
(I am looking for a way to add a static constructor within the WCF service--or something better?).
(The method will determine the network I am on and construct an appropriate connection string to the network server.)
Note: I can make no changes to the auto-generated Entity file.
Use static constructor.
[ServiceContract]
public class ChaosService
{
private static string connectionString;
static ChaosService(){
connectionString = your logic...
}
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
using (var context = new ChaosModel(connectionString))
{
...
}
}
}
or eventually a singleton pattern:
public class ConnectionInfo
{
public string ConnectionString { get; private set; }
private ConnectionInfo()
{
var connectionstring = string.Empty;
//some logic
this.ConnectionString = connectionstring;
}
private static ConnectionInfo current;
public static ConnectionInfo Current {
get {
if (current != null)
current = new ConnectionInfo();
return current;
}
}
}
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
using (var context = new ChaosModel(ConnectionInfo.Current.ConnectionString))
{
...
}
}

Create a Singleton Factory for a Class that takes parameters / arguements

First of all I read this on an article - which basically tells me I should not be using a singleton at all -
Most commonly, singletons don't allow any parameters to be specified when creating the instance - as otherwise a second request for an instance but with a different parameter could be problematic! (If the same instance should be accessed for all requests with the same parameter, the factory pattern is more appropriate.)
Since I need parameters, and same instances with same parameters - I concluded I need a factory pattern.
But I was unable to find a good factory pattern implementation anywhere.
Kindly direct me if you find any good c# singleton factory pattern implementation with parameters
Ok I am going to try and be very specific here... hope this explains my situation.
Alternate methods are most welcome. I just combined a lot of implementations - my understanding may be off.
So I have a class 'A'. It is a class used to connect to a database - Database connection.
The connection needs 4 parameters & the constraints are:
I need to have multiple connections possible - with different databases (parameters differ)
I need only 1 instance of a specific connection - a singleton with parameters which are same (in my understanding)
I will need a factory model as per the article mentioned above and also to limit the number of connections, close the connection after a timeout etc.
On this basis I need a singleton factory with paramenters/arguements... I assume
So the class A is going to look something like this
<which access modifier ?> Class A {
private Class A(string hostname, string port, string username, string pw_hash) {
//create a new instance with the specified parameters
}
//other methods on the connection
protected void close() {
//close the connection
}
}
public class AFactory//should it inherit class A?? {
private IList<A> connections = new List<A>();
private AFactory()
{
//do something
}
private static readonly Lazy<AFactory> lazy
= new Lazy<AFactory>(() => new AFactory());
public static AFactory Instance { get { return lazy.Value; } }
public A getA(string hostname, string service, string username, string pw_hash)
{
foreach (A a in A)
{
if (a.hostname == hostname && a.service == service && a.username == username)
return a;
}
A d = new A(hostname, service, username, pw_hash);
connections.Add(d);
return d;
}
Now this works well and good as long as the class A constructor is public - but It kind of defeats the purpose of a singleton.
What do I need to do to get this code to work.
I need only 1 instance of class A for the specified parameters.
Thanks
Indrajit
Factory is used to generate object rather than manage object. I think a DB connection manager is more suitable in your situation. You can declare the manager as singleton. For individual connection you can use internal class/struct.
See below example:
class DBConnectionManager
{
struct Connection
{
public string Hostname;
public string ServerName;
public string UserName;
public string Password;
public void Connect()
{
}
public void Close()
{
}
}
private static s_instance;
public static DBConnectionManager Instance
{
get {return s_instance; }
}
private List<Connection> m_connections;
public Connection GetConnection(string hostname, string serverName, string userName, string password)
{
// if already exist in m_connections
// return the connection
// otherwise create new connection and add to m_connections
}
public void CloseConnection(string hostname, string serverName, string userName, string password)
{
// if find it in m_connections
// then call Close()
}
public void CloseAll()
{
//
}
}
So I have done this and it works... can you tell me if it is correct. And also is it Thread-Safe?
public Class A
{
private A(string hostname, string port, string username, string pw_hash) {
//create a new instance with the specified parameters
}
//other methods on the connection
protected void close() {
//close the connection
}
public class AFactory
{
private IList<A> connections = new List<A>();
private AFactory()
{
//do something
}
private static readonly Lazy<AFactory> lazy
= new Lazy<AFactory>(() => new AFactory());
public static AFactory Instance { get { return lazy.Value; } }
public A getA(string hostname, string service, string username, string pw_hash)
{
foreach (A a in connections)
{
if (a.hostname == hostname && a.service == service && a.username == username)
return a;
}
A d = new A(hostname, service, username, pw_hash);
connections.Add(d);
return d;
}
}
}
I am using it like this:
A.AFactory fact = A.AFactory.Instance;
A conn = fact.getA(a, b, c, d);
A conn2 = fact.getA(e, f, g, h);
Is there something glaringly wrong with this implementation?
you could try this:
public static class Singlett<Param,T>
where T : class
{
static volatile Lazy<Func<Param, T>> _instance;
static object _lock = new object();
static Singlett()
{
}
public static Func<Param, T> Instance
{
get
{
if (_instance == null)
{
_instance = new Lazy<Func<Param, T>>(() =>
{
lock (Singlett<Param,T>._lock)
{
try
{
ConstructorInfo constructor = null;
Type[] methodArgs = { typeof(Param) };
constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, methodArgs, null);// Binding flags excludes public constructors.
if (constructor == null)
{
constructor = typeof(T).GetConstructor(BindingFlags.Public, null, methodArgs, null);
if (constructor == null)
return delegate(Param o) { return (T)Activator.CreateInstance(typeof(T), new object[] { o }); };
}
return delegate(Param o) { return (T)constructor.Invoke(new object[] { o }); };
}
catch (Exception exception)
{
throw exception;
}
}
});
}
return _instance.Value;
}
}
}
then to use it:
instead of
int i = 10;
MyClass class = new MyClass(i);
you can write:
int i = 10;
MyClass class = Singlett<int,MyClass>.Instance(i);
Try this:
This interface is exposed from the factory initializer and contains the exposed methods and properties.
public interface IDatabase
{
string ConnectionString { get; set; }
IDataReader ExecuteSql(string sql);
}
Factory base abstract class where you can perform common features to different types of database factories.
public abstract class FactoryBase
{
public FactoryBase() { }
public abstract IDatabase GetDataLayer();
}
Concrete sql class that contains your calls. Have a look at the ExecuteSql method. The connection is self contained in the command so you don't have to worry about opening and closing and disposing of it.
public class SQL : IDatabase
{
private string m_ConnectionString = string.Empty;
public string ConnectionString
{
get { return m_ConnectionString; }
set { m_ConnectionString = value; }
}
public IDataReader ExecuteSql(string sql)
{
using (var command = new SqlCommand(sql, new SqlConnection(ConnectionString)) { CommandType = CommandType.Text, CommandText = sql, CommandTimeout = 0 })
{
if (command.Connection.State != ConnectionState.Open) command.Connection.Open();
return command.ExecuteReader();
}
}
}
Sql factory class that creates an instance of the Sql concrete class.
class SQLFactory : FactoryBase
{
public override IDatabase GetDataLayer()
{
return new SQL();
}
}
The factory initializer class that a developer will use to pass in a type of factory and it will return the IDatabase.
public static class FactoryInitializer
{
public static IDatabase LoadFactory<T>(string connectionstring) where T : FactoryBase, new()
{
var factory = new T();
var data = factory.GetDataLayer();
data.ConnectionString = connectionstring;
return data;
}
}
Then use it as:
var factory = FactoryInitializer.LoadFactory<SQLFactory>(connectionString);
factory.ExecuteSql("SELECT ...");
You can then create may be an OracleFactory and an Oracle concrete class and use it the same way.

Categories

Resources