How to declare a generic variable inside a generic method? - c#

I would like to know if it is possible to declare a generic variable inside a generic method. In the following code, I am trying to make "results" a generic variable but I am having a hard time doing so. Therefore, I would appreciate any help on this. Thanks in advance
public static T GetSQLResults<T>(string StoredProcName, Dictionary<string, string> StoredProcParameters)
{
var results;
using (SqlConnection conn = new SqlConnection(Settings.ConnStringDb))
{
using (SqlCommand cmd = new SqlCommand(StoredProcName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
foreach (var param in StoredProcParameters)
{
cmd.Parameters.AddWithValue(param.Key, param.Value);
}
conn.Open();
if (typeof(T) == typeof(Boolean))
{
results = cmd.ExecuteReader().HasRows;
}
else if (typeof(T) == typeof(DataTable))
{
results.Load(cmd.ExecuteReader());
}
//results.Load(cmd.ExecuteReader());
conn.Close();
}
return (T)Convert.ChangeType(results, typeof(T));
}
}

Related

How to convert stored procedure result to entity

I'm currently executing my stored procedure below, and it works perfectly. But I can't specify the command timeout.
var results = await _dbContext.DbContext.Database.SqlQuery<GetOutputDto>(#"[dbo].[GetOutput] " + parameterString, list.ToArray()).ToListAsync();
Now I've change this to the below, and wondering what's the best way to convert the result to an object. I have over 30 properties, so setting each value would be quite tedious. Was wondering if there's a clean solution as Entity Framework solution.
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
cmd.ExecuteNonQuery();
cmd.Connection.Close();
// How to get the result to entity in a clean manner.
}
Using System.reflection in those situation is really handy.
public static List<T> Convert<T>(IDataReader dr) where T : class, new()
{
List<T> list = new List<T>();
T obj = default(T);
while (dr.Read()) {
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
if (!object.Equals(dr[prop.Name], DBNull.Value)) {
prop.SetValue(obj, dr[prop.Name], null);
}
}
list.Add(obj);
}
return list;
}
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
using ( var reader = cmd.ExecuteReader() ){
List<Entity> result = Convert<Entity>(reader); // convert to entity.
cmd.Connection.Close();
}
}
I would in all honesty send over as an array and convert to table type within SQL and do the dirty work on the server side. Also a good way to be able to specify the timeout can be done by either the connection strings within your config file or you can also pass that same parameter over to sql with a WAITFOR DELAY.
Cheers!
Not that hard, do it like this
note, this is lazy eval so it should perform well when there is user IO, still fairly fast in other cases, I've used it in data ETL projects with many records.
public static IEnumerable<dynamic>( /* params */)
{
// build command object here.
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read()) // read the first one to get the columns collection
{
var cols = reader.GetSchemaTable()
.Rows
.OfType<DataRow>()
.Select(r => r["ColumnName"]);
do
{
dynamic t = new System.Dynamic.ExpandoObject();
foreach (string col in cols)
{
((IDictionary<System.String, System.Object>)t)[col] = reader[col];
}
yield return t;
} while (reader.Read());
}
}
// remember to close connection
}
From my simple DB framework https://gist.github.com/hoganlong/b7f5c5e8dde61ae3cd6f

Pass logic function as parameter to general function

Trying to reduse some code and follow the DRY principle with a generic approach. The code is kind of trivial, but I can not manage to compile it. I guess you see what I'm trying to do here (not repeat GetData(), but rather have several Logic() func's), what am I doing wrong?
Caller:
IEnumerable<IMyClass> result = Factory.GetData<IEnumerable<IMyClass>>("storedProsedureName", Factory.LogicFunction());
Logic:
public static IEnumerable<T> GetData<T>(string storedProsedureName, Func<string, OracleConnection, OracleCommand, IEnumerable<T>> functionCall) where T : class
{
using (OracleConnection conn = new OracleConnection(Helpers.GetConnectionString()))
{
using (OracleCommand cmd = conn.CreateCommand())
{
try
{
return functionCall(storedProsedureName, conn, cmd);
}
catch (Exception e)
{
throw e;
}
}
}
}
public static IEnumerable<IMyClass> LogicFunction(string storedProsedureName= "", OracleConnection conn = null, OracleCommand cmd = null)
{
conn.Open();
{...}
conn.Close();
return IEnumerable<IMyClass>;
}
Error:
Argument 2: cannot convert
from
'System.Collections.Generic.IEnumerable_IMyClass>'
to
'System.Func_string, OracleConnection, OracleCommand, IEnumerable _IEnumerable_IMyClass>>>'
Change your caller to below code.
Removed () and also changed generic closing type from IEnumerable<IMyClass> to IMyClass.
IEnumerable<IMyClass> result = Factory.GetData<IMyClass>("storedProsedureName", Factory.LogicFunction);

Adding multiple parameters to a query

So I wish to create a nice function that will query MySQL commands in a nicer way. Something along the lines of this one:
public int mysql_query_scalar(string query, parameters)
{
mysql_Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = mysql_connection;
cmd.CommandText = query;
cmd.Prepare();
cmd.Parameters.AddWithValue(parameterfromstring, parameternumber);
mysql_Close();
return int.Parse(cmd.ExecuteScalar().ToString());
}
Now, what I wish to do is to be able to add as many parameters as I can, and then start building up parameters. How is this possible? If so, please tell me or give me a snippet of it please.
I will gladly appreciate someone telling me how to use it too. I am pretty new to MySQL and I am not C# expert.
mysql_Open(); and mysql_Close(); are functions I did myself. Refer them as mysql_connection.Open(); and mysql_connection.Close();
You can use an extension method, like this:
public static class DbCommandExtensions
{
public static void AddInputParameters<T>(this IDbCommand cmd,
T parameters) where T : class
{
foreach (var prop in parameters.GetType().GetProperties())
{
object val = prop.GetValue(parameters, null);
var p = cmd.CreateParameter();
p.ParameterName = prop.Name;
p.Value = val ?? DBNull.Value;
cmd.Parameters.Add(p);
}
}
}
Call as below :
cmd.AddInputParameters(new { a = textBox1.Text, b = TextBox2.Text.... });
i would call this method that way:
List<SqlParameter> sList = new List<SqlParameter>();
sList.Add(new SqlParameter("#myValue", myValue));
sList.Add(new SqlParameter("#myValue2", myValue2));
mysql_query_scalar(myQuery, sList.ToArray());
and i would use using-directives
public static int mysql_query_scalar(string Command, SqlParameter[] parameters)
{
using (MySqlConnection myConnection = new MySqlConnection(ConnectionString))
{
myConnection.Open();
using (MySqlCommand myCommand = new MySqlCommand(Command, myConnection))
{
myCommand.Parameters.AddRange(parameters); // Add Parameters here
return (int)myCommand.ExecuteScalar();
}
}
}

selecting a certain column value from looping in ienumerable

I have a result of IEnumerable from a stored procedure and i am looping through the results inorder to get the value of a column(GUID). I am unsure of how to go about on getting the Guid column from my results set in the foreach loop
this is what i have:
var results = GetGuids(instId);
foreach (var item in results)
{
}
public IEnumerable GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.AddWithValue("#ItemID", id);
return _command.ExecuteReader();
}
}
You can't use most of the normal linq extension methods directly on the non-generic IEnumerable... but you can call .Cast<T>() to make it an IEnumerable<T>. At that point, things get easier:
public IEnumerable<Guid> GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add("#ItemID", SqlDbType.Int).Value = id;
return _command.ExecuteReader()
.Cast<DbDataRecord>()
.Select(r => (Guid)r["GuidColumn"]);
}
}
You need to produce the results yourself from the SqlDataReader
var results = GetGuids(instId);
foreach (var item in results)
{
}
public IEnumerable<Guid> GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.AddWithValue("#ItemID", id);
var guids = new List<Guid>();
using (SqlDataReader reader = _command.ExecuteReader())
{
while (reader.Read()
{
guids.Add( (Guid)reader["GuidColumn"]);
}
}
}
}

.net SQL Server stored procs wrapper class structure

I'm looking to write a C# SQL Server wrapper to call some stored procedures. If I was writing a single function I'd do something like the following (which I think is correct/proper):
void RunStoredProc1(object arg1)
{
using(SqlConnection conn = new SqlConnection(connStr)){
try{
SqlCommand cmd = new SqlCommand("storedProc1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#input1", arg1);
conn.Open();
cmd.ExecuteNonQuery();
} catch (Exception ex){
//handle the exception appropriately.
}
}
}
The problem I'm having is that it seems like a lot of repeated code... every function will have the same using/try(open/execute)/catch code, and it'd be nice to have it all in only one place. Is there a clean way of doing this? How about for queries that I'd want to use a data reader on?
Something like this should do:
void RunStoredProc(string storedProcName, IDictionary<string, object> args)
{
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
foreach (KeyValuePair<string, object> kvp in args)
{
cmd.Parameters.AddWithValue(kvp.Key, kvp.Value);
}
conn.Open();
cmd.ExecuteNonQuery();
}
}
The connection object itself would probably also be better off as a parameter to this helper method, so you could make it static. It might be interesting to write it as an extension method on SqlConnection.
I would keep the exception handling in your RunStoredProc1 method or even more likely: in the methods that call RunStoredProc1, because exception handling will likely differ on a case by case basis.
void RunStoredProc1(object input1)
{
var args = new Dictionary<string, object>()
{
{ "input1", input1 }
};
try
{
RunStoredProc("storedProc1", args);
}
catch (Exception ex)
{
// Handle exception properly
}
}
Just a fun exercise for me, and not necessarily the way you'd want to implement it. I wrote a quick fluent interface for building and executing SqlCommands.
A couple of sample usages:
int i = Sql.UsingConnection("sample")
.GetTextCommandFor("Select Top 1 ActorID From Actor Where FirstName = #fname")
.AddParameters(new {fname = "Bob"})
.OnException(e => Console.WriteLine(e.Message))
.ExecuteScalar<int>();
var q = Sql.UsingConnection("sample")
.GetTextCommandFor("Select * From Actor Where FirstName=#fname and ActorID > #id")
.AddParameters(new {id = 1000, fname = "Bob"});
using(var reader = q.ExecuteReader())
{
while(reader.Read())
{
// do something
}
}
The actual class(es) and Interfaces are below:
public class Sql
{
public static ISqlCommandTypeSelector UsingConnection(string connection)
{
return new SqlBuilder(connection);
}
private class SqlBuilder : ISqlCommandTypeSelector, ISqlParameterManager, ISqlExecutor
{
private string _connection;
private string _sqltext;
private CommandType _commandtype;
private Action<Exception> _exceptionBehavior = DefaultExceptionBehavior;
private IList<SqlParameter> _inParams;
public SqlBuilder(string connection)
{
_connection = ConfigurationManager.ConnectionStrings[connection].ConnectionString;
_inParams = new List<SqlParameter>();
}
public ISqlParameterManager GetTextCommandFor(string text)
{
_sqltext = text;
_commandtype = CommandType.Text;
return this;
}
public ISqlParameterManager GetProcCommandFor(string proc)
{
_sqltext = proc;
_commandtype = CommandType.StoredProcedure;
return this;
}
public ISqlExecutor OnException(Action<Exception> action)
{
_exceptionBehavior = action;
return this;
}
public void ExecuteNonQuery()
{
try
{
using (var connection = new SqlConnection(_connection))
using (var cmd = connection.CreateCommand())
{
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
cmd.ExecuteNonQuery();
}
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
}
public T ExecuteScalar<T>()
{
T result = default(T);
try
{
using (var connection = new SqlConnection(_connection))
using (var cmd = connection.CreateCommand())
{
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
result = (T) cmd.ExecuteScalar();
return result;
}
}
catch(InvalidCastException ex)
{
// rethrow?
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
return result;
}
public IDataReader ExecuteReader()
{
try
{
var connection = new SqlConnection(_connection);
var cmd = connection.CreateCommand();
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
return null;
}
public ISqlExecutor AddParameters(object #params)
{
var type = #params.GetType();
var props = type.GetProperties();
foreach (var propertyInfo in props)
{
var param = new SqlParameter("#" + propertyInfo.Name, propertyInfo.GetValue(#params, null));
param.Direction = ParameterDirection.Input;
_inParams.Add(param);
}
return this;
}
public ISqlExecutor WithoutParams()
{
return this;
}
private void ConfigureCommand(SqlCommand cmd)
{
cmd.CommandText = _sqltext;
cmd.CommandType = _commandtype;
}
private void PopulateParameters(SqlCommand cmd)
{
cmd.Parameters.AddRange(_inParams.ToArray());
}
private static void DefaultExceptionBehavior(Exception e)
{
// do something
}
}
}
public interface ISqlCommandTypeSelector
{
ISqlParameterManager GetTextCommandFor(string text);
ISqlParameterManager GetProcCommandFor(string proc);
}
public interface ISqlExecutor
{
ISqlExecutor OnException(Action<Exception> action);
void ExecuteNonQuery();
T ExecuteScalar<T>();
IDataReader ExecuteReader();
}
public interface ISqlParameterManager
{
ISqlExecutor AddParameters(object #params);
ISqlExecutor WithoutParams();
}
There is some repeated code that could probably be refactored some more if you really hate repeated code. This is just a fun exercise, and probably not how you want to do your data access however. This also doesn't support out parameters as it is written.
The Microsoft Enterprise Library Data Access Application Block can help to reduce redundant code like that, if you're sticking to pure ADO.NET for your data layer. See http://msdn.microsoft.com/en-us/library/ff664408(v=PandP.50).aspx. There are lots of code samples online and in the download as well, i.e. http://msdn.microsoft.com/en-us/library/ff664702(v=PandP.50).aspx.
I'm a big fan of letting computers do the rote, repetitive work. They're very good at it. I only have to teach them to do it once. So I wrote a code generator that uses a reference database to generate strongly typed access code. The advantage of this technique is that if you change the stored procedure's signatures, all you have to do it re-gen your data access layer. Any breaking changes will cause compile errors.
The code generate I wrote reads an XML file identifying the stored procedures of interest and retrieves their metadata from the specified reference database(s).
The XML file contains flags identifying whether each stored procedure returns
multiple result sets (dataset)
a single result set (datatable)
a single row (datarow)
a single row with a single column (a scalar value)
a DataReader
an XmlReader
or nothing (nonquery)
From that it generates appropriate code, 1 class per stored procedure. The generated code provides access to the stored procedure's return code as well as the returned value for any output parameters.
It also parses the declaration for the stored procedure in the stored procedure's source code to identify any optional arguments (those with default values): the generated code allows those to be omitted in the call to execute the stored procedure.
Invoking the generated code goes like this:
public DataTable GetRiskFactorsForPatient( int patientID )
{
dbo_GetRiskbyPatient sp = new dbo_GetRiskbyPatient( CONNECT_STRING_ID ) ;
int rc = sp.Exec( patientID ) ;
DataTable dt = sp.ResultSet ;
if ( dt == null ) throw new InvalidOperationException( "nothing returned from stored procedure" ) ;
return dt ;
}
Personally, i prefer
void RunStoredProc1(object arg1)
{
try
{
using(SqlConnection conn = new SqlConnection(connStr))
{
using SqlCommand cmd = new SqlCommand("storedProc1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#input1", arg1);
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
//handle the exception appropriately.
}
}
Over the traditional try catch finally that you would need to do to manage your resources.
But overall, I like doing it with separate methods, so that you can custom tailor your catch blocks for the sproc.
Also, You might need more than one parameter down the road, and you would just be making a mess of a fairly straight-forward function

Categories