I am trying to write a generic method to get the data from tables.
I am using sqlite-net ORM.
My methods compile well for delete:
public bool DeleteItem<T>(T NewItem)
{
SQLiteConnection conn = new SQLiteConnection(GetConnection());
var result = conn.Delete<T>(NewItem);
//...
}
And
public void CreateTable<T>()
{
SQLiteConnection conn = new SQLiteConnection(GetConnection());
conn.CreateTable<T>();
conn.Close();
}
But if I try to get Table data as list, I get a compile error at conn.Table:
T Must be a non-Abstract Type ....
Here is my code that does not want to compile:
public List<T> GetAllItems<T>(string SelTable)
{
SQLiteConnection conn = new SQLiteConnection(GetConnection());
List<T> MyItems = conn.Table<T>().ToList();
conn.Close();
return MyItems;
}
The definition of Table in SQLiteConnection is
public TableQuery<T> Table<T>() where T : new();
So you have to add type constraints:
public bool DeleteItem<T>(T NewItem) where T : new() // not required, but useful
{
SQLiteConnection conn = new SQLiteConnection("xx");
var result = conn.Delete<T>(NewItem);
return true;
}
public void CreateTable<T>() where T : new() // not required, but useful
{
SQLiteConnection conn = new SQLiteConnection("xx");
conn.CreateTable<T>();
conn.Close();
}
public List<T> GetAllItems<T>(string SelTable) where T : new()
{
SQLiteConnection conn = new SQLiteConnection("xx");
List<T> MyItems = conn.Table<T>().ToList();
conn.Close();
return MyItems;
}
Related
I have a common method which is being called multiple times(around 30-35 references in the project). This method is basically fetching data from DB into data table.
Following is the testable code:
public class MyApp
{
private readonly IDataProvider _dbProvider;
public MyApp(IDataProvider dbProvider)
{
_dbProvider = dbProvider;
}
public void Process()
{
string query = "something";
Helper h = new Helper(_dbProvider);
// This method will be called in the Process method several times
var data = h.GetData(query);
}
}
public interface IDataProvider
{
IDbConnection CreateConnection(string connectionString);
DataTable FillDatatableFromAdapter(IDbCommand command);
}
public class DataProvider : IDataProvider
{
public IDbConnection CreateConnection(string connectionString)
{
return new SqlConnection(connectionString);
}
public DataTable FillDatatableFromAdapter(IDbCommand command)
{
DataSet dataSet = new DataSet();
SqlCommand sqlCommand = command as SqlCommand;
using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand))
{
sqlDataAdapter.Fill(dataSet);
}
if (dataSet.Tables.Count == 0)
return null;
return dataSet.Tables[0];
}
}
public class Helper
{
private readonly IDataProvider _dbProvider;
public Helper(IDataProvider dbProvider)
{
_dbProvider = dbProvider;
}
public DataTable GetData(string query)
{
DataTable table = new DataTable();
using (IDbConnection connection =
_databaseProvider.CreateConnection(_connectionString))
{
connection.Open();
using (IDbCommand command = connection.CreateCommand())
{
command.CommandText = query;
command.Connection = connection;
table = _dbProvider.FillDatatableFromAdapter(command);
}
}
return table;
}
}
I have mocked DB classes to not hit DB from unit tests.
Following is the test case code:
[TestMethod]
public void TestMyApp()
{
Mock<IDbCommand> mockDbCommand = new Mock<IDbCommand>();
Mock<IDbConnection> mockDbConnection = new Mock<IDbConnection>();
Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();
mockDbConnection.Setup(m => m.CreateCommand()).Returns(mockDbCommand.Object);
mockDatabaseProvider.Setup(m => m.CreateConnection(It.IsAny<string>())).Returns(mockDbConnection.Object);
DataTable table = new DataTable();
mockDatabaseProvider.SetupSequence(mock => mock.FillDatatableFromAdapter(It.IsAny<IDbCommand>()))
.Returns(dataTable);
MyApp app = new MyApp(mockDatabaseProvider.Object);
app.Process();
// And then after that I am testing some data.
}
I will be calling GetData() method several times and therefore FillDatatableFromAdapter will also be called several times. As in the above test case I have mocked FillDatatableFromAdapter method and I am returning some fake data table for further testing.
I am aware of SetupSequence method in Moq which I can use to return multiple data tables from the mocked method everytime that method is called.
I want a suggestion is that approach correct because then I will have to create that many number of Data tables which will be return from the mocked method using SetupSequence? Or is there some other better approach?
Any help??
You need to define what you want to test.
A small refactoring will help you better test your code
public class MyApp
{
private readonly IDataProvider _dbProvider;
private readonly IHelper _h;
public MyApp(IDataProvider dbProvider)
{
_dbProvider = dbProvider;
_h = new Helper(_dbProvider);
}
public void Process()
{
string query = "something";
// This method will be called in the Process method several times
var data = _h.GetData(query);
}
}
public interface IDataProvider
{
IDbConnection CreateConnection(string connectionString);
DataTable FillDatatableFromAdapter(IDbCommand command);
}
public class DataProvider : IDataProvider
{
public IDbConnection CreateConnection(string connectionString)
{
return new SqlConnection(connectionString);
}
public DataTable FillDatatableFromAdapter(IDbCommand command)
{
DataSet dataSet = new DataSet();
SqlCommand sqlCommand = command as SqlCommand;
using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand))
{
sqlDataAdapter.Fill(dataSet);
}
if (dataSet.Tables.Count == 0)
return null;
return dataSet.Tables[0];
}
}
public interface IHelper
{
public DataTable GetData(string query);
}
public class Helper
{
private readonly IDataProvider _dbProvider;
public Helper(IDataProvider dbProvider)
{
_dbProvider = dbProvider;
}
public DataTable GetData(string query)
{
DataTable table = new DataTable();
using (IDbConnection connection =
_databaseProvider.CreateConnection(_connectionString))
{
connection.Open();
using (IDbCommand command = connection.CreateCommand())
{
command.CommandText = query;
command.Connection = connection;
table = _dbProvider.FillDatatableFromAdapter(command);
}
}
return table;
}
}
Now you can have 4 isolated tests
[TestMethod]
public void TestProccessFunctionCallHelperGetData()
{
Mock<IHelper> mockHelper = new Mock<IHelper>();
Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();
mockHelper.Setup(m => m.getData(It.IsAny<string>())).Returns(new DataTable());
MyApp app = new MyApp(mockDatabaseProvider.Object, mockHelper.Setup);
app.Process();
Assert.AreEquals(mockHelper.numTimesCalled, 1);
}
[TestMethod]
public void TestHelperGetData()
{
var query = ""; //TODO: write dummy query
var dataTableToReturn = new DataTable(); //TODO: add some data
Mock<IDataProvider> mockDatabaseProvider = new Mock<IDataProvider();
mockDatabaseProvider.Setup(m => m.CreateConnection(It.IsAny<string>())).Returns(new SqlConnection("dummy connection string"));
mockDatabaseProvider.Setup(m => m.FillDatatableFromAdapter(It.IsAny<IDbCommand>())).Returns(dataTableToReturn );
IHelper h = new Helper(mockDatabaseProvider.Object);
var actualDataTable = h.getData(query);
Assert.AreEqual(dataTableToReturn, actualDataTable );
}
[TestMethod]
public void TestDataProviderFillDataTableFromAdapter()
{
//This test seems to me more like integration test because you need to mock a db or use a real db
}
[TestMethod]
public void TestYourData()
{
// Create a dataTable with data so you can continue with your testing
}
I have a design problem with a 3 Tier application. Usually my database layer is only 1 class like this one:
public class DA
{
string _connString = "";
public DA()
{
_connString = ConfigurationManager.ConnectionStrings["MyConnString"].ToString();
}
public DA(string connString)
{
_connString = connString;
}
private SqlConnection GetConnection()
{
...
}
public Employee GetEmployee(int idEmployee)
{
...
}
public Whatever GetWhatever(int idWhatever)
{
...
}
...
But now I have a pretty big project and I would like to separate the DA class into smaller classes like DA_Employee, DA_Whatever, etc.
I would like to instantiate only DA one time and access the other classes like that:
DA db = new DA(connString);
db.Employee.GetEmployee(12);
db.Whatever.GetWhatever(89);
db.Whatever.UpdateWhatever(89, "newname");
I would prefer NOT having something like this:
DA db = new DA(connString);
DA_Employee dbEmployee = new DA_Employee(connString);
DA_Whatever dbWhataver = new DA_Whatever(connString);
I think I can instantiate all my classes in my main constructor and have some properties to access them?
How can I give access to all classes to GetConnection()?
Any help and reference appreciated.
Thanks!
Yes, you make the classes a property of DA.
public interface IRepository<T>
{
T GetById(int id);
}
public class EmployeeRepository : IRepository<Employee>
{
private SqlConnection sqlConn;
public EmployeeRepository(SqlConnection sqlconn)
{
this.sqlConn = sqlConn;
}
public Employee GetById(int id)
{
return new Employee();
}
}
Pass the SqlConnection as a constructor dependency.
public class DA : IDisposable
{
private SqlConnection sqlConn;
private IRepository<Employee> employeeRepo;
private IReposiotry<Whatever> whateverRepo;
public DA(string connectionString)
{
this.sqlConnection = GetSqlConnection(connectionString);
this.employeeRepo = new EmployeeRepository(this.sqlConnection);
this.whateverRepo = new WhateverRepository(this.sqlConnection);
}
public IRepository<Employee> Employee { get { return employeeRepo; } }
public IRepository<Whatever> Whatever { get { return whateverRepo; } }
}
And its usage
using (var db = new DA("connectionString"))
{
db.Employee.GetById(1);
db.Whatever.GetById(10);
}
I am currently implementing a small software that could read data from different DataBases. The followin is the code:
interface Fetch
{
Dictionary<string, DbDataReader> GetData();
}
abstract class Conn : Fetch
{
abstract public void Connect();
abstract public Dictionary<string, SqlDataReader> GetData();
}
class SqlConn : Conn
{
public override void Connect()
{
_connection = new SqlConnection(_connectionString);
try
{
_connection.Open();
}
catch (SqlException dbe)
{
throw dbe;
}
}
public override Dictionary<string, SqlDataReader> GetData()
{
using (_connection)
{
Dictionary<string, SqlDataReader> dataDictionary = new Dictionary<string, SqlDataReader>();
_xmlDoc.Load("Queries.xml");
XPathNavigator navigator = _xmlDoc.CreateNavigator();
XPathNodeIterator iterator = navigator.Select("//query");
while (iterator.MoveNext())
{
_command = new SqlCommand(iterator.Current.ToString());
_command.Connection = _connection;
_command.CommandText = iterator.Current.ToString();
SqlDataReader reader = _command.ExecuteReader() as SqlDataReader;
dataDictionary.Add(iterator.Current.GetAttribute("name", ""), reader);
}
return dataDictionary;
}
}
}
class OraConn : Conn
{
public override void Connect()
{
_connection = new OracleConnection(_connectionString);
}
public override Dictionary<string, OracleDataReader> GetData()
{
using (_connection)
{
Dictionary<string, OracleDataReader> dataDictionary = new Dictionary<string, OracleDataReader>();
_xmlDoc.Load("Queries.xml");
XPathNavigator navigator = _xmlDoc.CreateNavigator();
XPathNodeIterator iterator = navigator.Select("//query");
while (iterator.MoveNext())
{
_command = new OracleCommand(iterator.Current.ToString());
_command.Connection = _connection;
_command.CommandText = iterator.Current.ToString();
OracleDataReader reader = _command.ExecuteReader() as OracleDataReader;
dataDictionary.Add(iterator.Current.GetAttribute("name", ""), reader);
}
return dataDictionary;
}
}
}
But My problem is the return type, SQLDataReader and OraDataReader in the derived class.
This causes a compiler error that states
‘Error 2 'DashBoard.Connection.OraConn.GetData()': return type must be 'System.Collections.Generic.Dictionary' to match overridden member 'DashBoard.Connection.Conn.GetData()
’.
How can I solve this problem? Or is there other ways to implement this funtion?
Thank you!
The method signature has to stay the same, but as long as your sub-classes inherit from DbDataReader you can return other DbDataReader types such as SqlDataReader and it will compile.
for example:
abstract class DbDataReader
{
// ...
}
class SqlDataReader : DbDataReader
{
}
// ...
class SqlConn : Conn
{
public override Dictionary<string, DbDataReader> GetData()
{
return new Dictionary<string, DbDataReader>
{
{ "Key", new SqlDataReader() }
}
}
}
You can try this way:
public interface IValue<T> { T GetValue(); }
public class SomeClass : IValue<DbDataReader>, IValue<SqlDataReader>
{
DbDataReader IValue<DbDataReader>.GetValue() { return objDbDataReader; }
SqlDataReader IValue<SqlDataReader>.GetValue() { return objSqlDataReader; }
}
I'm trying to create a generic data retrieval process. What I have currently works, but there is a part of it that doesn't seem right and I'm hoping there is a better way to accomplish it.
So the idea is that I have classes for each table in the database, here is an example of a class:
public class CMCGRGRGROUP : IFacetsObject<CMCGRGRGROUP>
{
public int GRGR_CK { get; set; }
public string GRGR_NAME { get; set; }
public string GRGR_ADDR1 { get; set; }
public IEnumerable<CMCGRGRGROUP> ToObject(DataTable table)
{
return table.AsEnumerable().Select(row =>
{
return new CMCGRGRGROUP
{
GRGR_CK = Convert.ToInt32(row["GRGR_CK"]),
GRGR_NAME = row["GRGR_NAME"].ToString(),
GRGR_ADDR1 = row["GRGR_ADDR1"].ToString()
};
});
}
}
You'll notice that the class implements an interface of its own type. The interface simply defines a method called ToObject, which is used to convert a datatable to a class of that particular type:
public interface IFacetsObject<T>
{
IEnumerable<T> ToObject(DataTable obj);
}
Now, here is the method that I am using to execute a query:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return obj.ToObject(dt); //this is the interface method
}
}
So the main question is:
How can the generic method know that T should implement IFacetsObject<T>? That way I don't have to pass IFacetsObject<T> as a parameter. Ideally, I could change the return line to be something like this:
return T.ToObject(dt);
And call it like this:
var result = ExecuteQuery<CMCGRGRGROUP>(sql).Take(5);
Instead of like this:
var result = ExecuteQuery<CMCGRGRGROUP>(sql, new CMCGRGRGROUP()).Take(5);
I'll admit that I'm not terribly familiar with generics yet so there may be something within the implementation that isn't right.
You can add a constraint on your ExecuteQuery method. You already have one: requiring that T be newable. You'd declare it like:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj)
where T : IFacetsObject<T>, new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return obj.ToObject(dt); //this is the interface method
}
}
So it now knows T is an IFacetsObject<T>. You could now do:
public IEnumerable<T> ExecuteQuery<T>(string sql)
where T : IFacetsObject<T>, new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return new T().ToObject(dt); //this is the interface method
}
}
Which IMO is still pretty ugly.
EDIT Response:
Note that you cannot call T.ToObject - an interface cannot define a static method. The workaround is the use of new to create a new instance of T and call the instance method.
You need a generic constraint on your interface. Declare it like this:
public interface IFacetsObject<T> where T : IFacetsObject<T>
{
IEnumerable<T> ToObject(DataTable obj);
}
In order to get this to work, you also have to change your declaration like this:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj)
where T : IFacetsObject<T>, new()
I've tried for a while now and I really dont get it. I recive error "Cannot implicitly convert type 'void' to 'string'"
I have tried multiple variants of string, int, nothing, void, public, static and nope I really dont get it right.
I want to get one value from my db thoug my DAL and BLL, my code looks like this.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Repeater1.DataSource = BLL.getGames();
Repeater1.DataBind();
var culture = CultureInfo.GetCultureInfo("sv-SE");
var dateTimeInfo = DateTimeFormatInfo.GetInstance(culture);
var dateTime = DateTime.Today;
int weekNumber = culture.Calendar.GetWeekOfYear(dateTime, dateTimeInfo.CalendarWeekRule, dateTimeInfo.FirstDayOfWeek);
string mroundY = dateTime.Year.ToString();
string mroundW = weekNumber.ToString();
string mrounddate = mroundY + mroundW;
string mround = BLL.getMroundGames(mrounddate); <-- Here's the error
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
}
}
My BLL looks like this;
public class BLL
{
public static void getMroundGames(string mrounddate)
{
SqlCommand getMroundGames = new SqlCommand("SELECT mround FROM gameTB where mround = #mrounddate");
DAL.ExecuteNonQuery(getMroundGames);
}
}
Also tried this;
public class BLL
{
public static DataTable getMroundGames(string mrounddate)
{
SqlCommand getMroundGames = new SqlCommand("SELECT mround FROM gameTB where mround = #mrounddate");
getMroundGames.Parameters.Add("#mrounddate", SqlDbType.VarChar, 10).Value = mrounddate;
return DAL.GetData(getMroundGames);
}
}
And finaly my DAL looks like this;
public class DAL
{
public static SqlConnection GetConnection()
{
SqlConnection conn = new
SqlConnection(ConfigurationManager.ConnectionStrings["tiptopConnectionString"].ConnectionString);
conn.Open();
return conn;
}
public static DataTable GetData(SqlCommand command)
{
try
{
using (SqlConnection conn = GetConnection())
{
using (DataSet ds = new DataSet())
{
using (SqlDataAdapter da = new SqlDataAdapter())
{
da.SelectCommand = command;
da.SelectCommand.Connection = conn;
da.Fill(ds);
return ds.Tables[0];
}
}
}
}
catch (Exception err)
{
throw new ApplicationException(string.Format("Felmeddelande:{0}", err.Message));
}
}
public static object ExecuteScalar(SqlCommand command)
{
using (SqlConnection conn = GetConnection())
{
command.Connection = conn;
object result = command.ExecuteScalar();
return result;
}
}
public static void ExecuteNonQuery(SqlCommand command)
{
using (SqlConnection conn = GetConnection())
{
command.Connection = conn;
command.ExecuteNonQuery();
}
}
}
Where to begin?
The signature for this is wrong;
public static void getMroundGames(string mrounddate)
You need to change it to something similar to ;
public static string getMroundGames(string mrounddate)
Retrieve the string value from your DAL and return to the consumer accordingly.
var dt = Dal.GetData();
return (string)dt.Rows[0]["field"];
However, in all honesty, I would not be passing a datatable from your DAL to your BLL. I would return the string directly or introduce a DTO and populate this from your DAL through your BLL, back to the consumer.
You need to add a return type of string to getMroundGameas(string mrounddate).
It's not clear what type of object DAL is, but you should also probably use ExecuteReader method, http://msdn.microsoft.com/en-us/library/bb344397.aspx
rather then ExecuteNonQuery. This will return a Reader which can be queried for the value returned by the select statement.
Finally you should return the value.