First, this is my first post on this stack exchange site. So please be patient with me. If there is something wrong or you miss something, please let me know. I will add it asap.
I am currently working with the SOLID principles and trying to achieve a practical implementation.
I have seen Tim Corey's practical videos on YouTube, which explain the principles well.
Now I have a project where I would like to implement this and I am struggling a bit with the implementation of the dependency inversion principle in the context of SQL queries to a SQL server. Here, several instances of different classes are often needed, e.g. SqlConnection
, SqlDataAdapter
or DataSet
.
Perhaps in advance. I know that Dependency Injection could make things easier. But first I wanted to understand the principle of dependency inversion.
I've created now a (mini) project, where my approach becomes clear (the project with the complete code is also available on Github https://github.com/dnsnx/Dependency_Inversion_SQL).
I first created an interface IDatabase
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dependency_Inversion_SQL.Interfaces
{
public interface IDatabase
{
void AddParameters((string name, object value)[] parameters);
System.Data.DataSet GetData(string commandText, System.Data.CommandType commandType = System.Data.CommandType.Text, string sourceTable = null);
}
}
For simplicity, I just want to read data at the moment - no updates yet.
So I defined a method for setting parameters (AddParameters
) and one method for reading the data (GetData
).
First difficulty here:
Keep the interface non-specific, i.e. not directly designed for SQL Server. In the future, the DB system could be changed!
Then I created a class SQLDatabase
which implements this interface:
using Dependency_Inversion_SQL.Interfaces;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace Dependency_Inversion_SQL.Classes
{
public class SQLDatabase : IDatabase
{
#region Variables
private readonly string _SqlServer = "Integrated Security=true;Initial Catalog=Test;Server=localhost\\localhost;";
private SqlConnection _SqlConnection;
private SqlCommand _SqlCommand;
private SqlDataAdapter _SqlDataAdapter;
private DataSet _DataSet;
#endregion
#region Properties
public string SqlServer
{
get
{
return _SqlServer;
}
}
#endregion
#region Constructors
public SQLDatabase(SqlConnection sqlConnection, SqlDataAdapter sqlDataAdapter, DataSet dataSet)
{
_SqlConnection = sqlConnection;
_SqlConnection.ConnectionString = _SqlServer;
_SqlCommand = _SqlConnection.CreateCommand();
_SqlDataAdapter = sqlDataAdapter;
_DataSet = dataSet;
}
#endregion
#region Methods
public void AddParameters((string name, object value)[] parameters)
{
for (var i = 0; i < parameters.Length; i++)
{
_SqlCommand.Parameters.AddWithValue(parameters[i].name, parameters[i].value);
}
}
public System.Data.DataSet GetData(string commandText, System.Data.CommandType commandType = System.Data.CommandType.Text, string sourceTable = null)
{
if (sourceTable == null)
{
sourceTable = "someName";
}
_DataSet.Clear();
_SqlCommand.CommandText = commandText;
_SqlCommand.CommandType = commandType;
_SqlDataAdapter.SelectCommand = _SqlCommand;
_SqlDataAdapter.SelectCommand.Connection.Open();
_SqlDataAdapter.Fill(_DataSet, sourceTable);
_SqlDataAdapter.SelectCommand.Connection.Close();
_SqlCommand.Parameters.Clear();
_SqlCommand.CommandText = string.Empty;
return _DataSet;
}
#endregion
}
}
Now, to implement dependency inversion, we need a factory
class that defines which instances are created:
Factory
:
using Dependency_Inversion_SQL.Classes;
using Dependency_Inversion_SQL.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dependency_Inversion_SQL
{
public static class Factory
{
public static ICar CreateCar()
{
return new Car(CreateDatabase());
}
public static IDatabase CreateDatabase()
{
return new SQLDatabase(CreateDatabaseConnection(), CreateDatabaseAdapater(), CreateDatabaseDataSet());
}
public static System.Data.SqlClient.SqlConnection CreateDatabaseConnection()
{
return new System.Data.SqlClient.SqlConnection();
}
public static System.Data.SqlClient.SqlDataAdapter CreateDatabaseAdapater()
{
return new System.Data.SqlClient.SqlDataAdapter();
}
public static System.Data.DataSet CreateDatabaseDataSet()
{
return new System.Data.DataSet();
}
}
}
Then I needed a class, which queries SQL data. I created a simple class Car
, which "reads" a car name.
using Dependency_Inversion_SQL.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dependency_Inversion_SQL.Classes
{
class Car : ICar
{
IDatabase _Database;
public Car(IDatabase database)
{
_Database = database;
}
public string GetCarName()
{
var result = _Database.GetData("SELECT 'Audi R8' as CarName");
return result.Tables[0].Rows[0].ItemArray[0].ToString();
}
}
}
Now my console application:
using Dependency_Inversion_SQL.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dependency_Inversion_SQL
{
class Program
{
static void Main(string[] args)
{
ICar car = Factory.CreateCar();
Console.WriteLine($"My dream car is an { car.GetCarName() }");
Console.ReadLine();
}
}
}
As I wrote above, I was wondering, if there would be a more easy way to implement the SQLDatabase
class.
It needs all the stuff in the constructor (SqlConnection
, SqlDataAdapter
, DataSet
) and so on.
What I also don't know, if it is correct, how I implemented the generation of these classes in the Factory
class.
public static System.Data.SqlClient.SqlConnection CreateDatabaseConnection()
{
return new System.Data.SqlClient.SqlConnection();
}
public static System.Data.SqlClient.SqlDataAdapter CreateDatabaseAdapater()
{
return new System.Data.SqlClient.SqlDataAdapter();
}
public static System.Data.DataSet CreateDatabaseDataSet()
{
return new System.Data.DataSet();
}
What I understood is, that the return type, should be a interface as best. This is in my eyes not possible with the SQL classes.
If this question is too large or I misunderstood the stack exchange site, please let me know. I will delete the question then. Many thanks in advance for any comment on this.