I have two methods that have similar input parameter types, but the parameters themselves are different and used to build an SQL statement within the method.
C# doesn't like this - "Type Database already defines a member called 'DatabaseSearch' with the same parameter types."
As a newbie, this sounds to me like I'm structuring the class or methods wrong?
Should I perhaps be building the SQL statement outside the method and passing it and its parameters in?
// Surname ONLY
public void DatabaseSearch(DataGrid DataGrid, string surname)
{
string database_file_path = #"Data Source=.\MemberDB.db";
string sqlCmd = "Select * FROM Members WHERE Surname = #surname";
using (var con = new SQLiteConnection(database_file_path))
{
using (var cmd = new SQLiteCommand(con))
{
con.Open();
cmd.Parameters.AddWithValue("#surname", surname);
cmd.CommandText = sqlCmd;
var dataAdapter = new SQLiteDataAdapter(cmd);
var dt = new DataTable("Members");
dataAdapter.Fill(dt);
DataGrid.ItemsSource = dt.DefaultView;
dataAdapter.Update(dt);
}
}
}
// Firstname ONLY
public void DatabaseSearch(DataGrid DataGrid, string firstname)
{
string database_file_path = #"Data Source=.\MemberDB.db";
string sqlCmd = "Select * FROM Members WHERE FirstName = #firstname";
using (var con = new SQLiteConnection(database_file_path))
{
using (var cmd = new SQLiteCommand(con))
{
con.Open();
cmd.Parameters.AddWithValue("#firstname", firstname);
cmd.CommandText = sqlCmd;
var dataAdapter = new SQLiteDataAdapter(cmd);
var dt = new DataTable("Members");
dataAdapter.Fill(dt);
DataGrid.ItemsSource = dt.DefaultView;
dataAdapter.Update(dt);
}
}
}
Summary:
How do you have two overloaded methods with the same types of parameters?
public void myMethod( int one, string one){
....some stuff done...
}
public void myMethod( int two, string two){
....different stuff done...
}
You can either change the method name or change the order of the parameters. The first approach is better.
public void DatabaseSearchByFirstName(int one, string one){
// ...some stuff done...
}
public void DatabaseSearchBySurName(int two, string two){
// ...different stuff done...
}
Related
I'm trying to write a function -like Dcount and Dlookup in VBA Access- in a public class to use it everywhere in my project so I did the following :
public class MyTools
{
SqlConnection Cn = new SqlConnection(#"Server = AMR-PC\SQLEXPRESS ; Database=PlanningDB ; Integrated Security = True");
SqlDataAdapter da;
DataTable dt = new DataTable();
// DataView dv = new DataView();
SqlCommand cmd;
SqlDataReader DataRead;
// Variables
string MyColumn, MyTable, MyCondition,DlookResult;
int DcountResult;
// Methods & Functions
// Dcount
public int DCount(string MyColumn, string MyTable, string MyCondition)
{
da = new SqlDataAdapter("Select Count(#MyColumn) from #MyTable where #MyColumn = #MyCondition", Cn);
da.Fill(dt);
DcountResult = int.Parse(dt.Rows[0].ToString());
return DcountResult;
}
}
// Dlookup
}
And tried to use it like this :
int Result = DCount(txtColumn.Text, txtTable.Text, txtCond.Text);
txtResult.Text = null;
txtResult.Text = Result.ToString();
But it throws the error "Must declare the scalar variable "#MyColumn".
I tried to use sqlcommand and DataRead but I need to close the connection after the return and it became Unreachable or close before the return so it returns nothing , That's why i used SqlDataAdapter.
Thanks in advance .
It would have to look something more like this:
public class MyTools
{
private static string ConnectionString {get;} = #"Server = AMR-PC\SQLEXPRESS ; Database=PlanningDB ; Integrated Security = True";
public static int DCount(string MyTable, string MyColumn, string MyCondition)
{
string sql = $"Select Count({MyColumn}) from {MyTable} where {MyColumn} = #MyCondition";
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("#MyCondition", MyCondition);
cn.Open();
return (int)cmd.ExecuteScalar();
}
}
}
Just be aware this uses dynamic SQL, and is more than a little dangerous. In fact, you should not do this. I know you don't want to "keep typing SQL queries", but that might be exactly what you should do.
I was trying to add a combo box which could get all the product name but unfortunately I follow some tutorials and end up like this.
void fillCombo()
{
try
{
con.Open();
OleDbCommand command = new OleDbCommand("Select * from IblInventory");
command.Connection = con;
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
String product = reader.GetString("ProductName"); // << invalid argument
cmbProduct.Items.Add(product);
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
What could possibly the reason?
From the documentation of OleDbDataReader.GetString you will notice that the argument required by the method is an integer representing the position of the column in the returned record not its name.
If you (rightly) prefer to use the column name then you need to take a detour and use the GetOrdinal method to retrieve the position of the column given the name.
while (reader.Read())
{
int pos = reader.GetOrdinal("ProductName");
String product = reader.GetString(pos);
cmbProduct.Items.Add(product);
}
Another example, practically identical to your situation, can be found in the documentation page on MSDN about OleDbDataReader.GetOrdinal
It is also a common practice to write an extension method that allows you to write code as yours hiding the details of the mapping between name and position. You just need a static class with
public static class ReaderExtensions
{
public string GetString(this OleDbDataReader reader, string colName)
{
string result = "";
if(!string.IsNullOrEmpty(colName))
{
int pos = reader.GetOrdinal(colName);
result = reader.GetString(pos);
}
return result;
}
... other extensions for Int, Decimals, DateTime etc...
}
Now with this class in place and accessible you can call
string product = reader.GetString("ProductName");
it is working in my project
First fill your data in to datatable see the below code
DataTable results = new DataTable();
using(OleDbConnection conn = new OleDbConnection(connString))
{
OleDbCommand cmd = new OleDbCommand("Select * from IblInventory", conn);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
adapter.Fill(results);
}
Now
cmbProduct.DataSource = results ;
cmbProduct.DisplayMember = "ProductName";
cmbProduct.ValueMember = "Id feild of IblInventory table";
My query returns results, but for some reason my DataTable always shows 0. The only thing I altered was the fact that I added parameters to the C# syntax (altho if I manually run the stored procedure it returns results). This is my syntax, does anyone see something that is incorrect syntactically in it?
protected void btnPress1464()
{
RunSQLStoredProc();
DataTable tableA = ebdb.Tables[0];
if (this.dtgAttendanceTracker.Items.Count == 0)
{
this.gvwTest.DataSource = tableA
this.gvwTest.DataBind();
}
}
public DataSet RunSQLStoredProc()
{
ebdb = new DataSet();
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
return ebdb;
}
public DataSet DoThis(string sqlQuery, int employeeid, DateTime hiredate, DateTime terminationdate)
{
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (SqlConnection conn = new SqlConnection(connstring.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = sqlQuery;
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#employeeid", employeeid.ToString());
cmd.Parameters.AddWithValue("#hiredate", hiredate.ToShortDateString());
cmd.Parameters.AddWithValue("#terminationdate", terminationdate.ToShortDateString());
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ebdb);
conn.Close();
}
}
return ebdb;
}
catch (Exception exception) { throw exception; }
}
The CommandText should only contain the stored-procedure name and not also exec if the command's CommandType is StoredProcedure. The StringBuilder is also redundant.
I also think that the way how you use AddWithValue with the wrong types could cause this issue(look at the last paragraph of my answer):
So not
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
but
ebdb = DoThis("alphadawg", otherParamaters...);
It's also bad practice to pass a sql-string to a method that executes it, that often introduces sql injection issues. You should not have a method DoThis but GetAlphaDawg which encapsulates the sql-query and only pass the parameter-values.
Apart from that, why do you return the DataSet from a method if it's actually a field in your class that you return? Instead initialize and fill it in the method, that's much clearer and also prevents issues when you load an already filled dataset(data will be appended by default).
This would be a possible implementation. Note that you shouldn't use AddWithValue and don't use String for DateTime but always use the correct type, all the more if you use AddWithValue which needs to infer the type from the value:
public DataSet GetAlphaDawg(int employeeid, DateTime hiredate, DateTime terminationdate)
{
DataSet dsAlpha = new DataSet();
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (var conn = new SqlConnection(connstring.ConnectionString))
{
using (var da = new SqlDataAdapter("alphadawg", conn))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
var parameter = da.SelectCommand.Parameters;
parameter.Add("#employeeid", SqlDbType.Int).Value = employeeid;
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate;
parameter.Add("#terminationdate", SqlDbType.Date).Value = terminationdate;
da.Fill(dsAlpha); // Open/Close not needed with Fill
return dsAlpha;
}
}
} catch (Exception) { throw; }
}
Since you use ToShortDateString, if you actually want to remove the time portion of your DateTime use DateTime.Date, for example:
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate.Date;
I tried to convert a datatable that just has one field (the field's data is primary key) to int , in order to using in sql commands such as Select and etc.
but it fails!
and when i cast it to an object or convert it to string first , the commands gone wrong!
please help me
i want to select * from a table which has a foreign key where the foreign code equals by an int value that has been selected from a table in another sql command and returned as a datatable row with just one field.
here is my code :
class mydata :
public string strsql;
public DataTable showData()
{
SqlConnection Con1 = new SqlConnection("Data Source=.;database=daneshgah;integrated security=true");
Con1.Open();
SqlDataAdapter da = new SqlDataAdapter(strsql, Con1);
DataTable dt = new DataTable();
da.Fill(dt);
Con1.Close();
return (dt);
}
button event :
myData search = new myData();
int aa = int.Parse(txt_stdcourse.Text);
search.strsql = "select tchNo from University where couNo='" + aa + "'";
DataTable a = search.showData();
string b = a.Rows[0][0].ToString();
int c = int.Parse(b);
myData akhz = new myData();
akhz.strsql = "insert into stc (couNo,tchNo,stuNo)values('" + aa + "','" + c + "','" + id + "')";
akhz.Data();
lbl_stdcourseok.Visible = false;
lbl_stdcourseok.Visible = true;
Sounds like you need to use ExecuteScalar on a SqlCommand instead of using a DataAdapter. ExecuteScalar gives the first column of the first row of the dataset returned.
public object RunSQL(string sql)
{
SqlConnection Con1 = new SqlConnection("Data Source=.;database=daneshgah;integrated security=true");
Con1.Open();
SqlCommand command = new SqlCommand(strsql, Con1);
return command.ExecuteScalar();
}
//In some event handler
int myValue = (int)RunSQL("Select Value from Table where ID = " + ID);
That said, please don't do that - it is very bad practice. You almost certainly want to create a class that models whatever data objects you are dealing with, instead of executing arbitrary SQL from event handlers. It is also probably best to manage connections independently of your data class, in a separate data access layer.
An extremely rudimentary example:
public class Student
{
public int StudentID { get; set; }
public bool CurrentlyEnrolled { get; set; }
public string Name { get; set; }
public static Student LoadByID(int ID)
{
DataTable results = DAL.ExecuteSQL("Select * from Students WHERE StudentID = #StudentID", new SqlParameter("#StudentID", ID));
if (results.Rows.Count == 1)
{
return FillFromRow(results.Rows[0]);
}
else
{
throw new DataException("Could not find exactly one record with the specified ID.");
}
}
private static Student FillFromRow(DataRow row)
{
Student bob = new Student();
bob.CurrentlyEnrolled = (bool)row["CurrentlyEnrolled"];
bob.Name = (string)row["Name"];
bob.StudentID = (int)row["StudentID"];
return bob;
}
}
public static class DAL
{
private const string ConnectionString = "SomeConnectionString"; //Should really be stored in configuration files.
public static DataTable ExecuteSQL(string SQL, params SqlParameter[] parameters)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(SQL))
{
command.Parameters.AddRange(parameters);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable result = new DataTable();
adapter.Fill(result);
return result;
}
}
}
}
}
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);