In my project, I have a DBAdapter class that deals with database queries.
public class DBAdapter
{
private OleDbConnection _connection;
private void _Connect()
{
this._connection = new OleDbConnection();
_connection.ConnectionString = ConfigurationManager.AppSettings["Accessconnection"];
_connection.Open();
}
private void _Disconnect()
{
_connection.Close();
}
public DataTable Select(string query, OleDbParameterCollection parameters = null)
{
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
DataSet dataSet = new DataSet();
DataTable dataTable = new DataTable();
query = "SELECT " + query;
if (parameters != null)
{
foreach (OleDbParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
this._Connect();
cmd.Connection = _connection;
cmd.CommandText = query;
if (parameters != null) {
cmd.Prepare();
}
dataAdapter.SelectCommand = cmd;
dataAdapter.Fill(dataSet, "results");
this._Disconnect();
dataTable = dataSet.Tables["results"];
return dataTable;
}
}
In order to perform prepared queries, the Select method has an optionnal OleDBParameterCollection parameter.
Then, I have multiple Mappers for each domain object in my project, for example UserMapper, that use DataAdapter class to run queries (for example find user by id).
public class UserMapper : DataMapperAbstract
{
public User findByID(int id)
{
User user = new User()
string query = "* FROM USER WHERE idUser = ?";
OleDbParameterCollection parameters = new OleDbParameterCollection();
parameters.Add(new OleDbParameter("idUser", OleDbType.Integer).Value = id);
// Prepared query
DataTable results = adapter.Select(query, parameters);
this._populateData(user, results.Rows[0]);
return user;
}
}
Unfortunately, I have an error at this line
OleDbParameterCollection parameters = new OleDbParameterCollection();
VS says that the type OleDbParameterCollection has no constructor defined, and I don't really understand what is the problem here. Maybe I don't have rights to instantiate OleDbParameterCollection, but in that case, how should I pass a collection of parameters to my DBAdapter's method ?
OleDbParameterCollection doesn't expose public constructor we can access. It doesn't meant to be used that way so, simply change your method parameter to accept list of OleDbParameter instead of OleDbParameterCollection :
public DataTable Select(string query, List<OleDbParameter> parameters = null)
{
}
Then use it accordingly :
List<OleDbParameter> parameters = new List<OleDbParameter>();
parameters.Add(new OleDbParameter("idUser", OleDbType.Integer){ Value = id });
// Prepared query
DataTable results = adapter.Select(query, parameters);
OleDbParameterCollection has no public constructors.
When the OleDbDataAdapter object is instantiated it automatically creates an OleDbParameterCollection object for you within the SelectCommand object.
Here is some code from MSDN that I found:
OleDbDataAdapter adapter =
new OleDbDataAdapter(queryString, connection);
// Set the parameters.
adapter.SelectCommand.Parameters.Add(
"#CategoryName", OleDbType.VarChar, 80).Value = "toasters";
Here is the link:
http://msdn.microsoft.com/en-us/library/system.data.oledb.oledbparametercollection(v=vs.110).aspx
You may need to modify the way you run the query a little bit. But, it looks like you've already got an adapter object in there, so it shouldn't be too hard.
Related
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.
I'm developing a C# solution with data access to Oracle.
And would like to have a generic solution about query.
Here is a part of my code :
public DataTable GetData(string query)
{
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OracleClient");
using (DbConnection conn = factory.CreateConnection())
{
try
{
DbConnectionStringBuilder csb = factory.CreateConnectionStringBuilder();
csb["Data Source"] = #"Northwind";
csb["User Id"] = #"Northwind";
csb["Password"] = #"Northwind";
conn.ConnectionString = csb.ConnectionString;
conn.Open();
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = query;
using (DataTable dt = new DataTable())
{
DbDataAdapter da = factory.CreateDataAdapter();
cmd.CommandType = CommandType.Text;
da.SelectCommand = cmd;
da.Fill(dt);
return dt;
}
}
}
catch (Exception ex)
{
throw new Exception("Error", ex);
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
And I call my method like this :
DataAccess.Provider data = new DataAccess.Provider();
DataTabel dt = dt.GetData("select * from myTable);
This works pretty good but this is not my aim.
I have a second class called CL_mpg with all my SQL queries.
class CL_MPG
{
public string rq_sql;
public string selectParam(string param)
{
this.rq_sql = "select * from myTable where id = '" + param + "';";
return this.rq_sql;
}
public string select()
{
this.rq_sql = "select * from myTable";
return this.rq_sql;
}
//...
}
And I would like to use my methods selectParam and/or select to fill my datatable, but I don't know how to do that.
Although others complain at your learning attempt, everyone has to start somewhere. Your method is actually an ok start, but I would change the parameter from a string to a DbCommand object. Then, you can create your methods to properly build the command and set proper parameters. Then pass the entire prepared command to your wrapper method (that creates connection, tests open successful, queries data, etc) and have your method return a DataTable object as you have... something like
public class CL_MPG
{
private DataTable GetData(DbCommand cmd )
{
// do all the same as you have with exception of your USING DBCOMMAND.
// just set the connection property of the incoming command to that of
// your connection created
// AT THIS PART --
// using (DbCommand cmd = conn.CreateCommand())
// {
// cmd.CommandText = query;
// just change to below and remove the closing curly bracket for using dbcommand
cmd.Connection = conn;
}
// Now, your generic methods that you want to expose for querying
// something like
public DataTable GetAllData()
{
DbCommand cmd = new DbCommand( "select * from YourTable" );
return GetData( cmd );
}
public DataTable GetUser( int someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where ID = #parmID" );
cmd.Parameters.Add( "#parmID", someIDParameter );
return GetData( cmd );
}
public DataTable FindByLastName( string someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where LastName like #parmTest" );
cmd.Parameters.Add( "#parmTest", someIDParameter );
return GetData( cmd );
}
}
Notice the command is being built and fully prepared and parameterized vs concatination of strings as prior comment was made which could expose you to SQL-injection. As for the parameters, and not querying Oracle, they may need to be tweaked some. Different engines use slightly different conventions. If connecting to SQL-Server database, it uses "#" to identify a parameter. In SyBase Advantage Database, it uses ":". Using Visual FoxPro, a simple "?" placeholder is used.
Also, if your query has many criteria, just keep adding additional "#parm" type placeholders, then add your parameters in the same order as they appear in your query just to make sure you didn't miss any. Some functions could have none, one or more based on your needs. Then, in the samples provided, its as simple as doing something like
DataTable whoIs = yourCL_MPGObject.GetUser( 23 );
if( whoIs.Rows.Count > 0 )
MessageBox.Show( whoIs.Rows[0]["WhateverColumnName"] );
I am using generic list to store the data that comes by querying the databse.I uses List of classes actually for multiple rows.
But my problem is my classes have almost more than 20 properties and most of the time i uses only its 2 or 3 properties.
So I want to know that what is the best way to keep the data coming from database.
Below is my code
List<ImageGalleryCollection> tempList = new List<ImageGalleryCollection1>();
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList, connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
tempList.Add(igc);
}
data.Close();
connection.Close();
return tempList;
In ImageGalleryCollection I have more that 20 properties and above i only uses two properties.I think it is very inefficient
Can you how your base class implementation? You can create another class with the most using attributes and use an object of that class inside your class.
IEnumerable<ImageGalleryCollection> GetImageGalleryCollection()
{
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList,connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
yield return igc;
}
data.Close();
connection.Close();
}
I would like to suggest you to write a extension method for SqlDataReader and make use of the method in linq to fetch required columns from the returned rows of reader.
Extension method:
public static class DataReaderExtension
{
public static IEnumerable<Object[]> DataRecord(this System.Data.IDataReader source)
{
if (source == null)
throw new ArgumentNullException("source");
while (source.Read())
{
Object[] row = new Object[source.FieldCount];
source.GetValues(row);
yield return row;
}
}
}
using it in linq:
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Select * from tblUser"))
{
cmd.CommandType = CommandType.Text;
cmd.Connection = cn;
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var result = (from row in dr.DataRecord()
select new
{
UserId = row[0],
UserName = row[1]
}).ToList();
}
}
}
This result list has only the required properties you select and helps to reduce the consumption of memory for unwanted properties.
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;
}