C# - System Data SqlClient SqlException - c#

So I am in the midst of debugging a program I have been working on quite sometime that will email the user an email that will the user an email that contains information on an error a client is having trying to get into a mobile app. The problem that I am having is even though I have set the parameter, I am getting the following error message:
The parameterized query '(#USER_NAME varchar(8000))SELECT USER_NAME, EMAIL, FIRST_NAME, L' expects the parameter '#USER_NAME', which was not supplied.
I have never encountered this error before so I am not sure if there is something I am missing in my code or if I just put in the wrong syntax. Below is both the method where the error is happening in and the class that this method uses:
Method:
private static MyHomeInformation GetUserDataFromMyHome(string username)
{
MyHomeInformation myHomeInformation = null;
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MyHomeConnectionString))
{
SqlCommand sqlError = connection.CreateCommand();
sqlError.CommandText = #"SELECT USER_NAME, EMAIL, FIRST_NAME, LAST_NAME, TRAVELER_UID FROM TANDT_PORTAL.dbo.[USER] WHERE USER_NAME = #USER_NAME";
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar);
connection.Open();
SqlDataReader reader = sqlError.ExecuteReader();
if (reader.Read())
{
myHomeInformation = new MyHomeInformation();
myHomeInformation.myHomeUserName = Utilities.FromDBValue<string>(reader["USER_NAME"]);
myHomeInformation.myHomeEmail = Utilities.FromDBValue<string>(reader["EMAIL"]);
myHomeInformation.myHomeFirstName = Utilities.FromDBValue<string>(reader["FIRST_NAME"]);
myHomeInformation.myHomeLastName = Utilities.FromDBValue<string>(reader["LAST_NAME"]);
myHomeInformation.myHomeTravelerUID = Utilities.FromDBValue<Guid>(reader["TRAVELER_UID"]);
}
}
return myHomeInformation;
}
Class:
class MyHomeInformation
{
public string myHomeUserName { get; set; }
public string myHomeEmail { get; set; }
public string myHomeFirstName { get; set; }
public string myHomeLastName { get; set; }
public Guid myHomeTravelerUID { get; set; }
}

Change the sqlError.Parameters.Add to AddWithValue
private static MyHomeInformation GetUserDataFromMyHome(string username)
{
MyHomeInformation myHomeInformation = null;
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MyHomeConnectionString))
{
SqlCommand sqlError = connection.CreateCommand();
sqlError.CommandText = #"SELECT USER_NAME, EMAIL, FIRST_NAME, LAST_NAME, TRAVELER_UID FROM TANDT_PORTAL.dbo.[USER] WHERE USER_NAME = #USER_NAME";
sqlError.Parameters.AddWithValue("#USER_NAME", username); // THIS ROW SHOULD BE UPDATED
connection.Open();
SqlDataReader reader = sqlError.ExecuteReader();
if (reader.Read())
{
myHomeInformation = new MyHomeInformation();
myHomeInformation.myHomeUserName = Utilities.FromDBValue<string>(reader["USER_NAME"]);
myHomeInformation.myHomeEmail = Utilities.FromDBValue<string>(reader["EMAIL"]);
myHomeInformation.myHomeFirstName = Utilities.FromDBValue<string>(reader["FIRST_NAME"]);
myHomeInformation.myHomeLastName = Utilities.FromDBValue<string>(reader["LAST_NAME"]);
myHomeInformation.myHomeTravelerUID = Utilities.FromDBValue<Guid>(reader["TRAVELER_UID"]);
}
}
return myHomeInformation;
}

Here is an example of the correct way to use the parameters collection add function:
SqlParameter param = new SqlParameter("#USER_NAME", SqlDbType.NVarChar, 8000);
param.Value = "Sam";
sqlError.Parameters.Add(param);
Note the setting of the value to Sam.
You could also use the function AddWithValue if you don't want to create the param variable. (However, #JoelCoehoorn will be worried about performance issues - see question comments)

You add your parameter to the call, but give the parameter no value.
change
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar);
to
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar).value = username;

Related

How to get data in INT from SQLDataReader in .NET

I am trying to get int value from the database but It is throwing an error
Unable to cast object of type 'System.Byte' to type 'System.Int32'.
In the database, Active field is tinyint.
Also, how to return both values from this method.
private string CheckData(string firstValue, string SecondValue, int Active)
{
string Data = "";
StringBuilder sb = new StringBuilder();
string query = #"select M.ident Mi, mmp.active Active
from Iden.Iden M
inner join PtM.MPt MMP on MMP.mPat = M.id
where M.ident = 'firstValue'
and Mi.ident = 'SecondValue'";
sb.Append(query);
sb.Replace("firstValue", firstValue);
sb.Replace("SecondValue", SecondValue);
SqlConnection connection = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sb.ToString());
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
try
{
connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Data = reader.GetString(reader.GetOrdinal("Mi"));
Active = reader.GetInt32(reader.GetOrdinal("Active"));
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
finally
{
connection.Close();
connection.Dispose();
}
return Data;
}
Here's a stab at it. I can't debug it (since I don't feel like creating a database).
First I create a type to hold the results. You could just use a Tuple, but this seems clearer:
public class DataActive
{
public string Data { get; set; }
public byte Active { get; set; }
}
I make your function return a collection of these - it's not obvious from your code that there is only one.
You'll also notice that I use SqlParameters to add firstValue and secondValue to your query. Look up SQL Injection (and Little Bobby Tables).
If you are using a recent version of C# (which I don't), there's a new syntax for using that requires less indenting. The using statements stick a call to Dispose in a finally statement at the end of the block. Also note that I'm disposing the SqlCommand and the DataReader
public static IEnumerable<DataActive> CheckData(string firstValue, string secondValue)
{
var results = new List<DataActive>();
const string query = #"select M.ident Mi,mmp.active Active from Iden.Iden M
Inner join PtM.MPt MMP on MMP.mPat =M.id
where M.ident = #firstValue and Mi.ident = #secondValue";
using (var connection = new SqlConnection(connString))
{
using (var cmd = new SqlCommand(query))
{
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("#firstValue", SqlDbType.NVarChar, 50).Value = firstValue;
cmd.Parameters.Add("#secondValue", SqlDbType.NVarChar, 50).Value = secondValue;
try
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
var dataOrdinal = reader.GetOrdinal("Mi");
var activeOrdinal = reader.GetOrdinal("Active");
if (reader.HasRows)
{
while (reader.Read())
{
results.Add(new DataActive
{
Data = reader.GetString(dataOrdinal),
Active = reader.GetByte(activeOrdinal),
});
}
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
}
}
return results;
}
If your TINY_INT Active represents a boolean value, figure out what the rule is, and do a conversion after you get the value using reader.GetByte.
One final note, it's often better to log ex.ToString() rather than ex.Message. You get the message and the stack that way.

Invalid column name while using C# for SQL Server

This is my code in C#. I am just trying to add data to my table in the database. However, I have been having this issue for only ages. It says:
invalid column name.
Fotograf database is the only database I have and table ODEV1 is the only table I created. When I edit data through SQL Server there is not an issue, but when I try it by using Visual Studio C# I have issues.
Any help appreciated thank you!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace SQL_ORNEK2
{
class Program
{
static void Main(string[] args)
{
SqlConnection baglanti = new SqlConnection();
baglanti.ConnectionString = "Server=LAPTOP-K3JLTUR0; database=Fotograf; integrated security=True";
SqlCommand komut = new SqlCommand();
komut.Connection = baglanti;
string name;
name = Console.ReadLine().ToString();
string surname;
surname = Console.ReadLine().ToString();
int age;
age = Convert.ToInt32(Console.ReadLine());
string job;
job = Console.ReadLine().ToString();
komut.CommandText = "INSERT INTO dbo.ODEV1 VALUES('name', 'surname', age, 'job')";
baglanti.Open();
int sonuc = komut.ExecuteNonQuery();
Console.WriteLine(sonuc);
Console.ReadKey();
baglanti.Close();
}
}
}
Your insert statement is incorrect. You're using the list of columns in place of the values to insert. It should look something like this:
insert into dbo.ODEV1 (name, surname, age, job) values ('Harold', 'Green', 25, 'nerd')
To insert the actual data from the variables you read from user input, you'll want to use SQL parameters:
komut.Parameters.AddWithValue("#name", name);
komut.Parameters.AddWithValue("#surname", surname);
komut.Parameters.AddWithValue("#age", age);
komut.Parameters.AddWithValue("#job", job);
komut.CommandText = "insert into dbo.ODEV1 (name, surname, age, job) values (#name, #surname, #age, #job)";
If you use SSMS (SQL-Server Management Studio which is free) to create your INSERT INTO statement by right clicking the desired table, select "script table as", select "INSERT To" to a new query window we get this (using a table named Customers).
INSERT INTO [dbo].[Customer]
([FirstName]
,[LastName]
,[Address]
,[City]
,[State]
,[ZipCode]
,[AccountNumber]
,[JoinDate])
VALUES
(<FirstName, nvarchar(max),>
,<LastName, nvarchar(max),>
,<Address, nvarchar(max),>
,<City, nvarchar(max),>
,<State, nvarchar(max),>
,<ZipCode, nvarchar(max),>
,<AccountNumber, nvarchar(max),>
,<JoinDate, datetime2(7),>)
Now change the VALUES section by using a DECLARE for each value.
DECLARE #FirstName nvarchar(max)
DECLARE #LastName nvarchar(max)
DECLARE #Address nvarchar(max)
DECLARE #City nvarchar(max)
DECLARE #State nvarchar(max)
DECLARE #ZipCode nvarchar(max)
INSERT INTO Customer (FirstName,LastName,[Address],City,[State],ZipCode) VALUES (#FirstName,#LastName,#Address,#City,#State,#ZipCode)
Next, create a class rather than placing data operations into Program.cs with a method specific to adding a new record (the following still uses Customers table).
Full source where the following code comes from.
An alternate to cmd.Parameters.AddWithValue is cmd.Parameters.Add which provides fine tuning the type of the parameter.
The alternate to getting the new primary key if needed is to add a semi-colon to the end of the INSERT INTO and adding SELECT CAST(scope_identity() AS int); then use Convert.ToInt32(cmd.ExecuteScalar()) to get the new key.
So after testing with SSMS simply paste the query into a string variable and if this does not work there is something else going on.
public bool AddCustomer(string FirstName, string LastName, string Address, string City, string State, string ZipCode, ref int NewPrimaryKeyValue)
{
bool success = false;
using (var cn = new SqlConnection { ConnectionString = ConnectionString })
{
using (var cmd = new SqlCommand { Connection = cn })
{
cmd.CommandText =
"INSERT INTO Customer (FirstName,LastName,[Address],City,[State],ZipCode) " +
"VALUES (#FirstName,#LastName,#Address,#City,#State,#ZipCode)";
try
{
cmd.Parameters.AddWithValue("#FirstName", FirstName);
cmd.Parameters.AddWithValue("#LastName", LastName);
cmd.Parameters.AddWithValue("#Address", Address);
cmd.Parameters.AddWithValue("#City", City);
cmd.Parameters.AddWithValue("#State", State);
cmd.Parameters.AddWithValue("#ZipCode", ZipCode);
cn.Open();
int result = cmd.ExecuteNonQuery();
if (result == 1)
{
cmd.CommandText = "Select ##Identity";
NewPrimaryKeyValue = Convert.ToInt32(cmd.ExecuteScalar());
success = true;
}
}
catch (Exception ex)
{
HasErrors = true;
ExceptionMessage = ex.Message;
NewPrimaryKeyValue = -1;
success = false;
}
}
}
return success;
}
Calling the above method.
You can also validate column names using the following (still keeping with Customer table)
SELECT ORDINAL_POSITION,
COLUMN_NAME,
DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Customer'
AND TABLE_SCHEMA = 'dbo';
Results
1,id,int
2,FirstName,nvarchar
3,LastName,nvarchar
4,Address,nvarchar
5,City,nvarchar
6,State,nvarchar
7,ZipCode,nvarchar
8,AccountNumber,nvarchar
9,JoinDate,datetime2
Edit
Another option is to create a class which represents data to be inserted e.g.
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string AccountNumber { get; set; }
public DateTime? JoinDate { get; set; }
}
Then here we use the values passed. Note, in this version cmd.Parameters.AddWithValue is replaced with cmd.Parameters.Add and the query to get the new primary key is appended after the INSERT INTO separated by a semi-colon.
To call create an instance of the Customer class, populate properties and call the method.
public bool AddCustomer(Customer customer)
{
bool success = false;
using (var cn = new SqlConnection { ConnectionString = ConnectionString })
{
using (var cmd = new SqlCommand { Connection = cn })
{
cmd.CommandText =
"INSERT INTO Customer (FirstName,LastName,[Address],City,[State],ZipCode) " + // insert
"VALUES (#FirstName,#LastName,#Address,#City,#State,#ZipCode);" + // insert
"SELECT CAST(scope_identity() AS int);"; // get new primary key
try
{
cmd.Parameters.Add(new SqlParameter("#FirstName", SqlDbType.NVarChar))
.Value = customer.FirstName;
cmd.Parameters.Add(new SqlParameter("#LastName", SqlDbType.NVarChar))
.Value = customer.LastName;
cmd.Parameters.Add(new SqlParameter("#Address", SqlDbType.NVarChar))
.Value = customer.Address;
cmd.Parameters.Add(new SqlParameter("#City", SqlDbType.NVarChar))
.Value = customer.City;
cmd.Parameters.Add(new SqlParameter("#State", SqlDbType.NVarChar))
.Value = customer.State;
cmd.Parameters.Add(new SqlParameter("#ZipCode", SqlDbType.NVarChar))
.Value = customer.ZipCode;
cn.Open();
customer.Id = Convert.ToInt32(cmd.ExecuteScalar());
success = true;
}
catch (Exception ex)
{
HasErrors = true;
ExceptionMessage = ex.Message;
customer.Id = -1;
success = false;
}
}
}
return success;
}

Can't Get Byte Array from SQL database using Enterprise Library

I'm working on a web API using Enterprise Library 5.0.505 AND I'm having trouble getting a byte array from a database. My insert statement works just fine using DbType.Binary but when I try to return it using SqlStringAccessor it comes up null.
I've tried researching it but most posts use a reader and my project doesn't allow for that route.
My Get method:
public IEnumerable<User> UserSearch(string username)
{
string sql = "SELECT * FROM Users WHERE Username = #Username";
var accessor = Database.CreateSqlStringAccessor<User>(sql, new NamedParameterMapper("#Username"));
return accessor.Execute(username);
}
The properties of my User type:
public int UserId { get; set; }
public string Username { get; set; }
public byte[] SaltedAndHashedPassword { get; set; }
public byte[] Salt { get; set; }
public bool LoggedIn { get; set; }
The Insert code I have that works correctly:
using (DbCommand cmd = Database.GetStoredProcCommand("[HeadCount_Ver01].[dbo].[AddUser]"))
{
Database.AddInParameter(cmd, "#Username", DbType.String, user.Username);
Database.AddInParameter(cmd, "#SaltedAndHashedPassword", DbType.Binary, user.SaltedAndHashedPassword);
Database.AddInParameter(cmd, "#Salt", DbType.Binary, user.Salt);
return Database.ExecuteNonQuery(cmd) > 0;
}
Any help is really appreciated, thanks.
It's been awhile since I've done this but if I remember correctly one of the easier ways was to use the SqlDbType.Binary as an output parameter then simply pull the value with a query.
The code below is untested and modified from another example using this method. The conversion from a SqlDbType.Binary to a byte array might need some attention or modification but should be really close.
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand("select #bytearray = dbcolumn from table", conn))
{
SqlParameter outputByteParam = new SqlParameter("#bytearray", SqlDbType.Binary)
{
Direction = ParameterDirection.Output
};
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(outputByteParam);
conn.Open();
cmd.ExecuteNonQuery();
byte[] result = outputByteParam.GetValueOrDefault<byte[]>(); // this line might need attention
}

C# SQL stored procedure call values

Trying to communicate with the database, I am little bit confused about how to pass a value as a parameter(for ex. an itemID) and get back the records that are having this ID.
Here is my stored procedure:
ALTER PROCEDURE [dbo].[sp_lightItem]
(
#itemID INT
)
AS
BEGIN
SELECT [itemID],
[itemName],
[itemLocation],
[itemChBy]
FROM [dbo].[Item]
WHERE itemSystemType='E' and itemID=#itemID ORDER BY itemID DESC;
END
And this is my c# code so far..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", ItemID);
}
As you can see in stored procedure, what I want is to get back those 4 elements:
[itemID],[itemName],[itemLocation],[itemChBy]
Unfortunately I do not know how to get them back/how to call them in c# function.
Any help is welcome.
i dont have enough repo to comment , can you provide the definition of
AcFunctions();
i am sure you it must be returning ConnectionString
try this
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
}
Edit 1
try this
public string LoadItemNew(int ItemID)
{
List<string> testList = new List<string>();
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteReader(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
if(newstorevalue.HasRows)
{
while(newstorevalue.Read())
{
testList.Add(newstorevalue["itemID"].ToString());
testList.Add(newstorevalue["itemName"].ToString());
testList.Add(newstorevalue["itemLocation"].ToString());
testList.Add(newstorevalue["itemChBy"].ToString());
}
}
}
You can try with this approach, I will use Data Transfer Object for holding data retrieved from database and Execute DataReader for reading.
First of all, you need to create a DTO class, I will call it LightItemDTO
public class LightItemDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string ChangedBy { get; set; }
}
Note: How to know the type of properties, you can reference this link: SQL Server Data Type Mappings
And now, I will using ADO.NET for execute the stored procedure to get data from database
public IEnumerable<LightItemDTO> GetLightItem(string itemText, string sqlConnectionString)
{
var results = new List<LightItemDTO>();
using (var con = new SqlConnection(sqlConnectionString))
{
using (var cmd = new SqlCommand("sp_lightItem", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemText;
con.Open();
using (var reader = cmd.ExecuteReader())
{
results.Add(new LightItemDTO
{
Id = Convert.ToInt32(reader["itemID"]),
Name = reader["itemName"].ToString(),
Location = reader["itemLocation"].ToString(),
ChangedBy = reader["itemChBy"].ToString()
});
}
}
}
return results;
}
Using DataReader is the best practice with high performance.
ADO.NET is the manual way to achieve this task, you can use some ORM framework for do it easier, such as: Entity Framework, Dapper.NET ...
You could execute stored procedure with parameters in following:
using (SqlConnection con = new SqlConnection(dc.Con)) {
using (SqlCommand cmd = new SqlCommand("sp_lightItem", con)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemId.Text;
con.Open();
cmd.ExecuteNonQuery();
}
}
first of all set the Commandtype as stored procedure and that procedure will return some data which you will save in dataset and then return the data set to where ever you want to populate the data
public DataSet LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
return DataSet ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem",new SqlParameter("#itemID" ItemID);
}
You can try like this ..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("#itemID", ItemID));
DataSet Ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem" , parameters.ToArray());
return "ok";
}

Populating list from Database - System.InvalidCastException

I have a simple class that I want to populate a list of from a database:
class Foreman
{
public int ForeBadge { get; set; }
public string ForeName { get; set; }
}
Heres my method that is supposed to return a list of Foreman objects:
static public List<Foreman> getForeman()
{
connectionString.DataSource = "server";
connectionString.InitialCatalog = "db";
connectionString.UserID = "tunnelld";
connectionString.Password = "pw";
string queryString = "SELECT * FROM [QTRAXAdmin].[vwQT_Foreman]";
List<Foreman> list;
// Creates a SQL connection
using (var connection = new SqlConnection(connectionString.ToString()))
{
using (var command = new SqlCommand(queryString, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
list = new List<Foreman>();
while (reader.Read())
list.Add(new Foreman { ForeBadge = reader.GetInt32(0), ForeName = reader.GetString(1) });
}
}
connection.Close();
return list;
}
The method is erroring out at list.Add(new Foreman { ForeBadge = reader.GetInt32(0), ForeName = reader.GetString(1) });
with: An unhandled exception of type 'System.InvalidCastException' occurred in System.Data.dll
To me it looks like I'm using the correct variable types. What am I doing wrong?
Try using the field names explicitly in your query to ensure columns are coming back in the order you think they are. That is the most suspicious thing I see. Alternatively you could use column names in your using block. And of course double check the column definition in the database.
Assuming the table column names are forebadge and forename, replace:
string queryString = "SELECT * FROM [QTRAXAdmin].[vwQT_Foreman]";
with:
string queryString = "SELECT FOREBADGE, FORENAME FROM [QTRAXAdmin].[vwQT_Foreman]";

Categories

Resources