I'm trying to execute a batch call to a stored procedure to sync data between two systems. My SP has several in parameters and 1 out parameter. Everything works fine except I can't seem to get the out parameter.
I created a simplified sample that outlines my issue.
My Stored Procedure looks like this.
CREATE PROCEDURE `sampleProc`(IN i_val INT, OUT o_val INT)
BEGIN
SELECT i_val + i_val INTO o_val;
END
My .NET code
class TestingSP
{
public static void Test()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("i_val", typeof(System.Int32)));
dt.Columns.Add(new DataColumn("o_val", typeof(System.Int32)));
for (int x = 1; x <= 100; x++)
{
DataRow dr = dt.NewRow();
dr["i_val"] = x;
dr["o_val"] = 0;
dt.Rows.Add(dr);
}
MySqlCommand command = new MySqlCommand();
command.CommandText = "sampleProc";
command.CommandType = CommandType.StoredProcedure;
command.UpdatedRowSource = UpdateRowSource.OutputParameters;
command.Parameters.Add("?i_val", MySqlDbType.Int32).SourceColumn = "i_val";
MySqlParameter output = new MySqlParameter();
output.ParameterName = "?o_val";
output.MySqlDbType = MySqlDbType.Int32;
output.Direction = ParameterDirection.Output;
output.SourceColumn = "o_val";
command.Parameters.Add(output);
MySqlConnectionStringBuilder conBuilder = new MySqlConnectionStringBuilder();
conBuilder.Server = "myserver";
conBuilder.UserID = "root";
conBuilder.Password = "password";
conBuilder.Port = 3308;
conBuilder.Database = "test_db";
Console.WriteLine("Rows: " + dt.Rows.Count);
using (MySqlConnection connection = new MySqlConnection(conBuilder.ConnectionString))
{
connection.Open();
command.Connection = connection;
using (MySqlDataAdapter da = new MySqlDataAdapter())
{
da.AcceptChangesDuringUpdate = true;
da.ContinueUpdateOnError = true;
da.UpdateCommand = command;
da.UpdateBatchSize = 50;
da.Update(dt);
foreach(var c in dt.GetErrors())
{
Console.WriteLine("Err: " + c.RowError);
}
foreach(DataRow row in dt.Rows)
{
Console.WriteLine("{0}: {1}", row["i_val"], row["o_val"]);
}
}
}
command.Dispose();
Console.WriteLine("Done...");
Console.ReadLine();
}
}
I've been scratching my head for a couple of days trying to get this to work but no matter what I try o_val is always zero.
Any help would be greatly appreciated.
Related
I am trying to sort a datatable into a DataSet. I want to sort by the Status Column in "DESC". But I am not aware how to go about this. I have tried the suggested solutions online but I seem not to be doing something right. Here is what I have tried, albeit, I have commented out the sorting lines of the code as they do not work for me. How can I sort my table using the Status column in Desc?
[WebMethod(EnableSession = true)]
public List < TaskListClass > getTasks() {
var userId = Session["UserId"].ToString();
List < TaskListClass > objB = new List < TaskListClass > ();
try {
using(var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DBConnString"].ToString())) {
connection.Open();
DataSet Myds = new DataSet();
// Myds.Tables[0].DefaultView.Sort = "Status desc";
SqlDataAdapter sqldr = new SqlDataAdapter();
string ProcName = "getTasks";
SqlCommand cmd = new SqlCommand(ProcName, connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#userId", SqlDbType.VarChar, 900).Value = userId;
sqldr.SelectCommand = cmd;
sqldr.Fill(Myds);
DataTable dt = Myds.Tables[0];
// DataTable dt = Myds.Tables[0].DefaultView.ToTable();
for (int i = 0; i < dt.Rows.Count; i++) {
objB.Add(new TaskListClass() {
Id = Convert.ToString(dt.Rows[i]["Id"]),
Subject = Convert.ToString(dt.Rows[i]["Subject"]),
Customer = Convert.ToString(dt.Rows[i]["Customer"]),
Sender = Convert.ToString(dt.Rows[i]["Sender"]),
Receiver = Convert.ToString(dt.Rows[i]["Receiver"]),
Priority = Convert.ToString(dt.Rows[i]["Priority"]),
StartDate = Convert.ToString(dt.Rows[i]["StartDate"]),
EndDate = Convert.ToString(dt.Rows[i]["EndDate"]),
Status = Convert.ToString(dt.Rows[i]["Status"]),
OnProgress = Convert.ToString(dt.Rows[i]["OnProgress"]),
});
}
}
} catch (Exception e) {
msg = e.ToString();
}
return objB;
}
Ok, a few things.
first up, a dataset is a collection of tables - "many tables" possible.
But you have ONE table, so why use a dataset? I see no need. Just use a single data table for this.
And this will reduce the code.
So, I suggest this, or close to this:
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DBConnString"].ToString()))
{
using (var cmd = new SqlCommand("getTasks", connection))
{
connection.Open();
cmd.CommandType = CommandType.StoredProcedure;
DataTable dt = new DataTable();
cmd.Parameters.Add("#userId", SqlDbType.VarChar).Value = userId;
dt.Load(cmd.ExecuteReader());
// now sort the datatable
dt.DefaultView.Sort = "Status DESC";
// now fill out our object with each row
foreach (DataRow OneRow in dt.Rows)
{
objB.Add(new TaskListClass()
{
Id = OneRow["Id"].ToString(),
Subject = OneRow["Subject"].ToString(),
Customer = OneRow["Customer"].ToString(),
Sender = OneRow["Sender"].ToString(),
Receiver = OneRow["Receiver"].ToString(),
Priority = OneRow["Priority"].ToString(),
StartDate = OneRow["StartDate"].ToString(),
EndDate = OneRow["EndDate"].ToString(),
Status = OneRow["Status"].ToString(),
OnProgress = OneRow["OnProgress"].ToString(),
}); ;
}
}
}
}
return objB;
The way the current code is written, you could add this after the for-loop:
objB = objB.OrderByDescending(t => t.Status).ToList();
Depending of the datatype of Status, it might be sorted alphabetically.
var dataRow = dt.AsEnumerable().OrderByDescending(x => x.Field<string>("Status")).ToList();
foreach (var item in dataRow)
{
//Enter your Code Here
}
Here dt is your datatable.
dataRow is a set of list.
After get the data list, you can asign it to your "objB".
I'm trying to read table data from ORACLE with Parameter binding in c#. While executing the dataAdapter I'm getting ORA-12571 exception.
Below is my code for reading multiple data at the same time.
public DataTable SelectFromServer(string qualifiedDBName, DataTable dataTable) {
try
{
if (this.Con.State == ConnectionState.Closed)
{
this.OpenConnection();
}
DataTable resultTable = new DataTable();
dataTable.TableName = qualifiedDBName;
DbProviderFactory factory = DbProviderFactories.GetFactory(this.Con);
using (DbCommand command = factory.CreateCommand())
{
command.Connection = this.Con;
command.CommandText = this.GenerateSqlToSelect(factory,command,dataTable);
DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = command;
adapter.Fill(resultTable);
return resultTable;
}
}
catch (Exception exc)
{
throw exc;
}
}
Below is the method where I used to generate Select Query
private string GenerateSqlToSelect(DbProviderFactory factory, DbCommand command, DataTable table)
{
//var values1 = new List<string>();
var SelectQuery = new StringBuilder();
var data = table.ToArray();
var syntax = new OracleSyntax();
command.GetType().GetProperty("ArrayBindCount").SetValue(command, table.Rows.Count, null);
for (var i = 0; i < table.Columns.Count; i++)
{
var names = new StringBuilder();
var values = new StringBuilder();
var column = table.Columns[i];
OracleParameter parameter = new OracleParameter();
parameter.ParameterName = column.ColumnName;
parameter.Direction = ParameterDirection.Input;
parameter.DbType = column.DataType.GetDbType();
parameter.Value = data[i];
parameter.ArrayBindSize = GetDataLength(data[i]);
if (SelectQuery.Length > 0)
{
SelectQuery.Append(" and ");
}
names.AppendFormat("{0}", column.ColumnName);
values.AppendFormat("{0}{1}", syntax.ParameterPrefix, column.ColumnName);
SelectQuery.AppendFormat("{0} = {1}", names, values);
command.Parameters.Add(parameter);
}
string operationString = "SELECT * FROM";
string sqlQuery = string.Format("{0} {1} WHERE {2}", operationString, this.FormatByQuote(syntax,table.TableName), SelectQuery);
return sqlQuery;
}
private int[] GetDataLength(object[] objs)
{
List<int> dataLengthIterator = new List<int>();
foreach (object obj in objs)
{
dataLengthIterator.Add(obj.ToString().Length);
}
return dataLengthIterator.ToArray();
}
this.FormatByQuote() method is nothing but getting a quoted TableName.
Output of GenerateSqlToSelect() method is
SELECT * FROM "CUSTOMER_MASTER" WHERE USER_ID= :USER_ID and LINE_NUMBER = :LINE_NUMBER
The Query is generating based upon the DataTable input
Tried multiple workarounds but could not get the reason why the exception is occurring.
i'm trying to get multiple values from database in return, i write a code and it return me only one value but i want all values in return. i didn't know how to solve it because i'm beginner in c#. here is code
string CommandText = "SELECT * FROM SV WHERE [SVCode]=#id";
using (SQLiteCommand cmd = new SQLiteCommand(CommandText, conn))
{
cmd.Parameters.AddWithValue("#id", sve.SVID);
conn.Open();
DataTable dt = new DataTable();
SQLiteDataAdapter da = new SQLiteDataAdapter(cmd);
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
sve.SVName = dr["SVName"].ToString();
sve.SVCity = dr["SVCity"].ToString();
sve.SVState = dr["SVState"].ToString();
sve.SVEmail = dr["SVEmail"].ToString();
sve.SVWebsite = dr["SVWebsite"].ToString();
sve.SVAddress = dr["SVAddress"].ToString();
sve.SVNote = dr["SVNote"].ToString();
}
return sve.SVName; //how to return SVCity,SVState,SVEmail,SVWebsite,SVAddress,SVNote
}
private string SVCB(){
SVE sve = new SVE();
sve.SVID = MSVD_vendorcode.SelectedItem.ToString();
sve.SVName = MSVD_vendorname.Text;
sve.SVCity = MSVD_vendorcity.Text;
sve.SVState = MSVD_vendorstate.Text;
sve.SVEmail = MSVD_vendoremail.Text;
sve.SVWebsite = MSVD_vendorwebsite.Text;
sve.SVAddress = MSVD_vendoraddress.Text;
sve.SVNote = MSVD_vendornote.Text;
SVSCB sv = new SVSCB();
return sv.SVCBSI(sve);}
private void SVcode_SelectedIndexChanged(object sender, EventArgs e)
{
MSVname.Text = SVCB();
MSVcity.Text = SVCB();
MSVstate.Text = SVCB();
MSVemail.Text = SVCB();
MSVwebsite.Text = SVCB();
MSVaddress.Text = SVCB();
MSVnote.Text = SVCB();
}
this code working but show only one value of SVName in every textbox.
Update!!
public static string SV_CBSI(SVE sve)
{
using (SQLiteConnection conn = new SQLiteConnection(DatabaseConnection.ConnectionString()))
{
string CommandText = "SELECT * FROM SV WHERE [SVCode]=#id";
using (SQLiteCommand cmd = new SQLiteCommand(CommandText, conn))
{
cmd.Parameters.AddWithValue("#id", sve.SVID);
conn.Open();
DataTable dt = new DataTable();
SQLiteDataAdapter da = new SQLiteDataAdapter(cmd);
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
sve.SVName = dr["SVName"].ToString();
sve.SVCity = dr["SVCity"].ToString();
sve.SVState = dr["SVState"].ToString();
sve.SVEmail = dr["SVEmail"].ToString();
sve.SVWebsite = dr["SVWebsite"].ToString();
sve.SVAddress = dr["SVAddress"].ToString();
sve.SVNote = dr["SVNote"].ToString();
}
return sve.SVName; //how to return SVCity,SVState,SVEmail,SVWebsite,SVAddress,SVNote
}
}
}
public static string SVCBSI(SVE sve)
{
return SVWCB.SV_CBSI(sve);
}
here is complete code
You need to have a method that return the whole SVE retrieved from database given a particular SVCode.
Notice that I suppose that SVCode is an integer. If not change the input type for this method accordingly
public SVE SV_CBSI(int id)
{
SVE localSve = new SVE();
string CommandText = "SELECT * FROM SV WHERE [SVCode]=#id";
using (SQLiteCommand cmd = new SQLiteCommand(CommandText, conn))
{
cmd.Parameters.AddWithValue("#id", id);
conn.Open();
DataTable dt = new DataTable();
SQLiteDataAdapter da = new SQLiteDataAdapter(cmd);
da.Fill(dt);
if(dt.Rows.Count > 0)
{
localSve.SVName = dr["SVName"].ToString();
localSve.SVCity = dr["SVCity"].ToString();
localSve.SVState = dr["SVState"].ToString();
localSve.SVEmail = dr["SVEmail"].ToString();
localSve.SVWebsite = dr["SVWebsite"].ToString();
localSve.SVAddress = dr["SVAddress"].ToString();
localSve.SVNote = dr["SVNote"].ToString();
}
return localSve;
}
}
Now you can call this method and get back the reference to an SVE instance filled with your data.
private void SVcode_SelectedIndexChanged(object sender, EventArgs e)
{
SVE currentSVE = SV_CBSI(Convert.ToInt32(SVCode.SelectedItem))
MSVname.Text = currentSVE.SVName;
MSVcity.Text = currentSVE.SVCity;
MSVstate.Text = currentSVE.State;
MSVemail.Text = currentSVE.SVEmail;
MSVwebsite.Text = currentSVE.SVWebsite;
MSVaddress.Text = currentSVE.SVAddress;
MSVnote.Text = currentSVE.SVNote;
}
May be you need to return an array (or a List) of strings?
I've been searching but have not found a good example of what i need to accomplish. The title says it all. This is what i have so far:
//gather the report details
DataTable dtReturn = new DataTable();
DataTable dtResults = new DataTable();
string strScript = "";
bool doReturnData = false;
try
{
//this function returns my SQL table in to a datatable dtReturn:
var ii =
Utility.db.Connection.EstablishDBConnection("usp_get_data_recap", dtReturn, null, null, true, 60);
//Clear out table before insert
dtResults = OracleDataPull(9, "TRUNCATE TABLE user.SIGNUP_1");
OracleParameter myparam = new OracleParameter();
OracleCommand mycommand = new OracleCommand();
int n;
//bulk insert to the signup_1 table from the datatable members. Datatable contains the same 4 fields being inserted in to signup_1 on the oracle side:
mycommand.CommandText = "INSERT INTO user.SIGNUP_1 ([ID], [ACCOUNT_NUMBER], [MAIN_CUSTOMER], [SIGNUP_DATE]) VALUES(?)";
mycommand.Parameters.Add(myparam);
for (n = 0; n < 100000; n++)
{
[what do i do here?]
}
}
I am not sure if this is correct, or if there is an easier way, but i need to map dtReturn.Rows[n][0-3] to ID, account_number, main_customer, and signup_date respectively.
Help is very apprecaited! Thanks in advance!
edit:
i tried the suggestion below, but am getting an error with the lambda expression:
"Cannot convert lambda expression to type 'string' because it is not a delegate type" :
var ii =
Utility.db.Connection.EstablishDBConnection("usp_get_data", dtReturn, null, null, true, 60);
dtResults = OracleDataPull(9, "TRUNCATE TABLE user.PR_data");
OracleParameter myparam = new OracleParameter();
OracleCommand mycommand = new OracleCommand();
mycommand.ArrayBindCount = dtReturn.Rows.Count;
mycommand.Parameters.Add(":myparam", OracleDbType.Varchar2, dtReturn.Select(c => c.myparam).ToArray(), ParameterDirection.Input);
mycommand.ExecuteNonQuery();
int n;
mycommand.CommandText = "INSERT INTO user.PR_data ([ID], [ACCOUNT_NUMBER], [MAIN_CUSTOMER], [SIGNUP_DATE]) VALUES(?)";
mycommand.Parameters.Add(myparam);
for (n = 0; n < 100000; n++)
{
myparam.Value = n + 1;
mycommand.ExecuteNonQuery();
}
dtResults = Utility.db.Connection.oracletoDataTable(strScript, doReturnData);
I am also not sure this is set up to produce the correct results. Can you please advise where i am going wrong here?
I actually found a more efficient approach to this solution thanks to codeproject.com.
This is my final method that is working great. All i do is call it with my schema.table and datatable and it handles the rest:
public static void WriteToServer(string qualifiedTableName, DataTable dataTable)
{
//**************************************************************************************************************************
// Summary: Hit the Oracle DB with the provided datatable. bulk insert data to table.
//**************************************************************************************************************************
// History:
// 10/03/2017 Created
//**************************************************************************************************************************
try
{
OracleConnection oracleConnection = new OracleConnection(Variables.strOracleCS);
oracleConnection.Open();
using (OracleBulkCopy bulkCopy = new OracleBulkCopy(oracleConnection))
{
bulkCopy.DestinationTableName = qualifiedTableName;
bulkCopy.WriteToServer(dataTable);
}
oracleConnection.Close();
oracleConnection.Dispose();
}
catch (Exception ex)
{
Utility.db.Log.Write(Utility.db.Log.Level.Error, "Utility", "db:WriteToServer: " + ex.Message);
throw;
}
}
ref: https://www.codeproject.com/Questions/228101/oracle-data-bulk-insert
mycommand.ArrayBindCount = dtResults.Count;
mycommand.Parameters.Add(":parameterName", OracleDbType.Varchar2, dtResults.Select(c => c.ParameterName).ToArray(), ParameterDirection.Input);
mycommand.ExecuteNonQuery() ;
after CommandText set you may use the code above.
Error Returned:
"No value given for one or more required parameters."
String Array to pass to function:
String[,] arrParams = new String[1, 2] {
{"#ToUpper_user_id", id}
};
Value of id:
"test" (without the quotes)
SQL:
strSQL = "select * from users where ToUpper_user_id = ?;";
SQL Function Call:
if (jdb.getdb_data(strSQL, arrParams, strTableName, out dsGet, out strTechMessage))
{
...
}
Function that calls to get data from the db:
public static bool getdb_data(String strSQL, String[,] arrParams, String strTableName, out DataSet dsGet, out String strTechMessage)
{
bool boolRC = true;
String key = String.Empty;
String val = String.Empty;
dsGet = new DataSet();
strTechMessage = String.Empty;
String strSQL_Empty = String.Empty;
string connectionString = jdb.getConnString();
using (OleDbConnection connection =
new OleDbConnection(connectionString))
{
OleDbCommand command = new OleDbCommand(strSQL, connection);
if (arrParams.GetLength(0) > 0)
{
for (int i = 0; i < arrParams.GetLength(0); i++)
{
for (int j = 0; j < arrParams.GetLength(1); j++)
{
if (j.Equals(0)) { key = arrParams[i, j]; }
if (j.Equals(1)) { val = arrParams[i, j]; }
}
command.Parameters.AddWithValue(key, val);
}
}
else
{
boolRC = false;
strTechMessage = "No parameters found";
}
// Open the connection in a try/catch block.
// Create and execute the DataReader, writing the result
// set to the console window.
if (boolRC)
{
try
{
connection.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(strSQL, connection);
adapter.Fill(dsGet, strTableName);
}
catch (Exception ex)
{
boolRC = false;
strTechMessage = ex.Message;
}
}
}
return boolRC;
}
Please help - I think I am goin' insane! (The Update CRUD all works with parameters . . . just the select code is giving me the error.)
In "get_dbdata(...)" above, I should have have had:
OleDbDataAdapter adapter = new OleDbDataAdapter(command);
instead of:
OleDbDataAdapter adapter = new OleDbDataAdapter(strSQL, connection);
In the code, both the sql and parameters have already been added to the command above.
Works now!