Refactoring techniques for data access layer - c#

I've got a data access layer to which I am binding some controls. Currently I have something along the lines of
public List<Race> GetRaces()
{
List<Race> raceList = new List<Race>();
using (var con = new SqlConnection(this.ConnectionString))
{
using (var cmd = new SqlCommand("spGetRace",con))
{
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Race r = new Race();
r.RaceId = Convert.ToInt32(rdr["raceId"]);
r.RaceDescription = rdr["RaceDescription"].ToString();
raceList.Add(r);
}
}
return raceList;
}
}
public List<Ses> GetSes()
{
List<Ses> sesList = new List<Ses>();
using (var con = new SqlConnection(this.ConnectionString))
{
using (var cmd = new SqlCommand("spGetSes",con))
{
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Ses s = new Ses();
s.SesId = Convert.ToInt32(rdr["SesId"]);
s.SesDescription = rdr["SesDescription"].ToString();
sesList.Add(s);
}
}
return sesList;
}
}
which will be bound to drop down lists in my presentation layer. Instead of having to type the lion's share of this ADO.NET code over and over, what are some useful refactoring techniques for this basic type of data access? Can I do this by refactoring with a SqlConnection, SqlCommand, and one of my custom types Race/Ses as a parameter?

public enum SqlCommandNames
{
spGetRace,
spGetSes ,
spGetOthers
}
public class myobj{
public int id {get;set;}
public string description {get;set}
}
public List<myobj> GetObj(SqlCommandNames sqlcmd)
{
List<myobj> objList = new List<myobj>();
using (var con = new SqlConnection(this.ConnectionString))
{
using (var cmd = new SqlCommand(sqlcmd.ToString(),con))
{
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
myobj r = new myobj();
r.id = = reader.GetInt32(0);
r.description = reader.IsDBNull(1) ? "" : reader.GetString(1);
objList.Add(r);
}
}
return objList;
}
}
Additional advice will be to cache lists
List<myobj> objList = (List<myobj>)HttpContext.Current.Cache[sqlcmd.ToString()];
if (objList == null)
{
List<myobj> objList = new List<myobj>();
HttpContext.Current.Cache.Insert(sqlcmd.ToString(), objList);
....
....
}
//and caching all on start up
protected void Application_Start()
{
foreach (SqlCommandNames x in Enum.GetValues(typeof(SqlCommandNames)))
{
GetObj(x);
}
}

Related

C# SqlDataReader keeps reading

I have a method for getting data out of my SQL Server database. I'm using a reader to get all the data. The problem is that the reader keeps reading and the application doesn't pop up. It is like an infinite loop or something.
public static List<Reservering> GetReserverings()
{
Reservering res = new Reservering();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
}
reader.Close();
}
return GetReserverings();
}
Does anyone know how to fix this problem?
You've got an infinite recursion by calling the method itself at the end:
return GetReserverings();
You could've discovered this by setting a breakpoint in your method and stepping through your code.
You want to return a list of reservations instead:
var result = new List<Reservering>();
// your code...
return result;
And within your while() loop, you'd want to instantiate a new Reservering each iteration, and Add it to the result list.
You should create an instance of Reservering for each record read and store these instances in the List<Reservering>:
public static List<Reservering> GetReserverings() {
List<Reservering> result = new List<Reservering>();
using (var conn = new SqlConnection(ConnectionString)) {
conn.Open();
const string query =
#"select b.boekingid,
k.naam,
bk.incheckdatum,
bk.uitcheckdatum,
b.hotelid,
b.aantal_gasten
from boeking b join
klant k on k.klantid = b.boekingid join
boekingkamer bk on b.boekingid = bk.boekingid
where bk.incheckdatum is not null
and bk.uitcheckdatum is not null";
using (SqlCommand selectReserveringen = new SqlCommand(query, conn)) {
using (SqlDataReader reader = selectReserveringen.ExecuteReader()) {
while (reader.Read()) {
Reservering res = new Reservering();
result.Add(res);
res.Id = Convert.ToInt32(reader["boekingid"]);
res.Naam = Convert.ToString(reader["naam"]);
res.Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]);
res.Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]);
res.Hotel = Convert.ToInt32(reader["hotelid"]);
res.Aantal_personen = Convert.ToInt32(reader["aantal_gasten"]);
}
}
}
}
return result;
}
Edit: You can try to simplify while loop with a help of object initializing:
...
while (reader.Read()) {
result.Add(new Reservering() {
Id = Convert.ToInt32(reader["boekingid"]),
Naam = Convert.ToString(reader["naam"]),
Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]),
Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]),
Hotel = Convert.ToInt32(reader["hotelid"]),
Aantal_personen = Convert.ToInt32(reader["aantal_gasten"])
});
}
public static List<Reservering> GetReserverings()
{
List<Reserving> reservings = new List<Reservings>();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
Reservering res = new Reservering();
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
reservings.add(res);
}
reader.Close();
}
return reservings;
}

Calling a Method to a List

So i need to pass a list from this method:
[WebMethod]
public List<SLA_ClassLibrary.SLA_Class.ReadAtm> ReadAtm()
{
var AtmReadList = new List<SLA_ClassLibrary.SLA_Class.ReadAtm>();
using (SqlConnection Conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SLAdb"].ConnectionString))
{
using (Conn)
{
Conn.Open();
string SQLReadAtm = "Select * from ATM;";
SqlCommand ReadAtmCmd = new SqlCommand(SQLReadAtm, Conn);
SqlDataReader reader = ReadAtmCmd.ExecuteReader();
while (reader.Read())
{
AtmReadList.Add(new SLA_ClassLibrary.SLA_Class.ReadAtm
{
idatm = reader.GetInt32(reader.GetOrdinal("IDATM")),
District_Region = reader.GetString(reader.GetOrdinal("District_Region")),
Local = reader.GetString(reader.GetOrdinal("Local")),
IsUp = reader.GetBoolean(reader.GetOrdinal("IsUp"))
});
}
}
return AtmReadList;
}
}
to a website table in MVC, but i'm having problems on how to go about doing this.
I can't pass the "return AtmReadList" to a List<> so i have no idea on how to do this.
Any way to do this?

get all row and column data using SELECT - C#

I'm trying to get all data from an SQL table and store it in a List using the C# programming language.
the SQL statement I'm using is:
private string cmdShowEmployees = "SELECT * FROM Employees;";
This is being used in the same class as a function
public List<string> showAllIdData()
{
List<string> id = new List<string>();
using (sqlConnection = getSqlConnection())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandText = cmdShowEmployees;
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read()) {
id.Add(reader[0].ToString());
}
return id;
}
}
and here
public List<string> showAllActiveData()
{
List<string> active = new List<string>();
using (sqlConnection = getSqlConnection())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandText = cmdShowEmployees;
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read()) {
active.Add(reader[1].ToString());
}
return active;
}
I would have to create 9 more functions this way in order to get all the data out of the Employees table. This seems very inefficient and I was wondering if there was a more elegant way to do this.
I know using an adapter is one way to do it but I don't think it is possible to convert a filled adapter to a list, list list etc.
SqlDataAdapter adapter = sqlDataCollection.getAdapter();
DataSet dataset = new DataSet();
adapter.Fill(dataset, "idEmployees");
dataGridView1.DataSource = dataset;
dataGridView1.DataMember = "idEmployees";
Any ideas?
If you must use the reader in this way, why not create an object which holds the table row data.
public class SomeComplexItem
{
public string SomeColumnValue { get; set;}
public string SomeColumnValue2 { get; set;}
public string SomeColumnValue3 { get; set;}
public string SomeColumnValue4 { get; set;}
}
That way you can loop through with your reader as follows:
public List<SomeComplexItem> showAllActiveData()
{
List<SomeComplexItem> active = new List<SomeComplexItem>();
using (sqlConnection = getSqlConnection())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandText = cmdShowEmployees;
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
var someComplexItem = new SomeComplexItem();
someComplexItem.SomeColumnValue = reader[1].ToString();
someComplexItem.SomeColumnValue2 = reader[2].ToString();
someComplexItem.SomeColumnValue3 = reader[3].ToString();
active.Add(someComplexItem);
}
return active;
}
You could use two select statements to populate two List<string> as shown in the example below where the key between reads is reader.NextResult();.
The database used is the standard Microsoft NorthWind database.
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
namespace SQL_Server_TwoList
{
public class DataOperations
{
public List<string> Titles { get; set; }
public List<string> Names { get; set; }
/// <summary>
/// Trigger code to load two list above
/// </summary>
public DataOperations()
{
Titles = new List<string>();
Names = new List<string>();
}
public bool LoadData()
{
try
{
using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
string commandText = #"
SELECT [TitleOfCourtesy] + ' ' + [LastName] + ' ' + [FirstName] As FullName FROM [NORTHWND.MDF].[dbo].[Employees];
SELECT DISTINCT [Title] FROM [NORTHWND.MDF].[dbo].[Employees];";
using (SqlCommand cmd = new SqlCommand(commandText, cn))
{
cn.Open();
SqlDataReader reader = cmd.ExecuteReader();
// get results into first list from first select
if (reader.HasRows)
{
while (reader.Read())
{
Names.Add(reader.GetString(0));
}
// move on to second select
reader.NextResult();
// get results into first list from first select
if (reader.HasRows)
{
while (reader.Read())
{
Titles.Add(reader.GetString(0));
}
}
}
}
}
return true;
}
catch (Exception)
{
return false;
}
}
}
}
Form code
namespace SQL_Server_TwoList
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DataOperations dataOps = new DataOperations();
if (dataOps.LoadData())
{
listBox1.DataSource = dataOps.Names;
listBox2.DataSource = dataOps.Titles;
}
}
}
}
You could always add it all to a dataset or datatable instead of looping through using datareader to add to an array, dataset allows you to access data in similar way to array anyway.
Connstr = "Data Source = " + SelectedIP + "; Initial Catalog = " + dbName + "; User ID = " + txtUsername.Text +"; Password = "+ txtPassword.Text +"";
conn = new SqlConnection(Connstr);
try
{
string contents = "SELECT * FROM ..."
conn.Open();
SqlDataAdapter da_1 = new SqlDataAdapter(contents, conn); //create command using contents of sql file
da_1.SelectCommand.CommandTimeout = 120; //set timeout in seconds
DataSet ds_1 = new DataSet(); //create dataset to hold any errors that are rturned from the database
try
{
//manipulate database
da_1.Fill(ds_1);
if (ds_1.Tables[0].Rows.Count > 0) //loop through all rows of dataset
{
for (int i = 0; i < ds_1.Tables[0].Rows.Count; i++)
{
//rows[rownumber][column number/ "columnName"]
Console.Write(ds_1.Tables[0].Rows[i][0].ToString() + " ");
}
}
}
catch(Exception err)
{}
conn.Close();
}
catch(Exception ex)
{}

Generic data access code to stored procedures to return Enumerable<t>?

Instead of repeating the same ADO.net code with a different Enumerable, I want to make it more generic and reusable.
I have the following ADO.Net code to return a collection of objects:
public static IEnumerable<TasCriteria> GetTasCriterias()
{
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = "IVOOARINVENTORY_GET_TASCRITERIA";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
var types = (from c in dataset.Tables[0].AsEnumerable()
select new TasCriteria()
{
TasCriteriaId = Convert.ToInt32(c["TasCriteriaId"]),
TasCriteriaDesc= c["CriteriaDesc"].ToString()
}).ToList<TasCriteria>();
return types;
}
}
Model:
public class TasCriteria
{
public int TasCriteriaId { get; set; }
public string TasCriteriaDesc { get; set; }
}
If your stored procedure returns column names matching exactly properties in your class, you can use reflection like this code. I'll leave an exercise to add parameters to the stored procedure for someone else. (by the way, this is untested code, I wrote it off the top of my head)
public static IEnumerable<T> GetStoredProcedure<T>(string procedure) where T : new()
{
var data = new List<T>();
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = procedure;
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
//Get each row in the datatable
foreach (DataRow row in dataset.Tables[0].Rows)
{
//Create a new instance of the specified class
var newT = new T();
//Iterate each column
foreach (DataColumn col in dataset.Tables[0].Columns)
{
//Get the property to set
var property = newT.GetType().GetProperty(col.ColumnName);
//Set the value
property.SetValue(newT, row[col.ColumnName]);
}
//Add it to the list
data.Add(newT);
}
return data;
}
}
So lets say you have a class like this:
public class TasCriteria
{
public int TasCriteriaId { get; set; }
public string TasCriteriaDesc { get; set; }
}
You would call the function like this:
IEnumerable<TasCriteria> criteria = GetStoredProcedure<TasCriteria>("IVOOARINVENTORY_GET_TASCRITERIA");
An option would be to extract your specific code outside of your main method.
In example:
public static IEnumerable<T> GetCriterias(
string storedProcedureName,
Func<IEnumerable<T>> enumerateMethod)
{
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = storedProcedureName;
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
return enumerateMethod(dataset.Tables[0]);
}
}
If your stored procedure requires parameters, you can overload your function as:
public static IEnumerable<T> GetCriterias(
string storedProcedureName,
Func<IEnumerable<T>> enumerateMethod,
SqlParameter[] parameters)
Using this code, you can define methods that matches de Func<> signature.
public static IEnumerable<TasCriteria> EnumerateTasCriteria(DataTable table)
{
return
(from c in table.AsEnumerable()
select new TasCriteria()
{
TasCriteriaId = Convert.ToInt32(c["TasCriteriaId"]),
TasCriteriaDesc= c["CriteriaDesc"].ToString()
}).ToList<TasCriteria>();
}
public static IEnumerable<DetailCriteria> EnumerateDetailCriteriaCriteria(
DataTable table)
{
return
(from c in table.AsEnumerable()
select new DetailCriteria()
{
DetailCriteriaId = Convert.ToInt32(c["DetailCriteriaId"]),
DetailCriteriaDesc = c["CriteriaDesc"].ToString()
}).ToList<TasCriteria>();
}
Then, you can call your code as:
IEnumerable<TasCriteria> task =
GetCriterias<TasCriteria>("IVOOARINVENTORY_GET_TASCRITERIA", EnumerateTasCriteria);
IEnumerable<DetailCriteria> details =
GetCriterias<DetailCriteria>("IVOOARINVENTORY_GET_DETAILSCRITERIA", EnumerateDetailCriteriaCriteria);

Reading data from sitecore webforms for marketeers database

I want to build a custom interface (a separate aspx page) to manage the data that is put into the webforms for marketeers (WFFM) database, and that for just one form. It must be possible to edit the data and select records with particular sortings and pagings. The database is configured to be SQLite.
Is this possible and recommended, or is it just plain xml that is saved into the WFFM database? And how should I go about it?
This is completely doable, though the select query to get data out of WFFM is a bit funky because everything is stored loose in one huge table called "field" with only a trail of GUIDs to tie the stored values back to what form they came from and what field.
Provided below is part of an Export to Excel utility I wrote for WFFM data. It builds a DataTable object from submitted form results. You could adapt it to some other structure without much work though.
public string connectionStringWFFM = "user id=sitecore_admin;password=xxx;Data Source=SitecoreDBServer.com;Database=Sitecore_WebForms";
protected DataTable BuildDataTable(Item formItem)
{
List<FormResult> formResults = FormResults(formItem.ID.Guid);
List<Field> distinctFields = DistinctFields(formItem.ID.Guid);
var dt = new DataTable();
dt.Columns.Add("Submission_DateTime", typeof (string));
foreach (Field field in distinctFields)
{
var dataColumn = new DataColumn("_" + field.id.ToString("N"), typeof (string));
dataColumn.Caption = field.name.Replace(" ", "_");
dt.Columns.Add(dataColumn);
}
foreach (FormResult formResult in formResults)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select fieldid, value from field where formid=#formid order by fieldid";
command.Parameters.Add("#formid", SqlDbType.UniqueIdentifier).Value = formResult.id;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
DataRow dataRow = dt.NewRow();
dataRow["Submission_DateTime"] = formResult.timestamp.ToString("MM/dd/yyyy HH:mm:ss");
while (reader.Read())
{
dataRow["_" + reader.GetGuid(0).ToString("N")] = reader.GetValue(1).ToString().Replace("<item>", "").Replace("</item>", "");
}
dt.Rows.Add(dataRow);
reader.Close();
connection.Close();
}
return dt;
}
public List<Field> DistinctFields(Guid formitemid)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select distinct fieldid from field where formid in (select id from form where formitemid=#formitemid) order by fieldid";
command.Parameters.Add("#formitemid", SqlDbType.UniqueIdentifier).Value = formitemid;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
var results = new List<Field>();
int count = 0;
while (reader.Read())
{
var field = new Field();
field.id = reader.GetGuid(0);
Database database = Factory.GetDatabase("master");
Item i = database.GetItem(new ID(field.id));
if (i != null && i.DisplayName != null)
{
field.name = i.DisplayName;
}
else
{
field.name = "Field" + count;
}
results.Add(field);
count += 1;
}
reader.Close();
connection.Close();
return results;
}
public List<FormResult> FormResults(Guid formitemid)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select id, timestamp from form where formitemid=#formitemid";
command.Parameters.Add("#formitemid", SqlDbType.UniqueIdentifier).Value = formitemid;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
var results = new List<FormResult>();
while (reader.Read())
{
var result = new FormResult();
result.id = reader.GetGuid(0);
result.timestamp = reader.GetDateTime(1);
results.Add(result);
}
reader.Close();
connection.Close();
return results;
}
public class FormResult
{
public Guid id { get; set; }
public DateTime timestamp { get; set; }
}
public class Field
{
public Guid id { get; set; }
public string name { get; set; }
}

Categories

Resources