How to execute a stored procedure from string - c#

So, I'm trying to execute a stored procedure from a string to get a wide range of data but I get a
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types
How do I probably execute my stored procedure
public class CarBLL
{
public static string GetCar(string carnumberxx, string authTicketxx)
{
using (var context = new SindalDBEntities())
{
var doerTicket = "0x0100000067294c938207c9e5e0cb6b98ddc4edd9464bc14e68b59cbc73c9dc559c5d43ad9b3705d1720a52542e0220b7df11b2e4bd913d873b1aa18c";
var stamdata = "G0057";
String sql =
"SET NOCOUNT ON; " +
"EXEC [file].[usp_getCar] " +
"#p_CarNumber= '" + stamdata + "', " +
"#p_DoerTicket= '" + doerTicket + "' ";
List<SqlParameter> sqlParams = new List<SqlParameter>();
sqlParams.Add(new SqlParameter("CarNumber", System.Data.SqlDbType.VarChar, 100, stamdata));
sqlParams.Add(new SqlParameter("DoerTicket", System.Data.SqlDbType.VarChar, 100, doerTicket));
return context.Database.SqlQuery<string>(sql).FirstOrDefault<string>();
}
}
}
}
If I try to exclude one of my parameters from the SP as
public static usp_getCar_Result GetCar(string carnumber, string authTicket)
{
using (var context = new SindalDBEntities())
{
int? id = null;
// ObjectParameter Id = new ObjectParameter("p_ID", typeof(global::System.Int32));
// ObjectResult result = new ObjectResult("usp_getCar_Result", typeof(string));
return context.usp_getCar(id, carnumber, authTicket).FirstOrDefault();
}
}
}
}
I gets a
Additional information: The data reader is incompatible with the specified 'SindalDbModel.usp_getCar_Result'. A member of the type, 'ID1', does not have a corresponding column in the data reader with the same name.

It's better to import your usp_getCar stored procedure to your DbContext. See this tutorial.
In this case you will have a strongly typed method in your SindalDBEntities. So you will be able to just execute it:
return context.GetCar(stamdata, doerTicket).FirstOrDefault();

Related

Mysql update query works without using parameters but doesn't when using them

Good day,
In c#, I am trying to run a MySQL update query to update one record, based on its id. Everything goes well as long as I'm not using parameters.
I'm experiencing the issue once I am adding one or several parameters. I have made the test with only one parameter and same problem here.
What am I missing here ?
Thank you very much for your help.
public static void editCustomerTest(ClsCustomerTest pTest)
{
MySqlConnection l_Connection = null;
string l_SpName = string.Empty;
MySqlCommand l_MyCommand = null;
try
{
l_Connection = ClsIconEnv.getDataAccess().MySqlConnection;
ClsDataAccess.OpenConnection(l_Connection);
l_SpName = "update tbTestCustomers " +
"set sName = '#sLastName', " +
"sFirstName = '#sFirstName', " +
"sAddress = '#sAddress' " +
"Where id = #id);";
l_MyCommand = new MySqlCommand(l_SpName, l_Connection);
l_MyCommand.Parameters.Add("#sLastName", pTest.Last_Name);
l_MyCommand.Parameters.Add("#sFirstName", pTest.First_name);
l_MyCommand.Parameters.Add("#sAddress", pTest.Address);
l_MyCommand.Parameters.Add("#id", pTest.id);
l_MyCommand.ExecuteNonQuery(); // <----- This is the line at which the execution stops
ClsDataAccess.CloseConnection(l_Connection);
}
catch (Exception exc)
{
ClsIconErrorManager.manageException(exc);
}
finally
{
}
}
You do not need to wrap your params into the string and you have to use AddWithValue instead of Add if you don't want to explicitly specify the type, like this
l_SpName = "update tbTestCustomers " +
"set sName = #sLastName, " +
"sFirstName = #sFirstName, " +
"sAddress = #sAddress" +
"Where id = #id);";
l_MyCommand.Parameters.AddWithValue("#sLastName", pTest.Last_Name);
l_MyCommand.Parameters.AddWithValue("#sFirstName", pTest.First_name);
l_MyCommand.Parameters.AddWithValue("#sAddress", pTest.Address);
l_MyCommand.Parameters.AddWithValue("#id", pTest.id);
Like this:
l_SpName = #"update tbTestCustomers
set sName = #sLastName,
sFirstName = #sFirstName,
sAddress = #sAddress
Where id = #id";
l_MyCommand = new MySqlCommand(l_SpName, l_Connection);
l_MyCommand.Parameters.AddWithValue("#sLastName", pTest.Last_Name);
l_MyCommand.Parameters.AddWithValue("#sFirstName", pTest.First_name);
l_MyCommand.Parameters.AddWithValue("#sAddress", pTest.Address);
l_MyCommand.Parameters.AddWithValue("#id", pTest.id);
l_MyCommand.ExecuteNonQuery();

Why my ADO.NET queries does not run in single execution but they will run in seperate executions?

Regarding a question and its related answer, I'm trying to create a new table type using SQL queries and run a join query on data which are of this table type.
I put both queries in the same script, i.e. create type and select but it is not working. My query is as below (the whole code is brought in the end of question):
IF TYPE_ID(N'BranchMappingType') IS NULL
CREATE TYPE BranchMappingType As Table
(COL_ServerID int,
COL_ServerSchema Common._SMALL_,
COL_TableName Common._SMALL_,
COL_BranchNo Common._SMALL_,
COL_BranchSchema Common._SMALL_ );
SELECT [VMA_BranchID] FROM [VIP].[VipMapping]
JOIN #TVP ON
VMA_ServerID=COL_ServerID AND
VMA_ServerSchema=COL_ServerSchema AND
VMA_TableName=COL_TableName AND
VMA_BranchNo=COL_BranchNo AND
VMA_BranchSchema=COL_BranchSchema;
I will execute my query using SqlDataReader.ExecuteReader but It will throw exception:
Column, parameter, or variable #TVP. : Cannot find data type BranchMappingType.
But when I run 1st part of query, i.e., IF TYPE_ID(...) ... CREATE TYPE it will execute with no errors and then running the second part i.e. Select from ... join #TVP works flawlessly.
It seems newly created table type is not recognized when I run both queries with single execution of SqlDataReader.ExecuteReader.
I want to know what is the reason and If I've made some mistakes during my implementation or not? I prefer to run both queries with one-time calling of SqlDataReader.ExecuteReader method.
Here is full code for better inspection:
List<int> keys = new List<int>();
if (foreignKeyInfoList == null || foreignKeyInfoList.Count == 0)
return new StatusResponseKeysList { ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT };
const string ID_FIELD = "VMA_BranchID";
const string STRUCTURED_DATA_TABLE_NAME = "#TVP";
const string COL1_SERVER_ID = "COL_ServerID";
const string COL2_SERVER_SCHEMA = "COL_ServerSchema";
const string COL3_TABLE_NAME = "COL_TableName";
const string COL4_BRANCH_NO = "COL_BranchNo";
const string COL5_BRANCH_SCHEMA = "COL_BranchSchema";
//const string COL6_TABLE_ID = "COL_TableID";
const string MAP1_SERVER_ID = "VMA_ServerID";
const string MAP2_SERVER_SCHEMA = "VMA_ServerSchema";
const string MAP3_TABLE_NAME = "VMA_TableName";
const string MAP4_BRANCH_NO = "VMA_BranchNo";
const string MAP5_BRANCH_SCHEMA = "VMA_BranchSchema";
const string TYPE_NAME = "BranchMappingType";
//const string MAP6_TABLE_ID = "VMA_TableID";
string sqlQuery = $"IF TYPE_ID(N'{TYPE_NAME}') IS NULL" + " " +
$"CREATE TYPE {TYPE_NAME} As Table" + " " +
$"({COL1_SERVER_ID} int," + " " +
$"{COL2_SERVER_SCHEMA} Common._SMALL_," + " " +
$"{COL3_TABLE_NAME} Common._SMALL_," + " " +
$"{COL4_BRANCH_NO} Common._SMALL_," + " " +
$"{COL5_BRANCH_SCHEMA} Common._SMALL_" + " " +
$"); ";
sqlQuery += $"SELECT [{ID_FIELD}] " +
$"FROM {Settings.VIP_MAPPING_TABLE} " +
$"JOIN {STRUCTURED_DATA_TABLE_NAME} ON " +
$"{MAP1_SERVER_ID}={COL1_SERVER_ID} " +
$" AND {MAP2_SERVER_SCHEMA}={COL2_SERVER_SCHEMA} " +
$" AND {MAP3_TABLE_NAME}={COL3_TABLE_NAME} " +
$" AND {MAP4_BRANCH_NO}={COL4_BRANCH_NO} " +
$" AND {MAP5_BRANCH_SCHEMA}={COL5_BRANCH_SCHEMA} ";// +
//$" AND {MAP6_TABLE_ID}={COL6_TABLE_ID}";
DataTable dataTable = new DataTable(TYPE_NAME);
dataTable.Columns.Add(COL1_SERVER_ID, typeof(int));
dataTable.Columns.Add(COL2_SERVER_SCHEMA, typeof(string));
dataTable.Columns.Add(COL3_TABLE_NAME, typeof(string));
dataTable.Columns.Add(COL4_BRANCH_NO, typeof(string));
dataTable.Columns.Add(COL5_BRANCH_SCHEMA, typeof(string));
//dataTable.Columns.Add(COL6_TABLE_ID, typeof(int));
for (int i = 0; i < foreignKeyInfoList.Count; i++)
{
ForeignKeyLookupModelInBranchSide oneKeySet = foreignKeyInfoList[i];
DataRow row = dataTable.NewRow();
row[COL1_SERVER_ID] = Int32.Parse(oneKeySet.ServerID);
row[COL2_SERVER_SCHEMA] = oneKeySet.ServerSchema;
row[COL3_TABLE_NAME] = oneKeySet.TableName;
row[COL4_BRANCH_NO] = oneKeySet.BranchNo;
row[COL5_BRANCH_SCHEMA] = oneKeySet.BranchSchema;
dataTable.Rows.Add(row);
}
try
{
using (SqlConnection sqlConnection = new SqlConnection(Settings.connectionString))
{
SqlCommand command = new SqlCommand(sqlQuery, sqlConnection);
SqlParameter p = command.Parameters.Add(STRUCTURED_DATA_TABLE_NAME, SqlDbType.Structured);
p.Value = dataTable;
p.TypeName = TYPE_NAME;
sqlConnection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
keys.Add(reader.GetInt32(i));
}
}
}
else
{
}
sqlConnection.Close();
}
return new StatusResponseKeysList
{
keysList = keys,
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT
};
}
catch (Exception e)
{
//Exception caught here !!!!!!!:
//"Column, parameter, or variable #TVP. : Cannot find data type BranchMappingType."
if (e.GetType() == typeof(SqlException))
{
//this is a db error
return new StatusResponseKeysList
{
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_014_DATABASE_EXCEPTION
};
}
else
{
//other types of erros (not a DB-error)
return new StatusResponseKeysList
{
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_001_COMMON_ERROR
};
}
}
The table type must exist before the parameterized query that uses the type is executed. The reason is the database engine first creates an internal table in tempdb matching the table type defined on the server before the batch executes. It then bulk inserts the TVP value into tempdb using the structured parameter value and meta-data provided by the API. Only after the table is loaded does the batch execute, where it can use the TVP value.
The implication is that one cannot create the table type and use it in the same batch that uses the type as a parameter.

Error inserting a record and retrieving the autonumeric ID with OleDb

I am trying to make an oledb connection to an Access database to insert a new record and retrieving the key generated all at once. The code is this:
private static int createUser(OleDbConnection accessConn)
{
try
{
accessConn.Open();
//DbCommand also implements IDisposable
using (OleDbCommand cmd = accessConn.CreateCommand())
{
//create command with placeholders
cmd.CommandText =
"INSERT INTO EmployeeFiles " +
"([FirstName], [LastName], [JobTitleID], [SecurityLevel], [RowGUID])" +
"VALUES(#FirstName, #LastName, #JobTitleID, #SecurityLevel, #RowGUID)";
//Set Parameters
string FirstName = "Dick";
string LastName = "Tracy";
int JobTitleID = 11;
string SecurityLevel = "1";
string RowGUID = "{" + Guid.NewGuid() + "}";
//add named parameters
cmd.Parameters.AddRange(new OleDbParameter[]
{
new OleDbParameter("#FirstName", FirstName),
new OleDbParameter("#LastName", LastName),
new OleDbParameter("#JobTitleID", JobTitleID),
new OleDbParameter("#SecurityLevel", SecurityLevel),
new OleDbParameter("#RowGUID", RowGUID)
});
int userId = 0;
//Add #EmployeeID to the params collection and then retrieve it with Value
cmd.Parameters.Add("#EmployeeID", OleDbType.Integer).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
userId = (int)cmd.Parameters["#EmployeeID"].Value;
//userId = (int)cmd.ExecuteScalar();
Console.WriteLine("user created successfully: {0}", deliveryId);
return userId;
}
}
catch (Exception ex)
{
Console.WriteLine("Error: Failed creating the user.\n{0}", ex.Message);
return 0;
}
finally
{
accessConn.Close();
}
}
And when I run the code it throws this error:
System.Data.OleDb.OleDbDataAdapter internal error: invalid parameter accessor: 9 BADBINDINFO
I'm pretty sure the problematic line is this:
cmd.Parameters.Add("#EmployeeID", OleDbType.Integer).Direction = ParameterDirection.Output;
And I just don't get where is the mistake.
If I comment that and the next two lines, and uncomment this:
userId = (int)cmd.ExecuteScalar();
It throws the error: Object reference not set to an instance of an object
Is there a way to get this working with OleDbParameter? So far I'm being forced to make the insert and then a select to get the key generated.
I appreciate any suggestion.

SQLiteException constraint failed

I have a SQLite table which I've created and it works fine when inserting data which is non-zero. However, I need to insert some zero default values and the SQLiteParameter seems to be converting the zero values to null
Can someone explain why I'm getting #xxxx3=null instead of #xxxx3=0 and also how to fix it.
This appears to happen for any numeric field (INTEGER/NUMERIC).
I've put together a simplified example that shows the problem
class Program
{
private static List<SQLiteParameter> DefaultSystemParameters()
{
List<SQLiteParameter> sp = new List<SQLiteParameter>()
{
new SQLiteParameter("#xxxx2", 60),
//new SQLiteParameter("#xxxx3", 1), // Works fine
new SQLiteParameter("#xxxx3", 0), // Throws 'System.Data.SQLite.SQLiteException' NOT NULL constraint failed: tblxxxx.xxxx3
};
return sp;
}
static void Main(string[] args)
{
//Add Nuget package - System.Data.SQLite v 1.0.99
string baseDir = AppDomain.CurrentDomain.BaseDirectory + AppDomain.CurrentDomain.RelativeSearchPath + "db\\";
string fileName = "test.db";
string sqlCreateTable = "CREATE TABLE IF NOT EXISTS tblxxxx (" +
"xxxx1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
"xxxx2 INTEGER NOT NULL," +
"xxxx3 INTEGER NOT NULL" +
")";
string sqlInsert = "INSERT INTO tblxxxx (xxxx2, xxxx3) VALUES (#xxxx2, #xxxx3)";
if (!Directory.Exists(baseDir))
Directory.CreateDirectory(baseDir);
DataTable dt = new DataTable();
string connectionString = $"Data Source={baseDir + fileName};Version=3;";
using (var connection = new SQLiteConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
//CREATE
using (SQLiteCommand command = new SQLiteCommand(sqlCreateTable, connection))
{
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
//INSERT
command.CommandText = sqlInsert;
command.Parameters.AddRange(DefaultSystemParameters().ToArray());
command.ExecuteNonQuery();
}
transaction.Commit();
}
}
}
}
From https://msdn.microsoft.com/en-us/library/0881fz2y(v=vs.110).aspx:
Use caution when you use this overload of the SqlParameter constructor
to specify integer parameter values. Because this overload takes a
value of type Object, you must convert the integral value to an Object
type when the value is zero, as the following C# example demonstrates.
Parameter = new SqlParameter("#pname", (object)0);

C# Fatal error encoutered during data read

I'm selecting about 20,000 records from the database and then I update them one by one.
I looked for this error and I saw that setting the CommandTimeout will help, but not in my case.
public void Initialize()
{
MySqlConnectionStringBuilder SQLConnect = new MySqlConnectionStringBuilder();
SQLConnect.Server = SQLServer;
SQLConnect.UserID = SQLUser;
SQLConnect.Password = SQLPassword;
SQLConnect.Database = SQLDatabase;
SQLConnect.Port = SQLPort;
SQLConnection = new MySqlConnection(SQLConnect.ToString());
}
public MySqlDataReader SQL_Query(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
MySqlDataReader query_result = sql_command.ExecuteReader();
return query_result;
}
public void SQL_NonQuery(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
sql_command.ExecuteNonQuery();
}
And here is my method which makes the select query:
public void CleanRecords()
{
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
UpdateRecords(cashData["id"].ToString());
}
}
SQLActions.SQL_Close();
}
And here is the method which makes the update:
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id`='" + rowID + "'");
SQLActions.SQL_Close();
}
Changing the database structure is not an option for me.
I thought that setting the timeout to the maxvalue of int will solve my problem, but is looks like this wont work in my case.
Any ideas? :)
EDIT:
The error which I get is "Fatal error encoutered during data read".
UPDATE:
public void CleanRecords()
{
StringBuilder dataForUpdate = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while (cashData.Read())
{
if (cashData["income_money"].ToString() == cashData["total"].ToString())
{
dataForUpdate.Append(delimiter);
dataForUpdate.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(dataForUpdate.ToString());
}
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowID + ")");
SQLActions.SQL_Close();
}
You may be able to use
UPDATE cash_data .... WHERE id IN (SELECT ....)
and do everything in one go. Otherwise, you could do it in two steps: first the select collects all the ids, close the connection and then do the update in obne go with all the ids.
The code for the second option might look something like this:
public void CleanRecords()
{
StringBuilder builder = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
builder.Append(delimiter);
builder.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(builder.ToString());
}
public void UpdateRecords(string rowIDs)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowIDs + ")";
SQLActions.SQL_Close();
}
There are multiple problem:
First: You have reading information around 20K using data reader and then doing update one by one in reader itself. Reader holds the connection open until you are finished. So this is not the good way to do it. Solution: We can read the information using Data Adapter.
Second: Rather than doing one by one update, we can update in bulk in one go. There are multiple option for bulk operation. In SQL u can do either by sending information in XML format or u can use Table Valued Parameter (TVP) (http://www.codeproject.com/Articles/22205/ADO-NET-and-OPENXML-to-Perform-Bulk-Database-Opera) OR (http://dev.mysql.com/doc/refman/5.5/en/load-xml.html)

Categories