Must declare the scalar variable in Select command - c#

I am passing a long list of employeeIds to employeeIdlist and I split them into a List. Using this list I am adding parameters to my query.
I am getting the following error
{"Must declare the scalar variable \"#EmployeeId\"."}
public List<versionInfo> GetVersion(string employeeIdlist)
{
DbHelper helper = new DbHelper();
List<versionInfo> empVerInfo = new List<versionInfo>();
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand getVersion = new SqlCommand())
{
getVersion.Connection = conn;
getVersion.CommandText = #"SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (#EmployeeId)";
getVersion.CommandType = CommandType.Text;
List<int> empIds = employeeIdlist.Split(',').Select(int.Parse).ToList();
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (var emp in empIds)
{
// IN clause
sb.Append("#EmployeeId" + i.ToString() + ",");
// parameter
getVersion.Parameters.AddWithValue("#EmployeeId" + i.ToString(), emp);
i++;
}
// getVersion.Parameters.AddWithValue("#EmployeeId", employeeIdlist);
SqlDataReader rdr = getVersion.ExecuteReader();
while (rdr.Read())
{
versionInfo vi = new versionInfo();
vi.employeeId = helper.GetDb<int>(rdr, "EmployeeId");
vi.version = helper.GetDb<decimal>(rdr, "Version");
empVerInfo.Add(vi);
}
rdr.Close();
}
conn.Close();
}
return empVerInfo;
}

Remove the text after the IN
getVersion.CommandText = #"SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (";
then the foreach could build the full list of parameters and texts
foreach (var emp in empIds)
{
sb.Append("#EmployeeId" + i.ToString() + ",");
getVersion.Parameters.AddWithValue("#EmployeeId" + i.ToString(), emp);
i++;
}
after exiting the loop remove the last comma from the StringBuilder
sb.Length--;
finally, complete the command text appending the content of the StringBuilder and do not forget the closing parenthesys for the IN clause.
getVersion.CommandText += sb.ToString() + ")";
Now you can run the command with the correct IN clause and a matching list of parameters

If fails because your string query has one parameter named #EmployeeId and your Command object has many parameters with different names ("#EmployeeId1" is not equal to "#EmployeeId")
It seems like you are trying to apply this approach, which is a good idea.
You are two lines away of getting it to work:
Add this lines:
sb.Lenght--;
getVersion.CommandText = getVersion.CommandText.Replace("#EmployeeId",sb.ToString())
just before:
SqlDataReader rdr = getVersion.ExecuteReader();
After doing that your added parameters will match those #parameters existing in the sql string.

This is just another option. You can achieve the same result in 3 lines of code using Dapper ORM used in Stack Overflow.
You can download via NuGet.
public class VersionInfo
{
public int EmployeeId { get; set; }
public decimal Version { get; set; }
}
class Program
{
public static string connString = "...";
static void Main(string[] args)
{
var result = GetVersion(new List<int> {1, 2});
Console.ReadLine();
}
public static List<VersionInfo> GetVersion(IList<int> employeeIds)
{
using (IDbConnection conn = new SqlConnection(connString))
{
conn.Open();
var entities = conn.Query<VersionInfo>(
#"SELECT EmployeeId, Version from EmployeeVersion WHERE EmployeeId IN #EmployeeIds",
new {EmployeeIds = employeeIds});
return entities.ToList();
}
}
}

On your select statement you have to declare a value for your variable. I have made it an Integer. If it is a text value, then you can use varchar(25).
#"DECLARE #EmployeeId INT
SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (#EmployeeId)";

Related

How to retrieve blob data from MySql table and return it as Json format?

I have a table with three columns
MyDate : DateiIme
MyBlob: blob
Id: String
I want to return MyDate and MyBlob as Json. There can be multiple records in the table.
public class MyData
{
public string? MyDate { get; set; };
public string? MyBlob { get; set; };
}
public async Task<string> GetQueryResult(string Id)
{
MyData data = new MyData();
List<MyData> MyList = new List<MyData>();
string sqlSelect = string.Format("Select MyDate, MyBlob from MyTablee WHERE Id = '{0}'", Id);
try
{
MySqlCommand sqlcmd = new MySqlCommand();
MySqlConnection connetcion = new MySqlConnection(connectionString);
sqlcmd.Connection = connetcion;
sqlcmd.CommandTimeout = 0;
sqlcmd.CommandType = CommandType.Text;
sqlcmd.CommandText = sqlSelect;
connetcion.Open();
using (connetcion)
{
int count = 0;
using (MySqlDataReader reader = sqlcmd.ExecuteReader())
{
while (reader.Read())
{
data.MyDate = reader.GetString(0);
data.MyBlob = reader.GetString(1);
MyList.Add(data);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
var json = JsonSerializer.Serialize(results);
return json;
}
The output in the Postman is:
"{\"MyDate\":\"1/30/2023 9:16:40 PM\",\"MyBlob\":\"#MyBlob\"}"
I am not sure the blob data to JSON conversion is correct. Thank you.
It's probable that your table doesn't contain any BLOB data, but instead contains the literal string #MyBlob in that column.
The cause of this would be using a SQL statement like INSERT INTO MyTablee(MyDate, MyBlob) VALUES(NOW, '#MyBlob');, which inserts the literal text #MyBlob.
Make sure your insert code is constructed as follows:
using var command = connection.CreateCommand();
command.CommandText = "INSERT INTO MyTablee(MyDate, MyBlob) VALUES(NOW(), #blob);";
command.Parameters.AddWithValue("#blob", yourBlobDataHere);
command.ExecuteNonQuery();
In particular, there are no quotes around the parameter name in the INSERT statement, and command parameters are being used to send values, instead of string concatenation.

SQL query on ADO.net limitation with 2100+ parameters

I am trying to implement an ADO.NET code which executes the SQL query with multiple parameters. Looks like SQL parameter limit is 2100 and does not accept more than this limit. How do I achieve with my below code to have this accept more than the limitation.
I am finding it difficult to understand the implementations when validating online articles related how to send the queries in subsets or chunks to fulfill my request.
This is my code:
using (Connection = new SqlConnection(CS))
{
Connection.Open();
string query = "SELECT FamilyID, FullName, Alias FROM TABLE (nolock) WHERE FamilyID IN ({0})";
var stringBuiler = new StringBuilder();
var familyIds = new List<string>();
string line;
while ((line = TextFileReader.ReadLine()) != null)
{
line = line.Trim();
if (!familyIds.Contains(line) & !string.IsNullOrEmpty(line))
{
familyIds.Add(line);
}
}
var sqlCommand = new SqlCommand
{
Connection = Connection,
CommandType = CommandType.Text
};
var index = 0; // Reset the index
var idParameterList = new List<string>();
foreach (var familyId in familyIds)
{
var paramName = "#familyId" + index;
sqlCommand.Parameters.AddWithValue(paramName, familyId);
idParameterList.Add(paramName);
index++;
}
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
var dt = new DataTable();
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
{
dt.Load(sqlReader);
}
try
{
if (dt.Rows.Count > 0)
{
OutputdataGridView.DataSource = lstDownloadOwnerOutput;
OutputdataGridView.ColumnHeadersDefaultCellStyle.Font = new Font(DataGridView.DefaultFont, FontStyle.Bold);
OutputdataGridView.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
Gridviewdisplaylabel.Text = "Total no of rows: " + this.OutputdataGridView.Rows.Count.ToString();
}
else if (dt.Rows.Count == 0)
{
MessageBox.Show("Data returned blank!!!");
}
}
catch (Exception Ex)
{
if (Connection != null)
{
Connection.Close();
}
MessageBox.Show(Ex.Message);
}
}
Having a WHERE IN clause with 2100, or even 100, parameters is generally not good coding practice. You might want to consider putting those values into a separate bona fide table, e.g.
families (ID int PK, ...)
Then, you may rewrite your query as:
SELECT FamilyID, FullName, Alias
FROM TABLE (nolock)
WHERE FamilyID IN (SELECT ID FROM families);
You could also express the above using an EXISTS clause or a join, but all three approaches might just optimize to a very similar query plan anyway.
You can just add a table load call every 2000 parameters in your code:
var index = 0; // Reset the index
var idParameterList = new List<string>();
var dt = new DataTable();
foreach (var familyId in familyIds) {
var paramName = "#familyId" + index;
sqlCommand.Parameters.AddWithValue(paramName, familyId);
idParameterList.Add(paramName);
index++;
if (index > 2000) {
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
dt.Load(sqlReader);
sqlCommand.Parameters.Clear();
idParameterList.Clear();
index = 0;
}
}
if (index > 0) {
sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList));
using (SqlDataReader sqlReader = sqlCommand.ExecuteReader())
dt.Load(sqlReader);
}
For dynamic sql like this, I generally recommend using a Table-Valued Parameter.
It does require a bit of setup: you have to create a user-defined Type in the DB to hold the values, but that is a fairly trivial operation:
CREATE TYPE PrimaryKeyType AS TABLE ( VALUE INT NOT NULL );
We generally use these in conjunction with stored procedures:
CREATE PROCEDURE dbo.getFamily(#PrimaryKeys PrimaryKeyType READONLY)
AS
SELECT FamilyID, FullName, Alias
FROM TABLE (nolock) INNER JOIN #PrimaryKeys ON TABLE.FamilyID = #PrimaryKeys.Value
GO
However, you can also use inline SQL if you prefer.
Assigning the values to the stored proc or inline parameter is fairly straightforward, but there is one gotcha (more later):
public static void AssignValuesToPKTableTypeParameter(DbParameter parameter, ICollection<int> primaryKeys)
{
// Exceptions are handled by the caller
var sqlParameter = parameter as SqlParameter;
if (sqlParameter != null && sqlParameter.SqlDbType == SqlDbType.Structured)
{
// The type name may look like DatabaseName.dbo.PrimaryKeyType,
// so remove the database name if it is present
var parts = sqlParameter.TypeName.Split('.');
if (parts.Length == 3)
{
sqlParameter.TypeName = parts[1] + "." + parts[2];
}
}
if (primaryKeys == null)
{
primaryKeys = new List<int>();
}
var table = new DataTable();
table.Columns.Add("Value", typeof(int));
foreach (var wPrimaryKey in primaryKeys)
{
table.Rows.Add(wPrimaryKey);
}
parameter.Value = table;
}
The thing to watch out for here is the naming of the parameter. See the code in the method above that removes the database name to resolve this issue.
If you have dynamic SQL, you can generate a correct parameter using the following method:
public static SqlParameter CreateTableValuedParameter(string typeName, string parameterName)
{
// Exceptions are handled by the caller
var oParameter = new SqlParameter();
oParameter.ParameterName = parameterName;
oParameter.SqlDbType = SqlDbType.Structured;
oParameter.TypeName = typeName;
return oParameter;
}
Where typeName is the name of your type in the DB.

Output contents of column from MSSQL in c#

Trying to output all of the contents of a column from a MSSQL database into an array and at the moment I am getting this as an output:
ConsoleApplication3.Program+ClassName
ConsoleApplication3.Program+ClassName
if I add an extra row to the column then there will be three of the same thing output. The code that is handling all of this is below.
public class ClassName
{
public string Col1 { get; set; }
}
static void Main(string[] args)
{
try
{
SqlConnection con = new SqlConnection("Data Source=;Network Library=DBMSSOCN; Initial Catalog = Backups; User ID = BackupsU; Password = ; ");
ClassName[] allRecords = null;
string sql = #"SELECT company_Name FROM Company";
using (var command = new SqlCommand(sql, con))
{
con.Open();
using (var reader = command.ExecuteReader())
{
var list = new List<ClassName>();
while (reader.Read())
list.Add(new ClassName { Col1 = reader.GetString(0) });
allRecords = list.ToArray();
}
foreach (var item in allRecords)
{
Console.WriteLine(item.ToString());
}
Console.ReadKey();
Console.WriteLine();
}
You need to reference the Col1 property on each instance of your ClassName class
foreach (var item in allRecords)
{
Console.WriteLine(item.Col1);
}
Without that, you are just calling Object.ToString() which unless overridden, will just return the full name of your class.
You need to either override ClassName.ToString() to return ClassName.Col1, or just use item.Col1 in your Console.Writeline statement.
Since .ToString is not overriden it falls back to Object.ToString(), which outputs the class name.

SQL query return List

I can't seem to get this working:
My table column headers are 'genre' 'artist' 'album'
and the params I'm passing in are (type, filter, value) ("artist", "genre", "Rock") where there are two rows in the db with "Rock" for the genre.
When I follow the debugger, the 'while (reader.Read())' must return false because the loop is never entered and thus nothing written to the List.
public static List<String> getWithFilter(String type, String filter, String value)
{
List<String> columnData = new List<String>();
string query = "SELECT #type FROM Music WHERE" +
" #filter = '#value'";
SqlConnection connection = Database.GetConnection();
SqlCommand getData = new SqlCommand(query, connection);
getData.Parameters.AddWithValue("#type", type);
getData.Parameters.AddWithValue("#filter", filter);
getData.Parameters.AddWithValue("#value", value);
connection.Open();
using (connection)
{
using (getData)
{
using (SqlDataReader reader = getData.ExecuteReader())
{
while (reader.Read())
{
columnData.Add(reader.GetString(0));
}
}
}
}
return columnData;
}
You cannot use parameters for the names of columns and you don't put quotes around them when using them. Right now your query is the equivalent of
SELECT 'artist' FROM Music WHERE 'genre' = '#value'
You can do the following instead.
string query = "SELECT " + type + " FROM Music WHERE " + filter + " = #value";
And just remove the lines that create the #type and #fitler parameters.
You're looking either for formatting or string interpolation (requires C# 6.0):
string query =
$#"SELECT {type}
FROM Music
WHERE {filter} = #value";
...
getData.Parameters.AddWithValue("#value", value);
Formatting is a bit more wordy:
string query = String.Format(
#"SELECT {0}
FROM Music
WHERE {1} = #value", type, filter);
I assuming that you're using .net 2
DateTime current = DateTime.Now;
Console.WriteLine(current);
SqlConnection conn = new SqlConnection();
string q = "SELECT #field FROM student";
SqlDataAdapter da = new SqlDataAdapter(q, conn);
da.SelectCommand.Parameters.AddWithValue("#field", "snName");
DataTable dt = new System.Data.DataTable();
conn.Open();
da.Fill(dt);
conn.Close();
List<string> names = new List<string>();
foreach (DataRow dr in dt.Rows)
{
names.Add(dr[0].ToString());
}
Console.WriteLine("Fetching {0} data for {1}", names.Count, DateTime.Now - current);
Console.ReadKey();
You can use lambda expression to mapping the datatable in .net >4

SQL command not properly ended error while filtering gridview on checkboxlist select

I am trying to filter my gridview on the basis of checkboxlist selected.
So here is what I tried
string constr = ConfigurationManager.ConnectionStrings["OracleConn"].ConnectionString;
string strQuery = "select sr_no, type, stage, ref_no, ref_date, party_name, amount, remarks, exp_type, " +
"voucher_no, cheque_no,cheque_dt, chq_favr_name from XXCUS.XXACL_PN_EXPENSE_INFO";
string condition = string.Empty;
foreach (ListItem li in ddlStatus.Items)
{
condition += li.Selected ? string.Format("'{0}',", li.Value) : string.Empty;
if (!string.IsNullOrEmpty(condition))
{
condition += string.Format(" Where type IN ({0})", condition.Substring(0, condition.Length - 1));
}
using (OracleConnection conn = new OracleConnection(constr))
{
using (OracleCommand cmd = new OracleCommand(strQuery + condition))
{
using (OracleDataAdapter sda = new OracleDataAdapter(cmd))
{
cmd.Connection = conn;
using (DataTable dtcheck = new DataTable())
{
sda.Fill(dtcheck);
GridExpInfo.DataSource = dtcheck;
GridExpInfo.DataBind();
}
}
}
}
but I am getting error as
ORA-00933: SQL command not properly ended
My debugged query is
select sr_no, type, stage, ref_no, ref_date, party_name, amount, remarks, exp_type, voucher_no, cheque_no,cheque_dt, chq_favr_name
from XXCUS.XXACL_PN_EXPENSE_INFO'10',
Where type IN ('10')
I took reference from here
You should prepare the where condition using a List of strings and run the query after you have completed the query builder phase.
// Create a list of your conditions to put inside the IN statement
List<string>conditions = new List<string>();
foreach (ListItem li in ddlStatus.Items)
{
if(li.Selected)
conditions.Add($"'{li.Value}'");
}
// Now build the where condition used
string whereCondition = string.Empty;
if (condition.Count > 0)
{
// This produces something like "WHERE type in ('10', '11', '12')"
// The values for the IN are directly concatenated together
whereCondition = " Where type IN (" + string.Join(",", conditions) + ")";
}
using (OracleConnection conn = new OracleConnection(constr))
{
using (OracleCommand cmd = new OracleCommand(strQuery + whereCondition))
{
....
Consider that this approach is very exposed to Sql Injection attacks and to a parsing problems if your selected values are strings containing quotes
Instead if you start using parameters then your code should be changed to
int count = 1;
List<OracleParameter> parameters = new List<OracleParameter>();
List<string>conditions = new List<string>();
foreach (ListItem li in ddlStatus.Items)
{
if(li.Selected)
{
// parameters are named :p1, :p2, :p3 etc...
conditions.Add($":p{count}");
// Prepare a parameter with the same name of its placeholder and
// with the exact datatype expected by the column Type, assign to
// it the value and add the parameter to the list
OracleParameter p = new OracleParameter($":p{count}",OracleType.NVarChar);
p.Value = li.Value;
parameters.Add(p);
count ++;
}
}
if (condition.Count > 0)
{
// This produces something like "WHERE type in (:p1, :p2, :p3)"
// The values are stored before in the parameter list
whereCondition = " Where type IN (" + string.Join(",", conditions) + ")";
}
using (OracleConnection conn = new OracleConnection(constr))
{
using (OracleCommand cmd = new OracleCommand(strQuery + whereCondition))
{
// Add the parameters with the expected names, type and value.
cmd.Parameters.AddRange(parameters.ToArray());
....
It is a little more lines but this prevents Sql Injection and there is no more problem in parsing strings

Categories