Use of Generic Delegates - c#

We know that "Action, Func and Predicate are pre-defined Generic delegates. So as delegate they can point to functions with specified signature."
I have following data-access scenario in which Func<T,R> helps in avoiding a foreach loop in the calling method. The approach 2 doesn’t have looping. Here Func<T,R> helped to avoid loop.
What are the other scenarios for generic delegates in which it can save lots of lines of code?
REFERENCES
Dynamically Composing Expression Predicates
Advanced C#
C#/.NET Little Wonders: The Predicate, Comparison, and Converter Generic Delegates
Func vs. Action vs. Predicate
What is Func, how and when is it used
How can I pass in a func with a generic type parameter?
CODE
Approach 1
public class MyCommonDAL
{
public static IEnumerable<IDataRecord> ExecuteQueryWithTextCommandType(string commandText, List<SqlParameter> commandParameters)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = commandText;
command.CommandTimeout = 0;
command.Parameters.AddRange(commandParameters.ToArray());
connection.Open();
using (var rdr = command.ExecuteReader())
{
while (rdr.Read())
{
yield return rdr;
}
rdr.Close();
}
}
}
}
}
public class MyLogDAL
{
public List<LogSeverityType> GetLogSeveritiesFirstApproach(LogSeverityType logSeverityType)
{
List<SqlParameter> commandParameters = new List<SqlParameter>()
{
new SqlParameter {ParameterName = "#CreatedDateTime",
Value = logSeverityType.CreatedDateTime,
SqlDbType = SqlDbType.DateTime}
};
string commandText = #"SELECT * FROM dbo.LogSeverityType WHERE CreatedDateTime > #CreatedDateTime";
var results = MyCommonDAL.ExecuteQueryWithTextCommandType(commandText, commandParameters);
List<LogSeverityType> logSeverities = new List<LogSeverityType>();
//LOOP
foreach (IDataRecord rec in results)
{
LogSeverityType objLogSeverityType = LogSeverityType.LogSeverityTypeFactory(rec);
logSeverities.Add(objLogSeverityType);
}
return logSeverities;
}
}
Approach 2
public class MyCommonDAL
{
public static IEnumerable<T> ExecuteQueryGenericApproach<T>(string commandText, List<SqlParameter> commandParameters, Func<IDataRecord, T> factoryMethod)
{
//Action, Func and Predicate are pre-defined Generic delegates.
//So as delegate they can point to functions with specified signature.
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = commandText;
command.CommandTimeout = 0;
command.Parameters.AddRange(commandParameters.ToArray());
connection.Open();
using (var rdr = command.ExecuteReader())
{
while (rdr.Read())
{
yield return factoryMethod(rdr);
}
rdr.Close();
}
}
}
}
}
public class MyLogDAL
{
public List<LogSeverityType> GetLogSeveritiesSecondApproach(LogSeverityType logSeverityType)
{
List<SqlParameter> commandParameters = new List<SqlParameter>()
{
new SqlParameter {ParameterName = "#CreatedDateTime",
Value = logSeverityType.CreatedDateTime,
SqlDbType = SqlDbType.DateTime}
};
string commandText = #"SELECT * FROM dbo.LogSeverityType WHERE CreatedDateTime > #CreatedDateTime";
//var results = MyCommonDAL.ExecuteQueryWithTextCommandType(commandText, commandParameters);
IEnumerable<LogSeverityType> logSeverities = MyCommonDAL.ExecuteQueryGenericApproach<LogSeverityType>(commandText, commandParameters, LogSeverityType.LogSeverityTypeFactory);
//foreach (IDataRecord rec in results)
//{
// LogSeverityType objLogSeverityType = LogSeverityType.LogSeverityTypeFactory(rec);
// logSeverities.Add(objLogSeverityType);
//}
return logSeverities.ToList();
}
}
Other Code Required
public class LogSeverityType
{
public int LogSeverityTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime CreatedDateTime { get; set; }
public static LogSeverityType LogSeverityTypeFactory(IDataRecord record)
{
return new LogSeverityType
{
LogSeverityTypeID = (int)record[0],
Name = (string) record[1],
Description = (string)record[2],
CreatedDateTime = (DateTime) record[3]
};
}
}
static void Main(string[] args)
{
MyLogDAL logDAL = new MyLogDAL();
LogSeverityType logSeverityType = new LogSeverityType();
logSeverityType.CreatedDateTime = Convert.ToDateTime("1/1/2000");
List<LogSeverityType> logSeverities = logDAL.GetLogSeveritiesSecondApproach(logSeverityType);
}

I use generic delegates when parsing / finding nodes in XML / HTML documents, and assigning the values to properties. I wrote a blog post about it, which shows refactoring the code to pass in a generic delegate, and how much code was removed.

Related

value from a method to bind in Viewbag methods

Trying to return value from GetStage_details() methods and bind it to ViewBag.Stage_details but getting error at var result.
Error msg is :
can't implicitly convert type oracle.ManagedDtaAccessclient.oracledatareader to System.Collection.generic.List<Models.Stage_Details."
Any idea how to resolve it show that correct value return from table and bind to ViewBag.Stage_details will be appreciated.
public class Stage_details
{
public int Stage_Cd { get; set; }
public string Stage_Desc { get; set; }
}
public ActionResult Index_shift()
{
ViewBag.Stage_details = new SelectList(GetStage_details(), "Stage_Cd", "Stage_Desc");
}
private List<Stage_details> GetStage_details()
{
List<Stage_details> Stage_detail = new List<Stage_details>();
OracleConnection conn = new
OracleConnection(ConfigurationManager.ConnectionStrings["Mycon"].ToString());
conn.Open();
string cmdText= "select a.stage_cd,a.stage_desc from Stage_Mst a";
OracleCommand command = new OracleCommand(cmdText,conn);
command.CommandType = CommandType.Text;
var result = command.ExecuteReader();
return result;
}
According to Microsoft documentation, You could read each element from reader and build Stage_detail object inside loop, like the following code :
private List<Stage_details> GetStage_details()
{
List<Stage_details> Stage_detail = new List<Stage_details>();
OracleConnection conn = new
OracleConnection(ConfigurationManager.ConnectionStrings["Mycon"].ToString());
conn.Open();
string cmdText = "select a.stage_cd,a.stage_desc from Stage_Mst a";
OracleCommand command = new OracleCommand(cmdText, conn)
{
CommandType = CommandType.Text
};
using (OracleDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Stage_detail.Add(new Stage_details { Stage_Cd = (int)reader["stage_cd"], Stage_Desc = reader["stage_desc"].ToString() });
}
}
return Stage_detail;
}
I hope you find this helpful.

C# - MySql - Best way to convert data from MySqlDataReader to different models

I have experience in working and fixing bugs with existing code bases that implement MySql code, but have to design a new program from scratch at my new job. I am not sure what is the best way to return data from MySqlDataReader to my custom models. Please advise!
Here's what I have,
Folder structure:
Models (folder)
Metadata.cs
User.cs
MySqlDb.cs
Metadata.cs: Reresents data from metadata table
public class Metadata
{
public int Id { get; set; }
public string Title { get; set; }
public string Sku { get; set; }
public bool IsLive { get; set; }
}
User.cs: Represents data from user table
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public int Age { get; set; }
public string Address { get; set; }
}
MySqlDb.cs
using MySql.Data;
using MySql.Data.MySqlClient;
public class MySqlDb
{
public MySqlConnection Connection { get; set;}
public MySqlDb(string connectionString)
{
Connection = new MySqlConnection(connectionString);
}
public List<Metadata> RunSelectQueryForMetadata(string query)
{
var rdr = new MySqlCommand(query, Connection).ExecuteReader();
var metadata = new List<Metadata>();
using (rdr)
{
while(rdr.Read())
{
metadata.Add(
new Metadata {
Id = rdr["id"],
Title = rdr["title"],
Sku = rdr["sku"],
IsLive = rdr["islive"],
});
} // while
} // using
return metadata;
} // public void RunSelectQuery(string query)
} // public class MySqlDb
If I try to get Users data, I am thinking of writing another method (RunSelectQueryForUsers). I would like to avoid writing different methods for different tables. I am not sure how to use one method for retrieving data from different tables with different data structures and typecast them to the Model I want.
Any help is greatly appreciated!!
One way is to use micro-orm such as Dapper which is a simple object mapper built for .Net. Dapper extends the IDbConnection by providing useful extension methods to query your database.
Example of implementing dapper within your current menthod:
public List<Metadata> RunSelectQueryForMetadata(string query)
{
var metadata = new List<Metadata>();
try // implement proper error handling
{
Connection.Open();
metadata = Connection.Query<Metadata>(query).ToList();
Connection.Close();
}
catch(Exception ex)
{
// error here
}
return metadata;
}
Some useful links:
Dapper Github
Dapper Tutorial
Converting it to generic method: (not tested right now)
public List<T> RunSelectQuery<T>(string query)
{
try // implement proper error handling
{
Connection.Open();
metadata = Connection.Query<T>(query).ToList();
Connection.Close();
}
catch(Exception ex)
{
// error here
}
return metadata;
}
and use something like this below:
List<Metadata> myMetadata = RunSelectQuery<Metadata>(query);
I prefer a pattern more like this:
public class MySqlDb
{
//1. This should not be public!
// Keeping it private forces other code to go through your public methods,
// rather than using the connection directly.
// Even better if the class knows how to read the string from a
// config rile rather than accepting it via the constructor.
//2. Don't save a connection object for re-use.
// ADO.Net has a connection pooling feature that works when you
// create new objects for most queries
private string ConnectionString { get; set;}
public MySqlDb(string connectionString)
{
ConnectionString = connectionString;
}
//1. Use IEnumerable instead of List
// ...don't pull all of the results into memory at the same time until/unless you really have to.
//2. Methods that accept query strings should also accept parameters.
// Otherwise you are forced to build sql strings in insecure crazy-vulnerable ways
public IEnumerable<Metadata> RunSelectQueryForMetadata(string query, IEnumerable<MySqlParameter> parameters)
{
using (var cn = new MySqlConnection(ConnectionString))
using (var cmd = new MySqlCommand(query, cn))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
cn.Open();
using(var rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
yield return new Metadata {
Id = rdr["id"],
Title = rdr["title"],
Sku = rdr["sku"],
IsLive = rdr["islive"],
};
}
rdr.Close();
}
}
}
}
Ultimately, the ideal is for the RunSelectQuery__() method to be generic and private, and for public methods to not accept SQL statements. The goal is to force all SQL in your program to live in the MySqlDb class. Each query has a method that accepts specific typed inputs, and returns typed output. The reason you have that goal is to make it easy to manage your database access and easy to audit that all of your SQL code is safely using parameters (and not vulnerable to sql injection attacks! ). You want something like this:
//updated to remove the earlier explanatory comments
// and show example methods for isolating SQL from the rest of the application.
public class MySqlDb
{
private string ConnectionString { get; set;}
private string ReadConnectionStringFromConfigFile()
{
//TODO
throw NotImplementedException();
}
public MySqlDb()
{
ConnectionString = ReadConnectionStringFromConfigFile();
}
//This is now PRIVATE and generic, and allows for parameterized queries
private IEnumerable<T> RunSelectQuery(string query, Func<IDataReader, T> translateRecord, IEnumerable<MySqlParameter> parameters)
{
using (var cn = new MySqlConnection(ConnectionString))
using (var cmd = new MySqlCommand(query, cn))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
cn.Open();
using(var rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
yield return translateRecord(rdr);
}
rdr.Close();
}
}
}
////// Example methods showing how to use the generic method above
// These methods are the only public part of your class
public MetaData GetMetaDataById(int ID)
{
string sql = "SELECT * FROM MetatData WHERE ID= #ID";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#ID",
MySqlDbType = MySqlDbType.Int32,
Value = ID
}
};
return RunSelectQuery<MetaData>(sql, parameters, r =>
new Metadata {
Id = r["id"],
Title = r["title"],
Sku = r["sku"],
IsLive = r["islive"],
}).FirstOrDefault();
}
public IEnumerable<MetaData> GetAllMetaData()
{
string sql = "SELECT * FROM MetatData";
return RunSelectQuery<MetaData>(sql, null, r =>
new Metadata {
Id = r["id"],
Title = r["title"],
Sku = r["sku"],
IsLive = r["islive"],
});
}
public User GetUserByID(int ID)
{
string sql = "SELECT * FROM User WHERE ID= #ID";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#ID",
MySqlDbType = MySqlDbType.Int32,
Value = ID
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
}).FirstOrDefault();
}
public User GetUserByUsername(string UserName)
{
string sql = "SELECT * FROM User WHERE Username= #UserName";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#UserName",
MySqlDbType = MySqlDbType.VarChar,
Size = 20, //guessing at username lenght
Value = UserName
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
}).FirstOrDefault();
}
public IEnumerable<User> FindUsersByAge(int Age)
{
string sql = "SELECT * FROM User WHERE Age = #Age";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#Age",
MySqlDbType = MySqlDbType.Int32,
Value = Age
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
});
}
}
In larger applications, you abstract this further into a separate project, with a private class for the lower-level methods that are private here, and a public class for each of the object types you use via that database. You might even go full-blown service-oriented architecture, where you get all your data via web service calls, and only the service layer talks directly to any database.
Of course, at this level you can also use a mirco-ORM like Dapper. Micro-ORMs will help you avoid re-writing the same mapping code over and over, and also help more with the INSERT/UPDATE side of data operations. Their goal is to take over as much of the boilerplate code for you as they can.
The advantage of a micro-ORM over a full ORM is it keeps you closer to the SQL. This is a good thing. Full-blown ORMs like Entity Framework or NHibernate effectively force you to learn a whole new language on top of the SQL, while mostly limiting you to basic SQL statements that often lose the advantages from the "relational" part of a relational database. Eventually, you often end up needing to understand and write complex raw SQL anyway to optimize performace. Micro-ORMs try to offer a happy-medium... taking away as much of the boiler plate code needed to talk to a database as they can, while still leaving you to write your own SQL.
While not tailored to using MySql and straight up sql, the below code snippets provide a means to do what you're asking using generics. Could use some improvements though...
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.General
{
public abstract class DataObject
{
protected abstract void Initialize(IDataRecord dataRow);
private static string _connectionString = "";
/// <summary>
/// Loads a single data object from the results of a stored procedure.
/// </summary>
protected static T ReadObject<T>(string procedureName, SqlParameter[] sqlParameters, Type dataType)
{
DataObject returnItem = null;
using (SqlConnection sqlConnection = new SqlConnection(GetConnectionString()))
using (SqlCommand command = BuildCommand(sqlConnection, procedureName, sqlParameters))
{
sqlConnection.Open();
//Execute the reader for the given stored proc and sql parameters
using (IDataReader reader = command.ExecuteReader())
{
//If we get no records back we'll still return null
while (reader.Read())
{
returnItem = (DataObject)Activator.CreateInstance(typeof(T));
returnItem.Initialize(reader);
break;
}
}
}
//Return our DataObject
return (T)Convert.ChangeType(returnItem, dataType);
}
/// <summary>
/// Reads a collection of data objects from a stored procedure.
/// </summary>
protected static List<T> ReadObjects<T>(string procedureName, SqlParameter[] sqlParameters)
{
//Get cached data if it exists
List<T> returnItems = new List<T>();
T dataObject;
using (SqlConnection sqlConnection = new SqlConnection(GetConnectionString()))
using (SqlCommand command = BuildCommand(sqlConnection, procedureName, sqlParameters, null))
{
sqlConnection.Open();
//Execute the reader for the given stored proc and sql parameters
using (IDataReader reader = command.ExecuteReader())
{
//If we get no records back we'll still return null
while (reader.Read())
{
dataObject = (T)Activator.CreateInstance(typeof(T));
(dataObject as DataObject).Initialize(reader);
returnItems.Add(dataObject);
}
}
}
//Return the DataObjects
return returnItems;
}
/// <summary>
/// Builds a SQL Command object that can be used to execute the given stored procedure.
/// </summary>
private static SqlCommand BuildCommand(SqlConnection sqlConnection, string procedureName, SqlParameter[] sqlParameters, SqlTransaction sqlTransaction = null)
{
SqlParameter param;
SqlCommand cmd = new SqlCommand(procedureName, sqlConnection);
if (sqlTransaction != null)
{
cmd.Transaction = sqlTransaction;
}
cmd.CommandType = CommandType.StoredProcedure;
// Add SQL Parameters (if any)
foreach (SqlParameter parameter in sqlParameters)
{
param = new SqlParameter(parameter.ParameterName, parameter.DbType);
param.Value = parameter.Value;
cmd.Parameters.Add(param);
}
return cmd;
}
private static string GetConnectionString()
{
return _connectionString;
}
public static void SetConnectionString(string connectionString)
{
_connectionString = connectionString;
}
}
}
namespace Data.Library
{
public class Metadata : General.DataObject
{
protected Data.Model.Metadata _metaData;
public Data.Model.Metadata BaseModel
{
get { return _metaData; }
set { _metaData = value; }
}
//Typically I have properties in here pointing to the Data.Model class
protected override void Initialize(System.Data.IDataRecord dataRow)
{
_metaData = new Model.Metadata();
_metaData.Id = Convert.ToInt32(dataRow["Id"].ToString());
_metaData.Title = (dataRow["Title"].ToString());
_metaData.Sku = (dataRow["Sku"].ToString());
_metaData.IsLive = Convert.ToBoolean(dataRow["IsLive"].ToString());
}
public static Metadata ReadByID(int id)
{
return General.DataObject.ReadObject<Metadata>("dbo.s_MetadataGet", new[] { new SqlParameter("#ID", id) },
typeof(Metadata));
}
public static Metadata[] ReadBySku(string sku)
{
List<Metadata> metaDatas = General.DataObject.ReadObjects<Metadata>("dbo.s_MetadataGetBySku", new[] { new SqlParameter("#Sku", sku) });
return metaDatas.ToArray();
}
}
}
namespace Data.Model
{
public class Metadata
{
public int Id { get; set; }
public string Title { get; set; }
public string Sku { get; set; }
public bool IsLive { get; set; }
}
}

How to load data from MySql database into list(Console-Based app)?

I have created a database with 1 table "emp" and have some data in it. Now every time i start the app, i want a list to fetch the data from db and save it in list because i want to perform some calculations like tax and Gross-Salary on data at runtime for display only(don't want to save it in db ). I have tried many times but i am unable to understand how this can be done. This is my code:
Main Class:
static void Main(string[] args)
{
empDB empDB1 = new empDB();
List<emplyee> empLST1 = new List<emplyee>();
if (empLST1 == null)
{
empDB1.loadLST(out empLST1);
}
}
empDB Class:
class empDB
{
private string ConnectionString = #"server=localhost;DATABASE=hris;uid=root;Password=123456;";
internal void loadLST(out List<emplyee> loadedLST)
{
string query = "select name, grade from emp";
try
{
MySqlConnection con = new MySqlConnection(ConnectionString);
con.Open();
MySqlDataReader rdr = null;
MySqlCommand cmd = new MySqlCommand(query, con);
rdr = cmd.ExecuteReader();
while(rdr.Read())
{
List<employee> returnedLst = new List<employee>();
returnedLst.Add(rdr["name"].ToString(), rdr["grade"].ToString());
}
loadedLst = returnedLst;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
I have no idea even if my approach is right or not. I have googled it a few times but i just started working in .net a few days ago so i don't understand how to do it.
Okay i tried this and it also dosn't work:
internal void GetDatabaseList()
{
List<employee> databases = new List<employee>();
MySqlConnection con = new MySqlConnection(ConnectionString);
{
con.Open();
DataTable tbl = con.GetSchema("Databases");
con.Close();
foreach (DataRow row in tbl.Rows)
{
databases.Add(row["hris"].ToString());
}
}
}
static void Main(string[] args)
{
empDB empDB1 = new empDB();
List<emplyee> empLST1 = new List<emplyee>();
**if (empLST1 == null)
{
empDB1.loadLST(out empLST1);
}**
}
this will always be false because you defined empLST1 as a new List, meaning its not null
try this
public class Employee
{
public string Name { get; set; }
public string Grade { get; set; }
}
static void Main(string[] args)
{
empDB empDB1 = new empDB();
List<Employee> empLST1 = new List<Employee>();
empDB1.loadLST(ref empLST1);
}
public class empDB
{
public void loadLst(ref List<Employee> loadedLST)
{
string query = "select name, grade from emp";
try
{
MySqlConnection con = new MySqlConnection(ConnectionString);
con.Open();
MySqlDataReader rdr = null;
MySqlCommand cmd = new MySqlCommand(query, con);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Employee emp = new Employee();
emp.Name = rdr["name"].ToString();
emp.Grade = rdr["grade"].ToString();
loadedLST.Add(emp);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Assuming, that that employee class looks like this:
class employee
{
public string Name { get; set; }
public string Grade { get; set; }
}
I'd rewrite loadLST like this:
internal List<employee> loadLST()
{
string query = "select name, grade from emp";
// we should dispose IDisposable implementations:
// connection, command and data reader
using (var con = new MySqlConnection(ConnectionString))
{
con.Open();
using (var cmd = new MySqlCommand(query, con))
using (var rdr = cmd.ExecuteReader())
{
// it is hard to maintain manual mapping
// between query results and objects;
// let's use helper like Automapper to make this easier
Mapper.CreateMap<IDataReader, employee>();
Mapper.AssertConfigurationIsValid();
return Mapper.Map<List<employee>>(rdr);
}
}
}
Improvements:
IDisposable implementations must be disposed explicitly (see this and this)
to avoid manual mapping code, which maps the result from data reader and object (employee instance in your case), the code uses Automapper package
exception handling and out parameter are thrown away. There's no need for exception handling and out parameter here, unless you're writing method like TryToDoSomething (and even in that case your method must return bool to indicate the state of operation, and catch only specific exceptions instead of Exception).
Also note, that your code doesn't match naming guidelines (e.g., employee should be Employee).

Types that own disposable fields should be disposable. how to solve this warning?

I tried using Run Code Analysis option in VisualStudio 2012, as a result of it I got a warning as
CA1001 Types that own disposable fields should be disposable
Implement IDisposable on 'DBConnectivity'
because it creates members of the following IDisposable types: 'SqlConnection', 'SqlCommand'.
I referred some question in SO, but I couldn't catch the point regarding IDisposable
and following is the class, responsible for this warning.
class DBConnectivity
{
public SqlConnection connection = null;
public SqlCommand command = null;
public SqlDataReader dataReader = null;
public string connectionString = null;
public List<MasterTableAttributes> masterTableList;
public DBConnectivity()
{
connectionString = ConfigurationManager.ConnectionStrings["Master"].ConnectionString;
connection = new SqlConnection(connectionString.ToString());
//-----Master table results
connection.Open();
string masterSelectQuery = "SELECT * FROM MASTER_TABLE";
command = new SqlCommand(masterSelectQuery, connection);
dataReader = command.ExecuteReader();
masterTableList = new List<MasterTableAttributes>();
while (dataReader.Read())
{
MasterTableAttributes masterTableAttribute = new MasterTableAttributes()
{
fileId = Convert.ToInt32(dataReader["Id"]),
fileName = Convert.ToString(dataReader["FileName"]),
frequency = Convert.ToString(dataReader["Frequency"]),
scheduledTime = Convert.ToString(dataReader["Scheduled_Time"])
};
masterTableList.Add(masterTableAttribute);
}
dataReader.Close();
connection.Close();
}
}
I am really confused in implementing the IDisposable. Any help appreciated?
I fully agree with the compiler - you need to dispose your fields here, or (as already noted) - not make them fields in the first place:
class DBConnectivity : IDisposable // caveat! read below first
{
public void Dispose() {
if(connection != null) { connection.Dispose(); connection = null; }
if(command != null) { command.Dispose(); command = null; }
if(dataReader != null) { dataReader.Dispose(); dataReader = null; }
}
Note that you would then use this type via using(...)
However! It looks like a static method would be more appropriate:
static class DBConnectivity
{
public static List<MasterTableAttributes> GetMasterTableList()
{
var connectionString = ConfigurationManager.ConnectionStrings["Master"].ConnectionString;
using(var connection = new SqlConnection(connectionString))
{
connection.Open();
const string masterSelectQuery = "SELECT * FROM MASTER_TABLE";
using(var command = new SqlCommand(masterSelectQuery, connection))
using(var dataReader = command.ExecuteReader())
{
var masterTableList = new List<MasterTableAttributes>();
while (dataReader.Read())
{
MasterTableAttributes masterTableAttribute = new MasterTableAttributes()
{
fileId = Convert.ToInt32(dataReader["Id"]),
fileName = Convert.ToString(dataReader["FileName"]),
frequency = Convert.ToString(dataReader["Frequency"]),
scheduledTime = Convert.ToString(dataReader["Scheduled_Time"])
};
masterTableList.Add(masterTableAttribute);
}
return masterTableList;
}
}
}
}
or perhaps simpler with a tool like "dapper":
static class DBConnectivity
{
public static List<MasterTableAttributes> GetMasterTableList()
{
var connectionString = ConfigurationManager.ConnectionStrings["Master"].ConnectionString;
using(var connection = new SqlConnection(connectionString))
{
connection.Open();
const string sql = "SELECT Id as [FileId], FileName, Frequency, Scheduled_Time as [ScheduledTime] FROM MASTER_TABLE";
return connection.Query<MasterTableAttributes>(sql).ToList();
}
}
}
If that is you complete class you should move all the SQL variables inside the constructor. Or perhaps change the constructor to a static function that return the masterTableList

C# abstraction and database layer

I am wondering what's a better way to abstract some of this code, into a simple DAL. At this time, I'm just patching the code and don't have time or need yet to use EF, Linq2Sql or any ORM right now.
public string GetMySpecId(string dataId)
{
using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId", conn))
{
// Now add the parameter to the parameter collection of the command specifying its type.
command.Parameters.Add(new SqlParameter("dataId", SqlDbType.Text));
command.Prepare();
// Now, add a value to it and later execute the command as usual.
command.Parameters[0].Value = dataId;
using (SqlDataReader dr = command.ExecuteReader())
{
while (dr.Read())
{
specId = dr[0].ToString();
}
}
}
}
return specId;
}
What's a good clean way to pull the connection, commands, and such out of the GetMySpecId() as I will have tons of these functions and don't want to write the using.... over and over again.
Well, you could write your own custom data-access helper that encapsulates all that stuff and returns a DataTable:
public string GetMySpecId(string dataId)
{
DataTable result = _dbHelper.ExecuteQuery(
#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
new SqlParameter("dataId", dataId);
return result.Rows[0][0].ToString();
}
Or if you are stuck on the idea of using a DataReader, you could pass a delegate to the helper, which gets invoked inside of the using statements:
public string GetMySpecId(string dataId)
{
return _dbHelper.ExecuteQuery(
dr =>
{
if(dr.Read())
{
return dr[0].ToString();
}
// do whatever makes sense here.
},
#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
new SqlParameter("dataId", dataId));
}
You could also use a lightweight tool like Dapper to simplify some of the syntax and take care of mapping to your data types. (You'd still need to deal with opening a connection and such.)
Update
Here's an example of how you could write the ExecuteQuery method used in the second example:
public T ExecuteQuery<T>(
Func<IDataReader, T> getResult,
string query,
params IDataParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach(var parameter in parameters)
{
command.Parameters.Add(parameter);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
You could use the yield return statement in order to keep the connection, command and reader objects inside using statements.
public class ScalarReader<T>
{
const string MyConnectionString = "...";
private string _returnColumn, _table, _whereCond;
private object[] _condParams;
public ScalarReader(string returnColumn, string table, string whereCond,
params object[] condParams)
{
_returnColumn = returnColumn;
_table = table;
_whereCond = whereCond;
_condParams = condParams;
}
public IEnumerator<T> GetEnumerator()
{
using (SqlConnection conn = new SqlConnection(MyConnectionString)) {
conn.Open();
string select = String.Format(#"SELECT ""{0}"" FROM ""{1}"" WHERE {2}",
_returnColumn, _table, _whereCond);
using (SqlCommand command = new SqlCommand(select, conn)) {
for (int p = 0; p < _condParams.Length; p++) {
command.Parameters.AddWithValue("#" + (p+1), _condParams[p]);
}
using (SqlDataReader dr = command.ExecuteReader()) {
while (dr.Read()) {
if (dr.IsDBNull(0)) {
yield return default(T);
} else {
yield return (T)dr[0];
}
}
}
}
}
}
}
You would call it like this
var reader = new ScalarReader<string>("specId", "MyTable", "dataId=#1", "x");
foreach (string id in reader) {
Console.WriteLine(id);
}
Note that I am using a convention for the parameter names. They are named #1, #2, #3 ....
var reader =
new ScalarReader<DateTime>("date", "MyTable", "num=#1 AND name=#2", 77, "joe");
You would need to return an IDataReader from the middle of your using statement and as soon as you do that, you will lose the connection and the data. You can't really do what you are after.
You could do something like this, sorry for no actual code, but it will give you the idea. Of course it will have to be careful converting object[] back into something useful, but you're sort of doing that already with specId = dr[0].ToString();
class MyDb
{
public MyDb()
{
}
public void Initialize()
{
// open the connection
}
public void Finalize()
{
// close the connection
}
public List<object[]> Query(string command, List<SqlParameter> params)
{
// prepare command
// execute reader
// read all values into List of object[], and return it
}
}
You can create a base abstract class that will have some base function with all the usings and base code like so:
public abstract class BaseClass
{
public abstract void myFunc(SqlConnection conn);
public void BaseFunc()
{
using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
{
conn.Open();
myFunc(conn);
..any other base implementation...
}
}
}
each derived class will inheret the BaseClass and implement the abstract MyFunc with the specific query and all the specific stuff for each derived class. You will call from outside the BaseFunc function of the base abstract class.

Categories

Resources