Inserting byte array into SQL Server - c#

I am constructing a sql_insert_string to be used in Microsoft.ApplicationBlocks.Data.SqlHelper to be used as follows:
SqlHelper.ExecuteNonQuery(Transaction, CommandType.Text, sql_insert_string)
When I hover over the SQL statement it looks like below:
string sql_insert_string = "Insert into images_table(image_id, image_byte_array) values ('123', System.Byte[])
One of the insert value is a byte array as shown above. The variable has value in the byte array, say like byte[6738] . But after the sql_insert_string is constructed, it comes as System.Byte[]. The image_byte_array column type is varbinary(max). The database is SQL Server 2008. Because of this the database throws the following error:
An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as \"\" or [] are not allowed. Change the alias to a valid name.

you can insert the byte array like so:
private void FireSql(byte[] input)
{
const string sql_insert_string =
"Insert into images_table(image_id, image_byte_array) values (#image_id, #image_byte_array)";
SqlTransaction transaction = null; //wherever you get the transaction obj from.
var imageIdParam = new SqlParameter("#image_id", SqlDbType.Int, 4)
{
Direction = ParameterDirection.Input,
Value = 123
}; //change the data type to whatever data type you are expecting
var byteParam = new SqlParameter("#image_byte_array", SqlDbType.VarBinary)
{
Direction = ParameterDirection.Input,
Size = input.Length,
Value = input
}; //change the data type to whatever data type you are expecting
SqlHelper.ExecuteNonQuery(transaction, CommandType.Text, sql_insert_string, imageIdParam, byteParam);
}
I would suggest looking at an ORM (https://en.wikipedia.org/wiki/Object-relational_mapping) like Entity Framework(http://www.asp.net/entity-framework) to do all of this for you while increasing security and future changes much easier.

You should be using the Parameters while constructing the SQL Query which obviously will avoid SQL Injection attacks. How your queries are getting constructed is still unclear here.
Something like this should do it for you.
SqlParameter sParam = new SqlParameter("#image_byte_array", SqlDbType.VarBinary)
{
Value = image
};
SqlHelper.ExecuteNonQuery(Transaction, CommandType.Text, sql_insert_string, sParam)

You may use
string sql_insert_string =
String.Format("INSERT INTO images_table(image_id, image_byte_array) VALUES ('123', CAST('{0}' AS VARBINARY(MAX)))", System.Byte[].ToString());
And yes, as #marc_s commented, you shouldn't be constructing SQL statements as a security concern.

Related

SQL multiple-value parameter in C#

This is what I have:
select *
from AuditQuestionnaires
where Companyid = #companyid
and RiskTypeID != #RiskIdList[x];
Why can't I add a parameter like this:
command2.Parameters.Add(new SqlParameter("RiskIdList[x]", RiskIdList[x]));
I'm using SQL Server, and this is the script I'm using in .cs file
Firstly, since you are using SQL Server, you cannot have a parameter named #RiskTypeId[x]. It is simply not a valid name.
Secondly, when passsing parameters from c# the parameter name has to include the initial "#".
Thirdly, as a general rule it is much better to construct the SQLParameter including the DataType. This means that there is no guesswork required, and guesses occasionally go wrong.
Fourthly, it seems to me that you want to iterate through an array. There is nothing to stop you doing this in c#. You would simply create a SqlParameter like this
command2.Parameters.Add(new SqlParameter { ParameterName = "#RiskTypeId", DBType = DBType.Int32, Value = RiskIDList[x] });
However I suspect that what you are really trying to do is to send a list to SQL Server. To achieve this, you need to use a User-Defined Table Type. In your case the following is sufficient:
CREATE TYPE [dbo].[IdList] AS TABLE ([Id] int);
Then you can define a parameter to your query as:
#RiskTypeIdList IdList READONLY
Finally you have to put the contents of your array into a DataTable. Because this is something I do frequently, I have a Class for this job:
public class TDataTable<int> : DataTable
{
public TDataTable(IEnumerable<T> ids) : base()
{
Columns.Add("Id", typeof(T));
var added = new HashSet<T>();
foreach (T id in ids)
{
//ensure unique values
if (added.Add(id))
{
Rows.Add(id);
}
}
}
public TDataTable() : base()
{
Columns.Add("Id", typeof(T));
}
}
You can then pass the parameter like this:
command2.Parameters.Add(new SqlParameter { ParameterName = "#RiskTypeIdList", SqlDBType = SqlDBType.Structured, Value = new TDataTable<int>(RiskIDList) });
Within your SQL you can then do what Thorsten suggested:
WHERE RiskTypeId NOT IN (SELECT Id FROM #RiskTypeIdList)
#Thorsten Kettner, its a valid SQL query, we can != in sql query.
command2.Parameters.Add(new SqlParameter("RiskIdList[x]", RiskIdList[x]));
We cannot pass "RiskIdList[x]" as SQL Parameter, we need to pass parameter like this "RiskIdList", RiskIdList[x] means that you are passing argument at runtime, but query or stored procedure has not dynamic parameter in where clause.

Passing in Oracle Parameter to SQL string

I'm having a problem where I don't know how I'm supposed to pass in an Oracle parameter where the C# type is a string and the Oracle type is a Varchar2.
Currently I'm passing in this string as CMS','ABC thinking that Oracle will add in the '' that surround this string making it a varchar2 that looks like 'CMS','ABC'.
This works for a single string like CMS but when the value is something longer, like something typically in a IN (list) command the parameter won't be passed in correctly.
This is the code I'm referring too.
string sql = 'SELECT name FROM Pers p WHERE p.FirstName IN (:names)';
The below works when the value of :names being passed in is CML without any quotes.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML", ParameterDirection.Input);
Below doesn't work when the value of :names being passed in is CML','ABC with quotes on the inside.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML','ABC", ParameterDirection.Input);
Why is that?
Does Oracle add in single quotes around the parameter when it's passed into the sql statement? Why doesn't it add quotes around the second case?
ODP.NET parameters do not work with multiple, comma separated values. Each parameter is treated as a single value, whatever kind of quotes it contains.
Oracle does not add quotes around parameter values when passed to a query. Quotes are just a way to write a VARCHAR value in a query, but when using parameters, Oracle doesn't "replace your parameter with its value then execute the query", as this would allow SQL injection.
If that was the case, imagine your parameter value was: "CML', 'ABC');DROP DATABASE Test;--". Oracle would then execute SELECT name FROM Pers p WHERE p.FirstName IN ('CML', 'ABC');DROP DATABASE Test;--'!
See this question for ideas on how to solve your problem: Oracle Parameters with IN statement?
From your comments/answers I was able to come up with this solution. I hope it helps others who come.
To get around ODT.NET parameters not working with multiple comma separated values you can divide each value into its own parameter. Like the following.
string allParams = "CML, ABC, DEF";
string formattedParams = allParams.Replace(" ", string.Empty); // Or a custom format
string [] splitParams = formattedParams.Split(',');
List<OracleParamter> parameters = new List<OracleParameter>();
string sql = #"SELECT * FROM FooTable WHERE FooValue IN (";
for(int i = 0; i < splitParams.Length; i++)
{
sql += #":FooParam" + i + ",";
parameters.Add(new OracleParameter(":FooParam" + i, OracleDbType.Varchar2, splitParams[i], ParameterDirection.Input));
{
sql = sql.Substring(0, (sql.Length - 1));
sql += ')';
The string sql will now have this as it's value: SELECT * FROM FooTable WHERE FooValue IN (:FooParam0,:fooParam1, etc...)
This will solve the problem.
Another approach would be to add in a bunch of OR clauses for each parameter. The above example is better since you don't write a bunch of OR clauses though.

Update with Dapper using dynamic column name

I need to do an update but the column name is dynamic.
Code Snippet:
using (var cn = Connection)
{
var sql = #"UPDATE Teste
SET #columnName = #parameter,
DT_PROCESSAMENTO = #vDtProcessamento
WHERE ID = #ID";
var resultado = cn.Execute(sql, new
{
columnName,
parameter,
ID
});
}
Is it possible to pass the column name as a parameter?
This code is what I did, but it does not work. No exceptions but the update does not work.
You should use String.Format:
var columnName = "Name";
var sql = String.Format(#"UPDATE Teste
SET {0} = #parameter
WHERE ID = #ID", columnName);
But here you can get SQL injection.
So it's better to check that column name is really column name in your table.
No you cannot do that that way because the column name cannot be a variable.
To do it you do need dynamic SQL like this:
using (var cn = Connection)
{
var sql = $#"UPDATE Teste
SET {columnName} = #parameter,
DT_PROCESSAMENTO = #vDtProcessamento
WHERE ID = #ID";
var resultado = cn.Execute(sql, new
{
parameter,
ID
});
}
In the above snippet code you can combine # to use linebreaks inside the string and $ to insert variables in the string - it's a bit clearer and shorter than using String.Format.
I already use something like this with dapper for a few specific scenarios.
As noted, to do this you need to dynamically construct your SQL, either in SQL then use EXEC(), or in your C# with string.Format() or StringBuilder. Just be aware that by doing this, you will identify yourself as a savage in the eyes of some people! You have no guarantee that users of your method will provide a valid column name. You have to run your program to see what SQL it generates, and if it runs. You have no type safety around the input parameter. There must be a better way. disclaimer: I wrote QueryFirst

tinyint in SQL Server to byte in C#

In database management and application development we must be mindful of space and memory requirements. I have always been taught to use the data type that uses the least amount of space for your needs.
In my scenario, I have a column in a table that stores values {0,1,2,3,4} (SQL Server 2012). For this I chose to use the tinyint datatype. These values are pulled from the database into a C# application. Currently, I cannot get this tinyint data type to convert to a byte. When I try, I get an error "Cannot implicitly convert int to byte". If I change the datatype in the application to an integer, I can pull it just fine. Same with a string.
For performance purposes, is it okay to use integers throughout my entire application where I would normally use byte? If not, how do you convert an integer to a byte?
This is the code that I use that gives an error:
string strSQL = "SELECT securityLevel FROM security WHERE employeeID=#empID;";
using (SqlConnection dbConn = new SqlConnection(connParam))
{
dbConn.Open();
byte level = 0;
using (SqlCommand dbCommand = new SqlCommand(strSQL, dbConn))
{
dbCommand.CommandType = System.Data.CommandType.Text;
dbCommand.Parameters.AddWithValue("#empID", "12345");
using (SqlDataReader dbReader = dbCommand.ExecuteReader())
{
while (dbReader.Read())
{
level = dbReader.GetByte(0);
}
}
}
Console.WriteLine(level);
Console.ReadLine();
}
I have also tried:
level = (byte)dbReader.GetValue(0);
Yes, you were correct to pick TINYINT as the datatype if you are storing only 0 - 4.
Yes, TINYINT equates to a byte in .Net. You can see a list of mappings here:
http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
No, you did not actually use TINYINT when creating the table, else you would not be getting this error. The error message is very specific about the source datatype being INT.
No, do not use INT to store these values. That is unnecessary and what you are attempting to do (i.e. TINYINT and byte) is perfectly valid and I have done it many times.
Assuming you do not have millions of rows of data in that table and constant hits against it, run the following:
ALTER TABLE [security] ALTER COLUMN [securityLevel] TINYINT NOT NULL;
(I am assuming that the column is currently NOT NULL and in that case, if you leave off the NOT NULL in the ALTER TABLE statement, it will change the field to TINYINT NULL. If the field isn't currently NOT NULL, then just leave that part off)
I wrote the following LinqPad (against SqlServer express) to verify that you can read in a tinyint as a byte using .NET and SqlServer:
var cb = new SqlConnectionStringBuilder { DataSource = #".\Sqlexpress", InitialCatalog = "Medallion_OData_Tests_CustomersContext251990930203", IntegratedSecurity = true };
using (var c = new SqlConnection(cb.ConnectionString))
{
c.Open();
var cmd = c.CreateCommand();
cmd.CommandText = "SELECT CAST(1 AS tinyint)";
var reader = cmd.ExecuteReader();
reader.Read();
reader.GetByte(0).Dump();
}
This suggests to me that the actual securityLevel column in your database is not of the type TINYINT. To verify, why not temporarily modify your select query to include a CAST to TINYINT as in my example? If this works, that will confirm that the table schema is the problem.
Other ways to check the actual table schema include querying sys.columns or highlighting the name of the table in SqlServer Management Studio and hitting ALT+F1.
I think safest way is to use Convert.ToByte Method:
level = Convert.ToByte(dbReader.GetValue(0));
It converts from many value types to byte.

auto SQL field's quotation marks by type in c#

Suppose that I want to create an SQL SELECT statement dynamically with reflection on primary key. I search in the table for primary keys and then, I make the statement.
Problem is, I don't know the type of fields that compose the primary key before getting them. So, if it's a string or date, I must add quotation marks but not if it's an int.
Atm, I am doing like that :
var type = field.GetType().Name;
if (type.ToLower().StartsWith("string") || type.ToLower().StartsWith("date"))
{
field = "\"" + field + "\"";
} else if (type.ToLower().StartsWith("char"))
{
field = "\'" + field + "\'";
}
With this code, I can handle some SQL types but there are a lot more.
My problem is that it's combined with LinQ. I got a DataContext object and a generic type table from the context. And context.ExecuteQuery only allows parameters to be passed has values. I also tried with Dynamic LinQ but I got the same problem
Does anyone know a better solution?
That is simply the wrong way to write SQL. Parameterize it and all these problems evaporate (as do problems with "which date format to use", etc. And of course the biggie: SQL injection.
Then it just becomes a case of adding #whatever into the TSQL, and using cmd.Parameters.AddWithValue("whatever", field) (or similar).
Update (from comments): since you mention you are using DataContext.ExecuteQuery, this becomes easier: that method is fully parameterized using the string.Format convention, i.e.
object field = ...;
var obj = db.ExecuteQuery<SomeType>(
"select * from SomeTable where Id = {0}", field).ToList(); // or Single etc
No string conversions necessary.
(the last parameter is a params object[], so you can either pass multiple discreet terms, or you can populate an object[] and pass that, if the number of terms is not fixed at compile-time; each term in the array maps by (zero-based) index to the {0}, {1}, {2}... etc token in the query)
Have you tried with parameters? For instance if you are using SQLServer as a database and you want to do this query:
"SELECT * FROM someTable WHERE id = " + field;
Then you should use sometething like this:
"SELECT * FROM someTable WHERE id = #field"
and add parameter to your command:
SqlParameter param1 = new SqlParameter("#field", field);
command.Parameters.Add(param1);
EDIT: Watch out that for different database providers the syntax for the SQL query is different, the same for the Access would be
"SELECT * FROM someTable WHERE id = ?";
command.Parameters.AddWithValue("field", field);

Categories