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
}
Related
I am trying to display two SQL tables from the same DB in a webpage but the code below is displaying the 'BottomStock' table twice and everything I try seems to either get part of the data from the 'TopStock' table or none at all. I have scroll through countless forums but I have been unable to find a suitable solution. Any help would be appreciated.
public class Test4Model : PageModel
{
public List<FreezerInfo> listTopFreezer = new List<FreezerInfo>();
public List<FreezerInfo> listBottomFreezer = new List<FreezerInfo>();
public void OnGet()
{
try
{
using (var connection = new SqlConnection("Data Source=SDS-
LAPTOP\\SQLEXPRESS;Initial Catalog=test;user id=sa;password=wis09"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM TopStock";
command.ExecuteNonQuery();
command.CommandText = "SELECT * FROM BottomStock";
command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
//reader.Read();
{
FreezerInfo TopStock = new FreezerInfo();
TopStock.Description = reader.GetString(1);
TopStock.Quantity = reader.GetString(2);
listTopFreezer.Add(TopStock);
FreezerInfo BottomStock = new FreezerInfo();
BottomStock.Description = reader.GetString(1);
BottomStock.Quantity = reader.GetString(2);
listBottomFreezer.Add(BottomStock);
}
}
}
}
}
catch (Exception ex)
{
}
}
}
public class FreezerInfo
{
public string Description { get; set; }
public string Quantity { get; set; }
}
You are using SqlCommand completely wrong. ExecuteNonQuery does not return results. Only ExecuteScalar or ExecuteReader do. Furthermore, you have two batches each with a SELECT, but you are only executing one and somehow expecting the results to be interleaved.
I would advise you to use one batch of two SELECT statements, you can use NextResult to move to the next resultset within the batch.
Store your connection string in a settings file, not hard-coded.
Only select the columns you need, rather than SELECT *.
Use column names rather than ordinals, especially if you are using SELECT *.
Do not swallow exceptions. Handle them or allow them to bubble back to the caller.
Consider using async to allow the caller to continue asynchronously.
Reconsider the data types of the columns.
Consider why you have two almost identical tables in the first place. Perhaps they should be merged.
public void OnGet()
{
try
{
const string query = #"
SELECT Description, Quantity
FROM TopStock;
SELECT Description, Quantity
FROM BottomStock;
";
using var connection = new SqlConnection(connectionStringFromSettingsFileHere);
using var command = new SqlCommand(query, connection);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
while (reader.Read())
{
FreezerInfo TopStock = new FreezerInfo
{
Description = (string)reader["Description"],
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listTopFreezer.Add(TopStock);
}
reader.NextResult();
while (reader.Read())
{
FreezerInfo BottomStock = new FreezerInfo
{
Description = (string)reader["Description"];
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listBottomFreezer.Add(BottomStock);
}
}
catch (Exception ex)
{
// exception handling here. DO NOT SWALLOW
}
}
I have a permissions database that I created which has 10 data points in it. I can insert data into this table through the program, but when I try to pull the data it's been giving me a lot of headaches. I've attempted to use a Using loop and it errors out every time, so I'm trying to just go super basic, and pull the data line by line but it's not working at all. My goal is to pull all the data into a List Variable so that I can call each individual permission later. What is stored in each permission is simply the Text True or False, with the exception of the first one Emp_ID being an Int.
Con is my connection script, and it's working perfectly, as it works everywhere else within the program.
Settings.Emp_ID is the Emp_ID of the user that's currently logged in. This means that we can skip the Emp_ID from the permissions when pulling the data, but I've attempted to do an EXCLUDE or SKIP but it's failed every time as well.
SQL Format, Emp_ID = INT, all others = Text
As for Error:
System.NullReference Exception 'Object Reference Not set to an instance of an object.
namespace TMS
{
public partial class Login_Form : Form
{
string[] Data;
void verify()
{
SqlDataReader rdr = null;
SqlCommand cmd = new SqlCommand("SELECT * FROM Permissions WHERE Emp_ID = '"
+ Settings.Emp_ID + "'", Con);
try
{
Con.Open();
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
int Emp_ID = (int)rdr["Emp_ID"];
Data[0] = (string)rdr["Check_Out"];
Data[1] = (string)rdr["Check_In"];
Data[2] = (string)rdr["Self_His_Tool"];
Data[3] = (string)rdr["Self_His_User"];
Data[4] = (string)rdr["Tool_His"];
Data[5] = (string)rdr["User_His"];
Data[6] = (string)rdr["Add_Users"];
Data[7] = (string)rdr["Add_Tools"];
Data[8] = (string)rdr["Remove_Users"];
Data[9] = (string)rdr["Remove_Tools"];
}
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (Con != null)
Con.Close();
}
}
The Data array is still null, because this line does not actually create an array object:
string[] Data;
All it does is create a variable that might someday refer to an array object.
Later, when you have this and lines like it:
Data[0] = (string)rdr["Check_Out"];
You end up with the NullReferenceException. Data is still null, and so trying to access Data[0] is not allowed.
We want something more like this:
public class Permissions
{
//probably these should be "bool", but I adapted the types from the old code
public string Check_Out {get;set;}
public string Check_In {get;set;}
public string Self_His_Tool {get;set;}
public string Self_His_User {get;set;}
public string Tool_His {get;set;}
public string User_His {get;set;}
public string Add_Users {get;set;}
public string Add_Tools {get;set;}
public string Remove_Users {get;set;}
public string Remove_Tools {get;set;}
}
// ...
Permissions Data;
Permissions verify()
{
string SQL = "SELECT * FROM Permissions WHERE Emp_ID = #Emp_ID";
// Do NOT re-use the some connection object throughout an app or class!
// Only re-use the connection string.
// using directive will ensure connection is closed, so no need for finally block
using var con = new SqlConnection("connection string here");
using var cmd = new SqlCommand(SQL, con);
// Do NOT use string concatation to substitute data into a query!
cmd.Parameters.Add("#Emp_ID", SqlDbType.Int).Value = Settings.Emp_ID;
// don't need a "try" if there's no catch or finally
con.Open();
rdr = cmd.ExecuteReader();
Permissions result = null;
if (rdr.Read()) //Don't need "while" if we only expect one record
{
result = new Permissions();
int Emp_ID = Settings.Emp_ID;
result.Check_out =(string)rdr["Check_Out"];
result.Check_In = (string)rdr["Check_In"];
result.Self_His_Tool = (string)rdr["Self_His_Tool"];
result.Self_His_User = (string)rdr["Self_His_User"];
result.Tool_His = (string)rdr["Tool_His"];
result.User_His = (string)rdr["User_His"];
result.Add_Users = (string)rdr["Add_Users"];
result.Add_Tools = (string)rdr["Add_Tools"];
result.Remove_Users = (string)rdr["Remove_Users"];
result.Remove_Tools = (string)rdr["Remove_Tools"];
}
return result;
}
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";
}
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;
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Address
{
public string City { get; set; }
public string Country { get; set; }
}
/*
* There are 2 c# objects i have shown
* There is a stored procedure in my application which
* returns data for both objects simultaneously
* eg
* select FirstName, LasteName from Users where something="xyz"
* select City,Country from Locations where something="xyz"
*
* both queries are run by single procedure
* Now how can i fill both objects with from that stored procedure in asp.net using c#
*/
Use ADO.NET, open a SqlDataReader on a SqlCommand object executing the SP with the parameters. Use the SqlDataReader.NextResult method to get the second result set.
Basically:
SqlConnection cn = new SqlConnection("<ConnectionString>");
cn.Open();
SqlCommand Cmd = new SqlCommand("<StoredProcedureName>", cn);
Cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlDataReader dr = Cmd.ExecuteReader(CommandBehavior.CloseConnection);
while ( dr.Read() ) {
// populate your first object
}
dr.NextResult();
while ( dr.Read() ) {
// populate your second object
}
dr.Close();
You could use ADO.net and design a dataset which will create the classes for you so your queries will execute and read into classes which store the data you got.
http://msdn.microsoft.com/en-us/library/aa581776.aspx
That is an excellent tutorial on how to create a data access layer, which is what it sounds like you want to do.
using(SqlConnection connexion = new Sqlconnection(youconenctionstring))
using(SqlCommand command = conenxion.Createcommand())
{
command.Commandtext = "yourProcName";
command.CommandType = CommandType.StoredProcedure;
command.Paramters.Add("#yourparam",yourparamvalue);
connexion.Open();
SqlDataReader reader = command.ExecuteReader();
List<User> users = new List<User>;
List<Adress> adresses = new List<User>;
while(read.Read())
{
User user = new User();
user.firstName = (string) read["FirstName"];
users.Add(user);
}
read.NextResult();
while(read.Read)
{
Address address = new Address();
address.City = (string) read["Name"];
adresses.Add(address);
}
//make what you want with your both list
}
Linq to SQL, Entity Framework, or NHibernate would be my suggestions.
Check out the Enterprise library, specifically the Data Access block from microsoft patterns and practices. Even if you don't use it, you can steal, er... borrow code from it to do what you want.
http://www.codeplex.com/entlib