MySql parameterized stored procedure does not work in C# - c#

I have parameterized stored procedure (tested and works) in mysql.
I wanted to replace my long select statement with stored procedure in my code.
With select statement it worked but now I am not able to run it with sp (commented string req).
My stored procedure:
CREATE DEFINER=`root`#`%` PROCEDURE `GetProjectVM`(
IN projectName NVARCHAR(255)
)
BEGIN
select VirtualMachines.Name,
VirtualMachines.IpAddress,
VirtualMachines.DiskSize,
VirtualMachines.CPU,
VirtualMachines.Ram,
VirtualMachines.ImageUrl,
VirtualMachines.Role,
Hypervisors.Name as Hypervisor,
Managements.Netmask,
Managements.Gateway from VirtualMachines inner join Projects
on Projects.id = VirtualMachines.ProjectId inner join Hypervisors
on Hypervisors.HypervisorId = VirtualMachines.HypervisorId inner join Managements
on VirtualMachines.ManagementId = Managements.Id
where Projects.Name = projectName;
END
My code looks like:
private string _dbUrl;
private MySqlConnection _cnn;
public Dbmanagement(string dbUrl)
{
this._dbUrl = dbUrl;
this._cnn = new MySqlConnection(this._dbUrl);
}
private MySqlDataReader ExecuteMysqlCommand(string cmd)
{
_cnn.Open();
var result = new MySqlCommand(cmd, _cnn);
return result.ExecuteReader();
}
public IEnumerable<VM> GetProjectVM(string projectName)
{
var vmList = new List<VM>();
//string req =
// $"select VirtualMachines.Name,VirtualMachines.IpAddress,VirtualMachines.DiskSize,VirtualMachines.CPU,VirtualMachines.Ram,VirtualMachines.ImageUrl,VirtualMachines.Role,Hypervisors.Name as Hypervisor,Managements.Netmask,Managements.Gateway from VirtualMachines inner join Projects on Projects.id = VirtualMachines.ProjectId inner join Hypervisors on Hypervisors.HypervisorId = VirtualMachines.HypervisorId inner join Managements on VirtualMachines.ManagementId = Managements.Id where Projects.Name = \"{projectName}\" ;";
MySqlCommand cmd = new MySqlCommand("GetProjectVM", _cnn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#projectName", projectName);
var req = cmd;
var rd = ExecuteMysqlCommand(req.ToString());
while (rd.Read())
{
vmList.Add(new VM(rd));
}
_cnn.Close();
return vmList;
}

The problem - or at least the first problem - is how you're executing the command.
Calling ToString on a MySqlCommand is very unlikely to be able to convey all the relevant information. You're losing parameterization, command type etc.
Change this:
var req = cmd;
var rd = ExecuteMysqlCommand(req.ToString());
to
_cnn.Open();
var rd = req.ExecuteReader();
I'd also suggest not having a single MySqlConnection, but just constructing one when you need to, and letting the connection pool manage making that efficient. So something like:
using (var connection = new MySqlConnection(_dbUrl))
{
connection.Open();
using (var cmd = new MySqlCommand("GetProjectVM", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
// Avoid AddWithValue where possible, to avoid conversion issues.
cmd.Parameters.Add("#projectName", MySqlDbType. VarChar).Value = projectName;
using (var reader = cmd.ExecuteReader())
{
var list = new List<VM>();
while (reader.Read())
{
list.Add(new VM(reader));
}
return list;
}
}
}

Related

C# SqlDataReader keeps reading

I have a method for getting data out of my SQL Server database. I'm using a reader to get all the data. The problem is that the reader keeps reading and the application doesn't pop up. It is like an infinite loop or something.
public static List<Reservering> GetReserverings()
{
Reservering res = new Reservering();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
}
reader.Close();
}
return GetReserverings();
}
Does anyone know how to fix this problem?
You've got an infinite recursion by calling the method itself at the end:
return GetReserverings();
You could've discovered this by setting a breakpoint in your method and stepping through your code.
You want to return a list of reservations instead:
var result = new List<Reservering>();
// your code...
return result;
And within your while() loop, you'd want to instantiate a new Reservering each iteration, and Add it to the result list.
You should create an instance of Reservering for each record read and store these instances in the List<Reservering>:
public static List<Reservering> GetReserverings() {
List<Reservering> result = new List<Reservering>();
using (var conn = new SqlConnection(ConnectionString)) {
conn.Open();
const string query =
#"select b.boekingid,
k.naam,
bk.incheckdatum,
bk.uitcheckdatum,
b.hotelid,
b.aantal_gasten
from boeking b join
klant k on k.klantid = b.boekingid join
boekingkamer bk on b.boekingid = bk.boekingid
where bk.incheckdatum is not null
and bk.uitcheckdatum is not null";
using (SqlCommand selectReserveringen = new SqlCommand(query, conn)) {
using (SqlDataReader reader = selectReserveringen.ExecuteReader()) {
while (reader.Read()) {
Reservering res = new Reservering();
result.Add(res);
res.Id = Convert.ToInt32(reader["boekingid"]);
res.Naam = Convert.ToString(reader["naam"]);
res.Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]);
res.Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]);
res.Hotel = Convert.ToInt32(reader["hotelid"]);
res.Aantal_personen = Convert.ToInt32(reader["aantal_gasten"]);
}
}
}
}
return result;
}
Edit: You can try to simplify while loop with a help of object initializing:
...
while (reader.Read()) {
result.Add(new Reservering() {
Id = Convert.ToInt32(reader["boekingid"]),
Naam = Convert.ToString(reader["naam"]),
Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]),
Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]),
Hotel = Convert.ToInt32(reader["hotelid"]),
Aantal_personen = Convert.ToInt32(reader["aantal_gasten"])
});
}
public static List<Reservering> GetReserverings()
{
List<Reserving> reservings = new List<Reservings>();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
Reservering res = new Reservering();
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
reservings.add(res);
}
reader.Close();
}
return reservings;
}

Repeated values in list in while(reader.Read()) {}

[WebMethod]
public List<reports> getMyReports( int user_id )
{
string cs = ConfigurationManager.ConnectionStrings["ReportDB"].ConnectionString;
using (SqlConnection con = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand("getAllReportsByUserID", con);
cmd.CommandType = CommandType.StoredProcedure;
List<reports> repers = new List<reports>();
//users[][] liser = new users[][];
SqlParameter user_id_parameter = new SqlParameter("#user_id", user_id);
cmd.Parameters.Add(user_id_parameter);
reports report = new reports();
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
report.id = Convert.ToInt32(reader["id"]);
report.title = reader["title"].ToString();
report.description = reader["description"].ToString();
report.anonymous = (bool)reader["anonymous"];
report.location = reader["location"].ToString();
report.status = reader["status"].ToString();
report.category = reader["category"].ToString();
report.date = (DateTime)reader["date"];
report.picture_url = reader["picture_url"].ToString();
report.admin_id = Convert.ToInt32(reader["admin_id"]);
repers.Add(report);
}
return repers;
}
}
I have the top function that calls the following stored procedure:
CREATE Proc [dbo].[getAllReportsByUserID]
#user_id int
as
Begin
Select
id,
title,
description,
anonymous,
location,
status,
category,
date,
picture_url,
admin_id
from reports
where user_id = #user_id
End
I have tested the procedure individually and it works fine. Yet, when I test the WebService created above I get a list with the last value duplicated along the whole list.
Can someone please help me figure out why do I get the same (last)value repeated over and over again?
By creating the report object before the loop and reusing it repeatedly, you insert a reference to that same object multiple times in your list.
You should create the report object inside your loop:
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
reports report = new reports();
report.id = Convert.ToInt32(reader["id"]);
report.title = reader["title"].ToString();
report.description = reader["description"].ToString();
report.anonymous = (bool)reader["anonymous"];
report.location = reader["location"].ToString();
report.status = reader["status"].ToString();
report.category = reader["category"].ToString();
report.date = (DateTime)reader["date"];
report.picture_url = reader["picture_url"].ToString();
report.admin_id = Convert.ToInt32(reader["admin_id"]);
repers.Add(report);
}
return repers;

Oracle stored procedure works with ADO.NET but with OrmLite throws exception?

I have a following stored procedure:
create or replace PROCEDURE PRODUCT_DETAILS(p_code IN VARCHAR2,
cursorParam OUT SYS_REFCURSOR)
IS
BEGIN
OPEN cursorParam FOR
select str_auth_code, str_name
from strs
where str_auth_code = p_code;
END;
How can I call it with OrmLite? I've tryied:
connection.SqlList<Product>(#"EXEC PRODUCT_DETAILS #p_code", new { p_code = code });
but it throws an exception ORA-01036: illegal variable name/number
I just tried to do it with plain old ADO.NET and it worked:
using (var conn = new OracleConnection(connectionString))
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "PRODUCT_DETAILS";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("p_code", OracleType.NVarChar).Value = redemptionCode;
cmd.Parameters.Add("cursorParam", OracleType.Cursor);
cmd.Parameters["cursorParam"].Direction = ParameterDirection.Output;
conn.Open();
OracleDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Console.WriteLine(dr["Name"]);
}
conn.Close();
}
But I can't figure out how to do the same task with OrmLite.
What you have looks good. If you were concerned about the verbosity of the code, and were using a number of stored procedures, then you could use this extension method to remove some of the repeated code:
Extension Method:
public static class StoredProcExtensions
{
public static List<T> ExecStoredProcedure<T>(this IDbConnection connection, string procedureName, object parameters = null, string outputCursor = "cursorParam")
{
return connection.Exec(c => {
c.CommandText = procedureName;
c.CommandType = CommandType.StoredProcedure;
// Create the parameters from the parameters object
if(parameters != null)
foreach(var property in parameters.GetType().GetPublicProperties())
c.Parameters.Add(new OracleParameter(property.Name, property.GetValue(parameters)));
// Add the output cursor
if(outputCursor != null)
c.Parameters.Add(new OracleParameter(outputCursor, OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
// Return the result list
return c.ExecuteReader().ConvertToList<T>();
});
}
}
Usage:
var download = connection.ExecStoredProcedure<ProductDownloads>(
"PRODUCT_DETAILS",
new { p_code = redemptionCode }
);
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
So the arguments are:
Stored procedure name i.e. PRODUCT_DETAILS
Optional An object of input parameters i.e new { p_code = redemptionCode, other = "value" }
Optional The name of the output cursor - defaults to cursorParam
Note: this code is untested, because I don't have Oracle setup, but it does compile, and hopefully goes some way to simplifying your stored procedures.
So far ended up with following code:
using (var connection = factory.Open())
{
var download =
connection.Exec(c =>
{
c.CommandText = "PRODUCT_DETAILS";
c.CommandType = CommandType.StoredProcedure;
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("p_code", Oracle.DataAccess.Client.OracleDbType.NVarchar2) { Value = redemptionCode });
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("cursorParam", Oracle.DataAccess.Client.OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
return c.ExecuteReader().ConvertToList<ProductDownloads>();
});
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
}
But I think there should be a better way for doing this.
Late to the party, but this problem can be solved by simply adding BEGIN and END to the statement... it will work.
In my case, I was trying to refresh a materialized view, only after adding BEGIN and END will it work, otherwise it will throw OracleException ORA-00900: invalid SQL statement...
This should work:
db.ExecuteSql("BEGIN DBMS_SNAPSHOT.REFRESH('" + materializedViewName + "'); END;");

how to check if stored procedure exists or not in sql server using c# code

I tried below code for cheking SP is alredy exist or not. if not exist i am creating..
But every time it is showing sp is not created.....But my database already have this sp.
Let me know where i am doing mistake.
string checkSP = String.Format(
"IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false'",
"GP_SOP_AdjustTax");
SqlCommand command = new SqlCommand(checkSP, myConnection);
command.CommandType = CommandType.Text;
if (myConnection == null || myConnection.State == ConnectionState.Closed)
{
try
{
myConnection.Open();
}
catch (Exception a)
{
MessageBox.Show("Error " + a.Message);
}
}
bool Exist = false;
Exist = Convert.ToBoolean(command.ExecuteScalar());
if (Exist == false) //false : SP does not exist
{
// here i am writing code for creating SP
}
Try:
if exists(select * from sys.objects where type = 'p' and name = '<procedure name>' )
Also you can check that with c#:
string connString = "";
string query = "select * from sysobjects where type='P' and name='MyStoredProcedureName'";
bool spExists = false;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query, conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
spExists = true;
break;
}
}
}
}
For those who use Entity Framework and a DbContext:
create an extension class for DbContext:
internal static class DbContextExtensions
{
public static bool StoredProcedureExists(this DbContext context,
string procedureName)
{
string query = String.Format(
#"select top 1 from sys.procedures " +
"where [type_desc] = '{0}'", procedureName);
return dbContext.Database.SqlQuery<string>(query).Any();
}
}
As robIII remarked, this code should not be published to the outside world as it makes the database vulnerable for hackers (thank you RobIII!). To prevent this use a parameterized statement. The problem with the above mentioned method is described here
The solution is to put procedureName as a parameter in an SQL statement. SQL will check if the string parameter has the desired format, thus inhibiting malicious calls:
public static bool ImprovedExists(this DbContext dbContext, string procedureName)
{
object[] functionParameters = new object[]
{
new SqlParameter(#"procedurename", procedureName),
};
const string query = #"select [name] from sys.procedures where name= #procedurename";
return dbContext.Database.SqlQuery<string>(query, functionParameters).Any();
}
I found this on MSDN
select * from sys.objects where type_desc = 'SQL_STORED_PROCEDURE' AND name = 'Sql_PersonInsert'
Try:
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'GP_SOP_AdjustTax') AND type in (N'P', N'PC')
My stab at it:
Reusable extension method
Minimal Sql / Minimal C#
Called from .Net as the OP implicitly requested
Could be faster because of the object_id function
public static bool StoredProcedureExists(this string source)
{
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
conn.Open();
using (var cmd = new SqlCommand($"select object_id('{source}')", conn))
return !cmd.ExecuteScalar().ToString().IsNullOrWhiteSpace();
}
}
You can check with following tsql query (suitable for SQL Server):
select * from sysobjects where ytype='P' and name='MyStoredProcedureName'
If query returns row then stored procedure named 'MyStoredProcedureName' exists.
And here is how you can use it in code:
//TODO: set connection string
string connString = "";
string query = "select * from sysobjects where ytype='P' and name='MyStoredProcedureName'";
bool spExists = false;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query,conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
spExists = true;
break;
}
}
}
}
string checkSP = String.Format(
"IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false'",
"GP_SOP_AdjustTax");
is fine if you change the 'U' to 'P'.
With 'U' you query for user-tables, where 'P' gives you stored-procedures.
private static bool StoredProcedureExists(string sp)
{
var connString = #"<your string here>";
var query = string.Format("SELECT COUNT(0) FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = '{0}'", sp);
using (var conn = new SqlConnection(connString))
{
conn.Open();
using (var cmd = new SqlCommand(query, conn))
{
return Convert.ToInt32(cmd.ExecuteScalar()) > 0;
}
}
}
Handles procedure names with different schemas
Names with and without brackets ([])
Uses parameter to avoid SQL injection
Note: Caller owns SQL Connection
public static class SqlConnectionExtensions
{
public static Task<bool> StoredProcedureExistsAsync(this SqlConnection sqlConnection, string storedProcedureName)
{
string query = "SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#storedProcedureName) AND type in (N'P', N'PC')";
using (SqlCommand command = new SqlCommand(query, sqlConnection))
{
command.Parameters.AddWithValue("#storedProcedureName", storedProcedureName);
using (SqlDataReader reader = command.ExecuteReader())
{
return reader.ReadAsync();
}
}
}
}
The following works with MySQL, SQL Server, and Oracle I think:
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE='PROCEDURE'
AND (ROUTINE_SCHEMA='questionnaire' OR ROUTINE_CATALOG = 'questionnaire')
AND SPECIFIC_NAME='create_question';
Usage:
string procedureName = "create_question";
using (DbConnection conn = new SqlConnection("Server=localhost;Database=questionnaire;Trusted_Connection=True;")) // Connection is interchangeable
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = $"SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE' AND (ROUTINE_SCHEMA='{conn.Database}' OR ROUTINE_CATALOG = '{conn.Database}') AND SPECIFIC_NAME='{procedureName}';";
return cmd.ExecuteScalar() != null;
}
}
If you use Microsoft.SqlServer.Management.Smo, try
private static bool CheckIfStoredProcedureExists(Database db, string spName, string schema)
{
db.StoredProcedures.Refresh();
return (db.StoredProcedures[spName, schema] != null);
}
Try this;
if object_id('YourStoredProcedureName') is null
exec ('create procedure dbo.YourSp as select 1')
go
alter procedure dbo.YourStoredProcedure
as

Making a Webservice (asp.NET) which accepts several Paramters seperated by comma

I have the following Webservice and SQL-Query:
public class DIENSTLEISTUNG
{
public string DienstleistungName { get; set; }
public string DienstleistungArtBezeichnung { get; set; }
}
internal static List<DIENSTLEISTUNG> Dienstleistung(string RAUM_ID)
{
List<DIENSTLEISTUNG> BestuhlungObject = new List<DIENSTLEISTUNG>();
using (SqlConnection con = new SqlConnection(#"Data Source=Localhost\SQLEXPRESS;Initial Catalog=BOOK-IT-V2;Integrated Security=true;"))
using (SqlCommand cmd = new SqlCommand(#"
SELECT
DISTINCT dl.NAME AS NAME, da.BEZEICHNUNG AS BEZEICHNUNG
FROM RAUM r
RIGHT JOIN DIENSTLEISTUNG_Raum bs ON bs.RAUM_ID = (" + Room_ID + ")
RIGHT JOIN DIENSTLEISTUNG ba ON bs.DIENSTLEISTUNG_ID = ba.ID
RIGHT JOIN DIENSTLEISTUNGSART da ON ba.DIENSTLEISTUNGSART_ID = da.ID
RIGHT JOIN DIENSTLEISTER dl ON da.DIENSTLEISTER_ID = dl.ID", con))
{
con.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
if (rdr["NAME"] != DBNull.Value && rdr["BEZEICHNUNG"] != DBNull.Value)
{
BestuhlungObject.Add(new DIENSTLEISTUNG()
{
DienstleistungName = rdr["NAME"].ToString(),
DienstleistungArtBezeichnung = rdr["BEZEICHNUNG"].ToString()
});
}
}
}
}
return BestuhlungObject;
}
}
}
This Webservice is working like charm. I can send one Room_ID = 219 and get the expected output. My Question is, how can i send more then one Room_ID seperated by comma. For Example: Room_ID = 219, 220, 221
Can someone please help me with that?
Thanks in advance
See T-SQL stored procedure that accepts multiple Id values
One of many ways (MS SQL 2005+), if you can pass parameters as xml:
var sql =
#"SET ARITHABORT ON;
SELECT ...
RIGHT JOIN DIENSTLEISTUNG_Raum bs ON bs.RAUM_ID IN
(
SELECT tbl.col.value('.', 'varchar(20)')
FROM #xml.nodes('id') as tbl(col)
) ...";
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.Parameters.Add("#xml", SqlDbType.Xml).Value = "<id>111</id><id>222</id><id>333</id>";
Or using LIKE if you can pass parameters as ,aaa,bbb,ccc, (note the order of LIKE arguments to avoid an injection)
var sql =
#"SELECT ...
RIGHT JOIN DIENSTLEISTUNG_Raum bs ON #ids LIKE '%,' + bs.RAUM_ID + ',%'
...";
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.Parameters.Add("#ids", SqlDbType.VarChar).Value = ",111,222,333,";

Categories

Resources