Can I consolidate this in a generic method? - c#

I have a DataAccessBase class with the following 2 data access methods. One for ExecuteScalar and one for ExecuteNonQuery. Is it possible to consolidate it into one generic method or is even worth worrying about?
protected static int ExecuteNonQuery(SqlCommand command)
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
command.Connection = connection;
SqlDataAdapter da = new SqlDataAdapter(command);
command.Connection.Open();
int result = command.ExecuteNonQuery();
return result;
}
}
protected static string ExecuteScalar(SqlCommand command)
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
command.Connection = connection;
SqlDataAdapter da = new SqlDataAdapter(command);
command.Connection.Open();
string result = command.ExecuteScalar().ToString();
return result;
}
}
private static DataTable GetDT(int id)
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
string query = "select id, userid, name from tasks where id = #id";
SqlCommand command = new SqlCommand(query, connection);
SqlDataAdapter da = new SqlDataAdapter(command);
//Parameterized query to prevent injection attacks
command.Parameters.AddWithValue("id", id);
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
}

You can definitely avoid the current repetition you've got with a generic method, but I wouldn't try to reduce it to a single method. Here's what I'd potentially do:
protected static int ExecuteNonQuery(SqlCommand command) =>
ExecuteCommand(command, cmd => cmd.ExecuteNonQuery());
protected static string ExecuteScalar(SqlCommand command) =>
ExecuteCommand(command, cmd => cmd.ExecuteScalar().ToString());
private static T ExecuteCommand<T>(SqlCommand command, Func<SqlCommand, T> resultRetriever)
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
command.Connection = connection;
command.Connection.Open();
return resultRetriver(command);
}
}
For the DataTable one, following the same pattern you'd create the command first:
protected static DataTable GetDataTable(SqlCommand command) =>
ExecuteCommand(cmd =>
{
SqlDataAdapter da = new SqlDataAdapter(cmd)
DataTable table = new DataTable();
da.FillTable(table);
return table;
});

You can convert the ExecuteScalar to a generic method, allowing you to change the return type.
public T ExecuteScalar<T>(SqlCommand command)
{
using (SqlConnection connection = new SqlConnection(_connStr))
{
command.Connection = connection;
//SqlDataAdapter da = new SqlDataAdapter(command); //not needed...
command.Connection.Open();
var result = command.ExecuteScalar();
//rather than just returning result with an implicit cast, use Max's trick from here: https://stackoverflow.com/a/2976427/361842
if (Convert.IsDbNull(result))
return default(T); //handle the scenario where the returned value is null, but the type is not nullable (or remove this to have such scenarios throw an exception)
if (result is T)
return (T)result;
else
(T)Convert.ChangeType(result, typeof(T));
}
}
The logic is different in this method to in the ExecuteNonQuery function though, so you cannot have both represented by the same method.
Update
Regarding your question about the data table, I've taken and adapted #JonSkeet's answer to allow the class to also handle data tables:
public class SqlDatabaseThing //: ISqlDatabaseThing
{
// ... additional code here ... //
public int ExecuteNonQuery(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteNonQuery(_connStr, command, sqlParameters);
public static int ExecuteNonQuery(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteCommand(connectionString, command, cmd => cmd.ExecuteNonQuery());
public T ExecuteScalar(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteScalar(_connStr, command, sqlParameters);
public static T ExecuteScalar(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteCommand(connectionString, command, cmd => ConvertSqlCommandResult(cmd.ExecuteScalar()));
public DataTable ExecuteToDataTable(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteToDataTable(_connStr, command, sqlParameters);
public static DataTable ExecuteToDataTable(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
ExecuteCommand(connectionString, command, cmd => PopulateDataTable(cmd));
private static T ExecuteCommand<T>(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters, Func<SqlCommand, T> resultRetriever)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
command.Parameters.AddRange(sqlParameters);
command.Connection = connection;
command.Connection.Open();
return resultRetriver(command);
}
}
private static DataTable PopulateDataTable(SqlCommand command)
{
var da = SqlDataAdapter(command);
var dt = new DataTable();
da.Fill(dt);
return dt;
}
private static T ConvertSqlCommandResult(object result)
{
if (Convert.IsDbNull(result))
return default(T);
if (result is T)
return result as T;
(T)Convert.ChangeType(result, typeof(T));
}
}
NB: In your code you'd included logic related to getting specific tasks. That should be kept separate from your generic database logic (i.e. as presumably you'll want to return data tables for various queries, and don't want to have to rewrite your GetDT code each time). As such I've provided additional sample code below showing how you could separate that logic into another class...
public class TaskRepository //: IRepository<Task>
{
ISqlDatabaseThing db;
public TaskRepository(ISqlDatabaseThing db)
{
this.db = db;
}
readonly string GetByIdCommand = "select id, userid, name from tasks where id = #id";
readonly string GetByIdCommandParameterId = "#id"
readonly SqlDbType GetByIdCommandParameterIdType = SqlDbType.BigInt;
public Task GetById(long id)
{
var command = new SqlCommand(GetByIdCommand);
var parameters = IEnumerableHelper.ToEnumerable<SqlParameter>(new SqlParameter(GetByIdCommandIdParameter, GetByIdCommandIdParameterType, id));
var dataTable = db.ExecuteToDataTable(command, parameters);
return DataTableToTask(dataTable)[0];
}
private IEnumerable<Task> DataTableToTask(DataTable dt)
{
foreach (var row in dt.Rows)
{
yield return DataRowToTask(row);
}
}
private Task DataRowToTask (DataRow dr)
{
return new Task()
{
Id = dr["Id"]
,Name = dr["Name"]
,UserId = dr["UserId"]
};
}
}
public static class IEnumerableHelper
{
public static IEnumerable<T> ToEnumerable<T>(params T[] parameters)
{
return parameters;
}
}
NB: This code is untested; any issues please let me know.

Related

c# Reducing the number of methods used to get data from a DB

I have a Class that processes DB column metadata. One of the Properties of the Class is the table in question. This is passed to the object via the constructor. Also in the constructor I apply some logic to assign other variables within the class. To do this there are a number of private methods that connect to a DB, query something about the table, and return a value to the variable.
My problem is that I have a lot of different methods doing pretty much the same thing, but returning a different datatype. So for example my code is something like this
public Column(string tableName)
{
strTableName = tableName;
pkColumnName = GetPKColumnName(tableName);
pkColumnLenght = GetPKColumnLenght(tableName);
}
private string GetPKColumnName(string tableName)
{
string query = String.Format("SELECT myColName FROM myTable where myTableName = {0}", tableName);
string result = "";
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (string)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
private int GetPKColumnLenght(string tableName)
{
string query = String.Format("SELECT myColLenght FROM myTable where myTableName = {0}", tableName);
int result = 0;
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (int)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
There are many other methods like this also. This didn't look great to me, so I was wondering what the best practice was for something like this.
Should I just declare the return type as an object and do the datatype conversions when assigning the returned value to my variable?
My answer and the other assume different questions. It appears to me that you are trying to query a single value from a specific column and that you have to create a new method because the types are different. That said, I would personally just use a simple ORM solution and the other answer certainly is not wrong, just another abstraction.
You will want to use generics and cast to the generic.
I haven't tested this code, it is more of a guideline.
private T GetValue<T>(string tableName, colName)
{
string query = String.Format("SELECT {0} FROM myTable where myTableName = {1}", colName, tableName);
T result = default(T);
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (T)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
Create SqlManager class
public class SqlManager
{
public static string ConnectionString
{
get
{
return "Your ConnectionString"
}
}
public static SqlConnection GetSqlConnection(SqlCommand cmd)
{
if (cmd.Connection == null)
{
SqlConnection conn = new SqlConnection(ConnectionString);
conn.Open();
cmd.Connection = conn;
return conn;
}
return cmd.Connection;
}
public static object ExecuteScalar(SqlCommand cmd)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
return cmd.ExecuteScalar();
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
}
Now your methods, for the second one same thing:
private string GetPKColumnName(string tableName)
{
string query = String.Format("", tableName);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = #"SELECT myColName FROM myTable where myTableName = #TableName";
cmd.Parameters.AddWithValue("#TableName", tableName);
object result = SqlManager.ExecuteScalar(cmd);
return result != null ? (int)object: 0;
}

Why does the DataTableReader lose its data when using MySqlDataAdapter?

I have the following method:
public DataTableReader Get<T>(string sql, T[] parameters, CommandType commandType = CommandType.Text)
{
DataTableReader result;
using (var connection = new MySqlConnection(_connectionString))
{
using (var command = new MySqlCommand())
{
command.CommandType = commandType;
command.CommandText = sql;
command.Connection = connection;
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
using (var adapter = new MySqlDataAdapter())
{
adapter.SelectCommand = command;
using (var dataTable = new DataTable())
{
adapter.Fill(dataTable);
result = dataTable.CreateDataReader();
} // result contains data here
} // result "loses" data here
}
}
return result;
}
However, when I get to the row that I've commented about losing the data, the DataTableReader is empty. If I put a breakpoint in the row above it (where I've commented that the result contains the data) result does indeed contain the results.
What's going on? It's almost like something is being passed as a reference.
Edit: I should probably note that I've got the same code to connect to a SQL Server where any instance of a MySQL specific class above is replaced by SqlCommand,SqlDataAdapter instead. This all seems to work.
As requested, here is the corresponding method for MSSql:
public virtual DataTableReader Get(string sql, SqlParameter[] parameters, CommandType commandType = CommandType.Text)
{
DataTableReader result;
using (var sqlConnection = new SqlConnection(_coreConnectionString))
{
using (var myCommand = new SqlCommand())
{
myCommand.CommandType = commandType;
myCommand.CommandText = sql;
myCommand.Connection = sqlConnection;
if (parameters != null)
{
myCommand.Parameters.AddRange(parameters);
}
using (var myAdapter = new SqlDataAdapter())
{
myAdapter.SelectCommand = myCommand;
using (var myDataTable = new DataTable())
{
myAdapter.Fill(myDataTable);
result = myDataTable.CreateDataReader();
}
}
}
}
return result;
}
I've updated the top method to return the DataReader a bit sooner:
public DataTableReader Get<T>(string sql, T[] parameters, CommandType commandType = CommandType.Text)
{
using (var connection = new MySqlConnection(_connectionString))
{
using (var command = new MySqlCommand())
{
command.CommandType = commandType;
command.CommandText = sql;
command.Connection = connection;
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
using (var adapter = new MySqlDataAdapter())
{
adapter.SelectCommand = command;
using (var dataTable = new DataTable())
{
adapter.Fill(dataTable);
var reader = dataTable.CreateDataReader();
return reader;
}
}
}
}
}
I've left the assignment of the variable on a different line to the return statement so I can verify that reader is being filled. However, when it gets returned to the calling method, there's nothing there.
The code that is calling Get() is var dt = _dbHelper.Get(sql, parameters) and I know that the sql works because before it is returned, there is the data I am expecting in the object.
More Edits:I've been doing some more digging as this is really annoying me. Firstly, if I use var reader = dataTable.Copy().CreateDataReader(); the data is returned. But of course then this creates a new version of the object, so this might cause a memory issue.
Secondly (and this really annoys me) the Fill() method that MySqlDataAdapter uses isn't its own implementation. It uses the implementation from DbDataAdapter (which both it and SqlDataAdapter inherits from). So how come it doesn't work the same?
In this line:
using (var myDataTable = new DataTable())
{
myAdapter.Fill(myDataTable);
result = myDataTable.CreateDataReader();
}
with the using you are doing that when the code goes out from the using, the DataTable is disposed and then yo uwill get empty data.
Try declaring the DataTable and closing it at the end. Maybe using a try/catch/finally block is a good way to do this, and in the finally you can add the dispose.
I hope this helps

Dynamically choose which class to use within function

Is there a way to choose which class to use in a function based on how it is called? My question is best explained by code samples:
I have 2 separate files for dealing with each type of database (SQL, Access)
Access:
public static DataTable Select(string connString, string query, Dictionary<string, object> Parameters = null)
{
DataTable dt = new DataTable();
//Create Query
using (OleDbConnection conn = new OleDbConnection(connString))
using (OleDbCommand cmd = new OleDbCommand(query, conn))
using (OleDbDataAdapter da = new OleDbDataAdapter(cmd))
{
//Add Parameters
if (Parameters != null)
{
foreach (KeyValuePair<string, object> kvp in Parameters)
{
cmd.Parameters.AddWithValue(kvp.Key, kvp.Value);
}
}
//Execute Query
conn.Open();
da.Fill(dt);
return dt;
}
}
SQL:
public static DataTable Select(string connString, string query, Dictionary<string, object> Parameters = null)
{
DataTable dt = new DataTable();
//Create Query
using (SqlConnection conn = new SqlConnection(connString))
using (SqlCommand cmd = new SqlCommand(query, conn))
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
//Add Parameters
if (Parameters != null)
{
foreach (KeyValuePair<string, object> kvp in Parameters)
{
cmd.Parameters.AddWithValue(kvp.Key, kvp.Value);
}
}
//Execute Query
conn.Open();
da.Fill(dt);
return dt;
}
}
Notice the only difference is the type of the Connection, Command and Adapter.
Is there a way to merge these two functions so I can pass a parameter to specify which type to use?
What you ASKED for is called Double Dispatch. You don't need double dispatch.
public static DataTable Select(DbProviderFactory factory, string connString, string query, Dictionary<string, object> Parameters = null)
{
DataTable dt = new DataTable();
//Create Query
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = connectionString;
using(DbCommand cmd = conn.CreateCommand())
using(DbDataAdapter da = factory.CreateDataAdapter())
{
cmd.CommandText = query;
da.SelectCommand = cmd;
if (Parameters != null)
{
foreach (KeyValuePair<string, object> kvp in Parameters)
{
DbParameter parameter = cmd.createParameter();
parameter.ParameterName = kvp.Key;
parameter.Value = kvp.Value;
cmd.Parameters.Add(parameter);
}
}
conn.Open();
da.Fill(dt);
return dt;
}
}
}
To answer your question, you could create an enumeration of database types, pass it into Select, and then inside Select you could switch on the value, where each case contains the corresponding code.
(However, this looks like bad design to me, since this looks like a good opportunity to use polymorphism, at least some of which you should already get through ADO.NET. You could fill the gaps yourself.)
You may want to look into the DbProviderFactory approach
http://msdn.microsoft.com/en-us/library/wda6c36e(v=vs.110).aspx

Return Result from Select Query in stored procedure to a List

I'm writing a stored procedure that currently contains only a SELECT query. It will be expanded to do a number of other things, which is why it has to be a stored procedure, but for now, it is a simple query.
Something like this:
SELECT name, occupation, position
FROM jobs
WHERE ...
I'm looking to return the results of this query to be used in C#. I want to add it to a list so that I can bind it to a GridView component.
I don't know how to go about this, though. If I have to insert it into a list after returning all selected data, then that's alright, I just need to know how to properly return the data so that I can do that.
If I can return it in a format that can be popped right into a list, though, that would be ideal.
In stored procedure, you just need to write the select query like the below:
CREATE PROCEDURE TestProcedure
AS
BEGIN
SELECT ID, Name
FROM Test
END
On C# side, you can access using Reader, datatable, adapter.
Using adapter has just explained by Susanna Floora.
Using Reader:
SqlConnection connection = new SqlConnection(ConnectionString);
command = new SqlCommand("TestProcedure", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
List<Test> TestList = new List<Test>();
Test test = null;
while (reader.Read())
{
test = new Test();
test.ID = int.Parse(reader["ID"].ToString());
test.Name = reader["Name"].ToString();
TestList.Add(test);
}
gvGrid.DataSource = TestList;
gvGrid.DataBind();
Using dataTable:
SqlConnection connection = new SqlConnection(ConnectionString);
command = new SqlCommand("TestProcedure", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
DataTable dt = new DataTable();
dt.Load(command.ExecuteReader());
gvGrid.DataSource = dt;
gvGrid.DataBind();
I hope it will help you. :)
SqlConnection connection = new SqlConnection(ConnectionString);
command = new SqlCommand("TestProcedure", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
DataTable dt = new DataTable();
dt.Load(command.ExecuteReader());
gvGrid.DataSource = dt;
gvGrid.DataBind();
SqlConnection con = new SqlConnection("Data Source=DShp;Initial Catalog=abc;Integrated Security=True");
SqlDataAdapter da = new SqlDataAdapter("data", con);
da.SelectCommand.CommandType= CommandType.StoredProcedure;
DataSet ds=new DataSet();
da.Fill(ds, "data");
GridView1.DataSource = ds.Tables["data"];
GridView1.DataBind();
Passing Parameters in Stored Procedure and calling it in C# Code behind as shown below?
SqlConnection conn = new SqlConnection(func.internalConnection);
var cmd = new SqlCommand("usp_CustomerPortalOrderDetails", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add("#CustomerId", SqlDbType.Int).Value = customerId;
cmd.Parameters.Add("#Qid", SqlDbType.VarChar).Value = qid;
conn.Open();
// Populate Production Panels
DataTable listCustomerJobDetails = new DataTable();
listCustomerJobDetails.Load(cmd.ExecuteReader());
conn.Close();
I had the same question, took me ages to find a simple solution.
Using ASP.NET MVC 5 and EF 6:
When you add a stored procedure to your .edmx model, the result of the stored procedure will be delivered via an auto-generated object called yourStoredProcName_result.
This _result object contains the attributes corresponding to the columns in the database that your stored procedure selected.
The _result class can be simply converted to a list:
yourStoredProcName_result.ToList()
// GET: api/GetStudent
public Response Get() {
return StoredProcedure.GetStudent();
}
public static Response GetStudent() {
using (var db = new dal()) {
var student = db.Database.SqlQuery<GetStudentVm>("GetStudent").ToList();
return new Response {
Sucess = true,
Message = student.Count() + " Student found",
Data = student
};
}
}
Building on some of the responds here, i'd like to add an alternative way. Creating a generic method using reflection, that can map any Stored Procedure response to a List. That is, a List of any type you wish, as long as the given type contains similarly named members to the Stored Procedure columns in the response.
Ideally, i'd probably use Dapper for this - but here goes:
private static SqlConnection getConnectionString() // Should be gotten from config in secure storage.
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "it.hurts.when.IP";
builder.UserID = "someDBUser";
builder.Password = "someDBPassword";
builder.InitialCatalog = "someDB";
return new SqlConnection(builder.ConnectionString);
}
public static List<T> ExecuteSP<T>(string SPName, List<SqlParameter> Params)
{
try
{
DataTable dataTable = new DataTable();
using (SqlConnection Connection = getConnectionString())
{
// Open connection
Connection.Open();
// Create command from params / SP
SqlCommand cmd = new SqlCommand(SPName, Connection);
// Add parameters
cmd.Parameters.AddRange(Params.ToArray());
cmd.CommandType = CommandType.StoredProcedure;
// Make datatable for conversion
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dataTable);
da.Dispose();
// Close connection
Connection.Close();
}
// Convert to list of T
var retVal = ConvertToList<T>(dataTable);
return retVal;
}
catch (SqlException e)
{
Console.WriteLine("ConvertToList Exception: " + e.ToString());
return new List<T>();
}
}
/// <summary>
/// Converts datatable to List<someType> if possible.
/// </summary>
public static List<T> ConvertToList<T>(DataTable dt)
{
try // Necesarry unfotunately.
{
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var properties = typeof(T).GetProperties();
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
if (columnNames.Contains(pro.Name))
{
if (row[pro.Name].GetType() == typeof(System.DBNull)) pro.SetValue(objT, null, null);
else pro.SetValue(objT, row[pro.Name], null);
}
}
return objT;
}).ToList();
}
catch (Exception e)
{
Console.WriteLine("Failed to write data to list. Often this occurs due to type errors (DBNull, nullables), changes in SP's used or wrongly formatted SP output.");
Console.WriteLine("ConvertToList Exception: " + e.ToString());
return new List<T>();
}
}
Gist: https://gist.github.com/Big-al/4c1ff3ed87b88570f8f6b62ee2216f9f
May be this will help:
Getting rows from DB:
public static DataRowCollection getAllUsers(string tableName)
{
DataSet set = new DataSet();
SqlCommand comm = new SqlCommand();
comm.Connection = DAL.DAL.conn;
comm.CommandType = CommandType.StoredProcedure;
comm.CommandText = "getAllUsers";
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = comm;
da.Fill(set,tableName);
DataRowCollection usersCollection = set.Tables[tableName].Rows;
return usersCollection;
}
Populating DataGridView from DataRowCollection :
public static void ShowAllUsers(DataGridView grdView,string table, params string[] fields)
{
DataRowCollection userSet = getAllUsers(table);
foreach (DataRow user in userSet)
{
grdView.Rows.Add(user[fields[0]],
user[fields[1]],
user[fields[2]],
user[fields[3]]);
}
}
Implementation :
BLL.BLL.ShowAllUsers(grdUsers,"eusers","eid","euname","eupassword","eposition");

How would you refactor this bit of code?

This bit of code runs on Windows Compact Framework and what it does is obvious. It looks as it should be refactored (especially considering that I may want to add cmd.ExecuteResultSet() later), but I can't see an elegant way to do it. Any ideas appreciated.
internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();
using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}
internal int RunScalar(string query)
{
string connString = GetLocalConnectionString();
using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
return int.Parse(cmd.ExecuteScalar().ToString());
}
}
I'm not sure I would refactor it, but perhaps:
static void PerformQuery(string connectionString, string command,
Action<SqlCeCommand> action)
{ //TODO: sanity checks...
using(SqlCeConnection conn = new SqlCeConnection(connectionString))
using(SqlCeCommand cmd = conn.CreateCommand()) {
cmd.CommandText = command;
conn.Open();
action(cmd);
}
}
internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();
PerformQuery(connString, query, cmd => cmd.ExecuteNonQuery());
}
internal int RunScalar(string query)
{
int result = 0;
string connString = GetLocalConnectionString();
PerformQuery(connString, query,
cmd => {result = int.Parse(cmd.ExecuteScalar().ToString()); }
);
return result;
}
Otherwise - just maybe a CreateAndOpenConnection(string) method, and a CreateCommand(SqlCeConnection,string) method.
If you are using C# 3.0, you could do something like the following:
private T CreateCommand<T>(string query, Func<SqlCeCommand, T> func)
{
var connString = GetLocalConnectionString();
using (var cn = new SqlCeConnection(connString))
{
cn.Open();
using (var cmd = cn.CreateCommand())
{
cmd.CommandText = query;
return func(cmd);
}
}
}
private void CreateCommand(string query, Action<SqlCeCommand> action)
{
CreateCommand<object>(query, cmd =>
{
action(cmd);
return null;
});
}
internal void RunNonQuery(string query)
{
CreateCommand(query, cmd => cmd.ExecuteNonQuery());
}
internal int RunScalar(string query)
{
return CreateCommand(query, cmd =>
int.Parse(cmd.ExecuteScalar().ToString()));
}
I would create a class out of the code to wrap the connection creation and command execution logic. This will provide you with a single place to implement transactions in the future and will consolidate creation of the connection and command. This consolidation will allow for settings timeouts, joining transactions, etc.
class Connection : IDisposable
{
readonly SqlConnection _conn;
public Connection()
{
string connString = GetLocalConnectionString();
_conn = new SqlConnection(connString);
_conn.Open();
}
public void Dispose() { _conn.Dispose(); }
public SqlCommand CreateCommand(string qry)
{
SqlCommand cmd = _conn.CreateCommand();
cmd.CommandText = qry;
//cmd.CommandTimeout = TimeSpan.FromMinutes(x);
return cmd;
}
public int ExecuteNonQuery(string qry)
{
using (SqlCommand cmd = CreateCommand(qry))
return cmd.ExecuteNonQuery();
}
public int RunScalar(string qry)
{
using (SqlCommand cmd = CreateCommand(qry))
return int.Parse(cmd.ExecuteScalar().ToString());
}
}
Then if you still want to maintain your original API, you do the following:
class SqlCode
{
internal void RunNonQuery(string query)
{
using (Connection cn = new Connection())
cn.ExecuteNonQuery(query);
}
internal int RunScalar(string query)
{
using (Connection cn = new Connection())
return cn.RunScalar(query);
}
}
The only thing left is to re-insert the 'Ce' in the SqlXxxx stuff ;)

Categories

Resources