I'm writing an application which first connect to the database and retrieves a dt containing a list of all the stored procedures, inputs and their associated datatypes. The user then selected a SProc from the combobox and has to enter in the necessary inputs. The app will then connect to the database and run the selected SProc with the user specified inputs and return the results in a datatable.
What I'm unsure about is if I need to write a specific method for each SProc. I'm assuming so since I don't see how I could state what the parameters are otherwise.
Apologies for not making this clear the first time. Let me know if this still isn't clear enough.
Example is shown below (this is someone else's code)
public static GetDaysDTO GetDays(int offset)
{
GetDaysDTO ret = new GetDaysDTO { TODAY = DateTime.Now, TOMORROW = new DateTime(2012, 01, 01) };
SqlConnection con = new System.Data.SqlClient.SqlConnection(#"Server = FrazMan-pc\Programming; Database = master; Trusted_Connection = True");
SqlCommand cmd = new System.Data.SqlClient.SqlCommand
{
CommandText = "GetDays",
CommandType = System.Data.CommandType.StoredProcedure,
CommandTimeout = 1,
Connection = con,
Parameters = { new System.Data.SqlClient.SqlParameter("#offset", System.Data.SqlDbType.Int) { Value = offset } }
};
using (con)
{
con.Open();
using (System.Data.SqlClient.SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
ret.TODAY = DateTime.Parse(reader[0].ToString());
ret.TOMORROW = DateTime.Parse(reader["TOMORROW"].ToString());
}
}
}
return ret;
}
What you're looking for is a design pattern called Factory and a way to tell which typed data table to create on each SP call
If you have the list of the parameters for each procedure, u could instantiate the Parameters object via a loop:
This class will be used to fill the params of the sp received from the db
class ParamData
{
public object Data;
public SqlDbType type;
public string ParamName;
}
and then later on, when calling the sp, u should also pass thie ParamData object to the method, and used it to fill the params of ur sp dynamicly in a loop:
List<ParamData> list = new List<ParamData>();
//initialize command here as u did
SqlCommand cmd;
foreach (ParamData param in list)
{
SqlParameter sqlParam = new SqlParameter(param.ParamName, param.type);
sqlParam.Value = param.Data;
cmd.Parameters.Add(sqlParam);
}
//execute the command
//fill the datatable with result
DataTable dt = GetTableBySPName("GetDays");
SqlDataReader reader = cmd.ExecuteReader();
dt.Load(reader);
The only thing you need to add is the mapping between ur typed datatables and the returned table by the procedure.
You can add a method to do this:
private DataTable GetTableBySPName(string name)
{
DataTable dt = null;
switch (name)
{
case "GetDays":
{
dt = new GetDatsDTO();
break;
}
}
return dt;
}
Related
I know people are going to scream that this topic is all over the internet. I know, I've read them. And I still don't understand it. I simply want to populate my object from the results of a stored procedure. Now this stored procedure takes at least 2 parameters - an action and what to find.
#Action
#customername
The #Action simply determine what the stored procedure needs to do and returns the results. So for example if I want to update a customer, I'd call the sp through my object:
public class Customer()
{
//properties
public int UpdateCustomer()
{
using (SQLConnection connection = new SqlConnection(Helper.CnnVal("DataConnection")))
{
SQLCommand = new SqlCommand(Helper.Procedure("Customer"), connection);
command.CommandType = CommandType.StoredProcedure;
SqlParameterCollection parameterCollection = command.Parameters;
parameterCollection.Add("#Action", SqlDbType.NVarChar, -1).Value = "Update"
//complete the rest of it....
}
}
}
This works well. So the problem arises when I simply want to populate the object with the results of the sp. In this case I would pass "Retrieve" as the #Action parameter and this.customer_name as the #customername parameter to the sp.
But how do I put the results from the stored procedure into the object?
I have this...
public void GetCustomer()
{
using (SQLConnection connection = new SqlConnection(Helper.CnnVal("DataConnection")))
{
var retrieval = new DynamicParameters();
retrieval.Add("#Action", "Retrieve");
retrieval.Add("#name", this.customer_Name);
connection.Open():
connection.Execute(Helper.Procedure("Customer"), retrieval, commandType: CommandType.StoredProcedure);
}
}
But I don't think it's working.
Back a long time ago I used to run a "fetch" for PHP when I needed to populate an object. Should I go back to this?
You need to execute a SqlReader on the command, Something like this:
using (var connection = new SqlConnection("Connection"))
using (var command = new SqlCommand("Retrieve", connection))
{
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#Action", "Retrieve");
command.Parameters.AddWithValue("#name", this.customer_Name);
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var item = new YourClass();
// You can use GetX() methods to get the value of the fields
item.Name = reader.GetString("name");
// You can also access the fields by using bracket notation
item.Age = (int)reader["age"];
// Be careful of nullable fields though!
}
}
}
Using #Encrypt0r advice and guidance I got it working:
public void GetCustomer() {
using (SqlConnection connection = new SqlConnection(Helper.CnnVal("DataConnection"))) {
SqlCommand command = new SqlCommand(Helper.Procedure("Customer"), connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#Action", "Retrieve");
command.Parameters.AddWithValue("#name", this.customer_name);
connection.Open();
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
this.tbl_id = (int)reader["tbl_id"];
this.customer_name = (string)reader["customer_name"];
this.customer_id = reader.GetInt32(customer_id);
this.customer_address = (string)reader["customer_address"];
this.customer_city = (string)reader["customer_city"];
this.customer_state = (string)reader["customer_state"];
this.customer_zip = reader.GetInt32(customer_zip);
}
}
In my main form, I have implemented this code..
void SampleMethod(string name, string lastName, string database)
{
SqlParameter sqlParam = new SqlParameter();
sqlParam.ParameterName = "#name";
sqlParam.Value = name;
sqlParam.SqlDbType = SqlDbType.NVarChar;
SqlParameter sqlParam1 = new SqlParameter();
sqlParam1.ParameterName = "#lastName";
sqlParam1.Value = lastName;
sqlParam1.SqlDbType = SqlDbType.NVarChar;
SqlParameter sqlParam2 = new SqlParameter();
sqlParam2.ParameterName = "#database";
sqlParam2.Value = database;
sqlParam2.SqlDbType = SqlDbType.NVarChar;
SampleClass sampleClass = new SampleClass(new DBConn(#serverName, tableName, userName, password));
sampleClass.executeStoredProc(dataGridView1, "sp_sampleStoredProc", sqlParam, sqlParam1, sqlParam2);
}
And in my SampleClass, I have this kind of method.
public DataGridView executeStoredProc(DataGridView dtgrdView, string storedProcName, params SqlParameter[] parameters)
{
try
{
DataTable dt = new DataTable();
sqlDA = new SqlDataAdapter(storedProcName, sqlconn);
sqlDA.SelectCommand.CommandType = CommandType.StoredProcedure;
sqlDA.SelectCommand.CommandTimeout = 60;
// Loop through passed parameters
if (parameters != null && parameters.Length > 0)
{
foreach (var p in parameters)
sqlDA.SelectCommand.Parameters.Add(p);
}
sqlDA.Fill(dt);
dtgrdView.DataSource = dt;
sqlconn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
sqlconn.Close();
}
return dtgrdView;
}
What I am trying to do is avoid multiple
SqlParameter sqlParam = new SqlParameter()
in my code, I have tried so many solutions for this problem but I didn't get the right answer. I have also tried to research about this but I still couldn't get the right answer.
Please don't mind my naming convention and other codes as I intentionally change many of them :) Thanks.
As an alternative to your solution, try to use already existing one instead, using Dapper (https://github.com/StackExchange/dapper-dot-net).
You still need to use multiple parameters if your stored procedure or query needs it, but this is nicely abstracted for you and this will definatelly reduce the amount of code.
void SampleMethod(string name, string lastName, string database)
{
using(var connection = new SqlConnection(MY_CONNECTION_STRING))
{
var resultListOfRows = connection.Query<ReturnObject>(MY_STORED_PROCEDURE, new {
name = name,
lastName = lastName,
database = database}, commandType: System.Data.CommandType.StoredProcedure);
}
}
First of all, the easiest option would be to use a microORM like Dapper, and retrieve a strongly-typed collection. Gridviews can bind to anything, including strongly typed collections. All this code could become:
using(var con=new SqlConnection(myConnectionString))
{
con.Open();
var result= connection.Query<ResultClass>("sp_MySproc",
new { Name= name, LastName= lastName,Database=database},
commandType: CommandType.StoredProcedure);
return result;
}
Even when using raw ADO.NET, you can create a SqlParameter in one line by using the appropriate constructor . For example, you can create a new nvarchar(n) parameter with:
var myParam=new SqlParameter("#name",SqlDbType.NVarchar,20);
or
var myParam=new SqlParameter("#name",SqlDbType.NVarchar,20){Value = name};
A better idea though is to create the SqlCommand object just once and reuse it. Once you have an initialized SqlCommand object, you can simply set a new connection to it and change the parameter values, eg:
public void Init()
{
_loadCustomers = new SqlCommand(...);
_loadCustomers.Parameters.Add("#name",SqlDbType.NVarChar,20);
...
}
//In another method :
using(var con=new SqlConnection(myConnectionString)
{
_loadCustomers.Connection=con;
_loadCustomers.Parameters["#name"].Value = myNameParam;
con.Open();
using(var reader=_load.ExecuteReader())
{
//...
}
}
You can do the same thing with a SqlDataAdapter, in fact that's how Windows Forms and Data Adapters are meant to be used since .NET 1.0 .
Instead of creating a new one each time you want to fill your grid, create a single one and reuse it by setting the connection and parameters before execution. You can use the SqlDataAdapter(SqlCommand) constructor to make things a bit cleaner:
public void Init()
{
_loadCustomers = new SqlCommand(...);
_loadCustomers.Parameters.Add("#name",SqlDbType.NVarChar,20);
....
_myGridAdapter = new SqlDataAdapter(_loadCustomers);
...
}
And call it like this:
using(var con=new SqlConnection(myConnectionString))
{
_myGridAdapter.SelectCommand.Connection=con;
_myGridAdapter.SelectCommand.Parameters["#name"].Value =....;
con.Open();
var dt = new DataTable();
_myGridAdapter.Fill(dt);
dtgrdView.DataSource = dt;
return dtgrdView;
}
Separate your Database logic at one place(put sqladapter, sqlcommand etc at one place), Then encapsulate parameters within your command like mentioned below and you don't need to declare sqlparameter separately, add it inside parameters list.
cmdToExecute.Parameters.Add(new SqlParameter("#parameter", value));
Take a look at the complete example below
public DataTable ProdTypeSelectAll(string cultureCode)
{
SqlCommand cmdToExecute = new SqlCommand();
cmdToExecute.CommandText = "dbo.[pk_ProdType_SelectAll]";
cmdToExecute.CommandType = CommandType.StoredProcedure;
DataTable toReturn = new DataTable("ProdType");
SqlDataAdapter adapter = new SqlDataAdapter(cmdToExecute);
cmdToExecute.Connection = _mainConnection;
cmdToExecute.Parameters.Add(new SqlParameter("#CultureName", cultureCode));
_mainConnection.Open();
adapter.Fill(toReturn);
return toReturn;
}
You may be able to use the SqlParameter Constructor (String, Object). Replace:
sampleClass.executeStoredProc(dataGridView1,
"sp_sampleStoredProc",
sqlParam,
sqlParam1,
sqlParam2);
With:
sampleClass.executeStoredProc(dataGridView1,
"sp_sampleStoredProc",
new SqlParameter("#name", (object)name),
new SqlParameter("#lastName", (object)lastName),
new SqlParameter("#database", (object)database));
I am creating a method that can be called from anywhere in my application that will take in the named of a stored procedure and a list of parameters to pass to it.
In doing this I ran across the Parameters.AddWithValue command, but also ran across a blog posts and some SO posts that say this is bad due to conversion issues. They all recommended to add parameters using
Parameters.Add(PARAMETER, SqlDbType.TYPE);
but the problem with this is if I have a method like mine how do I properly use the Parameters.Add method when I don't know what type the parameters are when they come in? What is a good way to address this, or am I being overly paranoid and just should stick with Parameters.AddWithValue?
For reference here is the base method right now that I am attempting to update so it can handle parameters
public static DataTable ExecuteDynamicsStoredProc(string procedureName)
{
var dataTable = new DataTable();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DynamicsDB"].ToString()))
{
using (var command = new SqlCommand("c2s_ProjectPerformanceReport", connection))
{
connection.Open();
command.CommandType = CommandType.StoredProcedure;
var dataAdapter = new SqlDataAdapter();
dataAdapter.SelectCommand = command;
dataAdapter.Fill(dataTable);
return dataTable;
}
}
}
You can make the method accept a SqlParameter[] and use command.Parameters.AddRange().
public static DataTable ExecuteDynamicsStoredProc(string procedureName, SqlParameter[] args) {
var dataTable = new DataTable();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DynamicsDB"].ToString())) {
using (var command = new SqlCommand(procedureName, connection)) { //use passed in proc name
connection.Open();
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(args); //add all the parameters
var dataAdapter = new SqlDataAdapter();
dataAdapter.SelectCommand = command;
dataAdapter.Fill(dataTable);
return dataTable;
}
}
}
public static void ExecuteProcOne(string name, int age, bool alive) {
SqlParameter p1 = new SqlParameter("name", name);
SqlParameter p2 = new SqlParameter("age", age);
SqlParameter p3 = new SqlParameter("alive", alive);
var result = ExecuteDynamicsStoredProc("ExecuteProcOne", new SqlParameter[] { p1, p2, p3 });
}
Use methods like ExecuteProcOne() to handle the individual procedures with their respective datatypes. You can extract this out further to make a method return the SqlParameter[].
This way you can just call MyProcName and you know what parameters you need from intellisense.
Here is my stored procedure:
CREATE Proc UpdateChecklist
(
#TemplateId As INT
) as
begin
select MF.CheckListDataId from TemplateModuleMap TM
inner join ModuleField MF
on TM.ModuleId = MF.ModuleId
where TM.TemplateId = #TemplateId and MF.CheckListDataId not in
(select cktm.CheckListDataId from ChecklistTemplateMap cktm
inner join ChecklistData ckd
on cktm.CheckListDataId = ckd.Id
where cktm.TemplateId = #TemplateId)
end
So I expect to have a returned list of CheckListDataId here. I'm trying to use Database.ExecuteSqlCommand() but not succeed yet. How can I return a list of CheckListDataId here? Do I need to modify my stored proc? I'm pretty new to sql.
Any suggestion? This is an ASP.NET MVC 5 project
Your Stored Procedure will return you a resultset and you can process that however you want in your C#.
I would call the procedure from inside my model class in this way:
DataTable loadLogFilterData = SQLHelper.ExecuteProc(STORED_PROCEDURE_NAME, new object[] {
//Parameters to Stored Proc If Any
});
Then I have a SQLHelper Class inside which I create the SQL Connection and have the delegate methods to call the stored procedures.
public static DataTable ExecuteProc(string procedureName, Object[] parameterList, string SQLConnectionString) // throws SystemException
{
DataTable outputDataTable;
using (SqlConnection sqlConnection = OpenSQLConnection(SQLConnectionString))
{
using (SqlCommand sqlCommand = new SqlCommand(procedureName, sqlConnection))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
if (parameterList != null)
{
for (int i = 0; i < parameterList.Length; i = i + 2)
{
string parameterName = parameterList[i].ToString();
object parameterValue = parameterList[i + 1];
sqlCommand.Parameters.Add(new SqlParameter(parameterName, parameterValue));
}
}
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand);
DataSet outputDataSet = new DataSet();
try
{
sqlDataAdapter.Fill(outputDataSet, "resultset");
}
catch (SystemException systemException)
{
// The source table is invalid.
throw systemException; // to be handled as appropriate by calling function
}
outputDataTable = outputDataSet.Tables["resultset"];
}
}
return outputDataTable;
}
You have treat every output from a stored procedure as a resultset no matter what it contains. Then you need to manipulate that result set in your Model to populate the desired data structure and data type.
This is a question from an experienced beginner!
Using ASP.NET 4 C# AND SQL server,
I have a connection string in web.config to myDatabase named "myCS".
I have a database named myDB.
I have a table named myTable with a primary key named myPK
What are the NECESSARY lines of code behind (minimal code) to create a SQL connection, then select from myTable where myPK=="simpleText"
it will probably include:
sqlconnection conn = new sqlconnection(??? myCS)
string SQLcommand = select * from myDB.myTable where myPK==myTestString;
sqlCommand command = new SqlCommand(SQL,conn);
conn.Open();
booleanFlag = ????
conn.Close();
conn.Dispose();
then
If ( theAnswer != NULL ) // or (if flag)
{
Response.Redirect("Page1.aspx");
}
else
{
Response.Redirect("Page2.aspx");
}
Here is a limited simple tutorial:
First, you want to have a class to do the hard work for you, then you will use it with ease.
First, you have to crate the connection string in your web.config file and name it.
Here it is named DatabaseConnectionString, but you may named it myCS as required in the question.
Now, in App_Code create a new class file and name it SqlComm (this is just an example name) like:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
public class SqlComm
{
// this is a shortcut for your connection string
static string DatabaseConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["dbConStr"].ConnectionString;
// this is for just executing sql command with no value to return
public static void SqlExecute(string sql)
{
using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
// with this you will be able to return a value
public static object SqlReturn(string sql)
{
using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
object result = (object)cmd.ExecuteScalar();
return result;
}
}
// with this you can retrieve an entire table or part of it
public static DataTable SqlDataTable(string sql)
{
using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Connection.Open();
DataTable TempTable = new DataTable();
TempTable.Load(cmd.ExecuteReader());
return TempTable;
}
}
// sooner or later you will probably use stored procedures.
// you can use this in order to execute a stored procedure with 1 parameter
// it will work for returning a value or just executing with no returns
public static object SqlStoredProcedure1Param(string StoredProcedure, string PrmName1, object Param1)
{
using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
{
SqlCommand cmd = new SqlCommand(StoredProcedure, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter(PrmName1, Param1.ToString()));
cmd.Connection.Open();
object obj = new object();
obj = cmd.ExecuteScalar();
return obj;
}
}
}
Okay, this only a class, and now you should know how to use it:
If you wish to execute a command like delete, insert, update etc. use this:
SqlComm.SqlExecute("TRUNCATE TABLE Table1");
but if you need to retrieve a specific value from the database use this:
int myRequiredScalar = 0;
object obj = new object();
obj = SqlComm.SqlReturn("SELECT TOP 1 Col1 FROM Table1");
if (obj != null) myRequiredScalar = (int)obj;
You can retrieve a bunch of rows from the database this way (others like other ways)
This is relevant to your sepecific question
int Col1Value = 0;
DataTable dt = new DataTable();
dt = SqlComm.SqlDataTable("SELECT * FROM myTable WHERE myPK='simpleText'");
if (dt.Rows.Count == 0)
{
// do something if the query return no rows
// you may insert the relevant redirection you asked for
}
else
{
// Get the value of Col1 in the 3rd row (0 is the first row)
Col1Value = (int)dt.Rows[2]["Col1"];
// or just make the other redirection from your question
}
If you need to execute a stored procedure with or without returning a value back this is the way to do that (in this example there are no returning value)
SqlComm.SqlStoredProcedure1Param("TheStoredProcedureName", "TheParameterName", TheParameterValue);
Again, for your specific question return the table using the SqlDataTable , and redirect if dt.Rows.Count >0
Have fun.
There are many ways: LINQ, SqlDataReader, SQLDataAdapter, according to what you want to read (single value, datatable ...), so here is an example:
using (SqlConnection con = new SqlConnection("SomeConnectionString"))
{
var cmd = new SqlCommand("select from myTable where myPK==N'"+ simpleText+ "'",con);
cmd.Connection.Open();
var sqlReader = cmd.ExecuteReader();
while(sqlReader.Read())
{
//Fill some data like : string result = sqlReader("SomeFieldName");
}
sqlReader.Close();
cmd.Connection.Close();
cmd.Dispose();
}