Error converting string to GUID in c# - c#

I am using the following code for performing a delete operation via EF code first using a inline query internally
void IRepository<T>.Delete(params Guid[] ids)
{
var sql = string.Format("UPDATE {0} SET [IsDeleted] = 1 WHERE [Id] IN (#ids) ", GetTableName());
string sep = String.Join(", ", ids.Select(x => "'" + x + "'"));
var sqlParams = new Object[]
{
new SqlParameter("ids", string.Join(",",sep)),
};
DataContext.Database.ExecuteSqlCommand(sql, sqlParams);
}
Now when I execute the command it gives me
conversion failed when converting from a character string to uniqueidentifier
error.
Hiwever when I run the query in sql say.
UPDATE [dbo].[Table] SET [IsDeleted] = 1 WHERE [Id] IN ('20Cr0BCA-6EBB-E411-A04B-BC305BA8C713','506c79c1-6ebb-e411-a04b-bc305ba8c733')
it works fine.
Is this possible to do this way ?Or what am I doing wrong ?

I would do something like this
void IRepository<T>.Delete(params Guid[] ids)
{
if (ids == null || !ids.Any())
return;
var idParams = ids.Select((x, cnt)=> new { ParamName ="#ids"+ cnt, Param = x});
var sql = string.Format("UPDATE {0} SET [IsDeleted] = 1 WHERE [Id] IN ("+ String.Join(", ",idParams.Select(x => x.ParamName)) + ") ", "Table");
var sqlParams = idParams.Select(x=> new SqlParameter(x.ParamName, x.Param)).ToArray();
DataContext.Database.ExecuteSqlCommand(sql, sqlParams);
}

Related

Query causing sql injection issue

Type entryEntityType = entry.Entity.GetType();
string tableName = GetTableName(entryEntityType);
string primaryKeyName = GetPrimaryKeyName(entryEntityType);
string deletequery = string.Format("UPDATE {0} SET IsDeleted = 1 WHERE {1} = #id", tableName, primaryKeyName);
Database.ExecuteSqlCommand(deletequery, new SqlParameter("#id", entry.OriginalValues[primaryKeyName]));
After running the sonar scan above query is giving a security hotspot for sql injection.How can this be handled?
It doesn't look like table name and primary key name are dependent on user input, so I would suppress the Sonar error around this code. If you insist on fixing it you can do something like this (pseudo code):
Do this once, if you will, make it static:
var deleteQueries = new Dictionary<Type, string>();
foreach (Type entryEntityType in AllEntityTypes) // I don't know how you will get all entities
{
string tableName = GetTableName(entryEntityType);
string primaryKeyName = GetPrimaryKeyName(entryEntityType);
string deletequery = string.Format("UPDATE {0} SET IsDeleted = 1 WHERE {1} = #id", tableName, primaryKeyName);
deleteQueries.Add(entryEntityType, deleteQuery);
}
When executing delete do this:
Type entryEntityType = entry.Entity.GetType();
string deleteQuery = deleteQueries[entryEntityType];
string primaryKeyName = GetPrimaryKeyName(entryEntityType);
Database.ExecuteSqlCommand(deletequery, new SqlParameter("#id", entry.OriginalValues[primaryKeyName]));
As I said, I would just suppress the error.
Also when injecting identifiers into dynamic SQL for SQL Server, you should sanitize the string by using a delimited identifier.
In TSQL you do this with the QUOTENAME function, and here's a C# version of it.
private static string QuoteName(string identifier)
{
var sb = new StringBuilder(identifier.Length + 3, 1024);
sb.Append('[');
foreach (var c in identifier)
{
if (c == ']')
sb.Append(']');
sb.Append(c);
}
sb.Append(']');
return sb.ToString();
}

SQL data projection with nested query in ef-core

there is a table of messages. scheme:
CREATE TABLE [dbo].[Messages] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[DateCreate] DATETIME2 (7) DEFAULT (getdate()) NOT NULL,
[SenderId] INT NOT NULL,
[RecipientId] INT NOT NULL,
[TextMessage] NVARCHAR (MAX) NULL,
[IsReaded] BIT NOT NULL,
CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED ([Id] ASC)
);
I want to get a table:
sender_id [int] - sender id key
all_count_messages [int] - number of messages from the sender
count_unreaded_messages [int] - number of unread messages from the sender
most_unreaded [DateTime] - the date of the most old unread messages
I also need group by [sender_id] and sort the output. Good. I make a simple request:
SELECT
message.SenderId AS sender_id,
COUNT(*) AS all_count_messages,
SUM(CAST(
CASE
WHEN IsReaded = 0
THEN 1
ELSE 0
END AS int)) as count_unreaded_messages,
MIN(message.DateCreate) AS most_unreaded
FROM
Messages AS message
GROUP BY
message.SenderId
ORDER BY
most_unreaded
demo result:
sender_id all_count_messages unreaded_messages most_unreaded
2 3 2 2019-08-15 20:03:59.0000000
1 9 8 2019-08-15 20:04:59.0000000
the answer suits me. how to describe it on EFCore?
try it
var chats = from my_messages in db.Messages
group my_messages by my_messages.SenderId into g
select
new
{
sender_id = g.Key,
all_count_messages = g.Count(),
unreaded_messages = from sub_messages in db.Messages where sub_messages.SenderId == g.Key && !sub_messages.IsReaded group sub_messages by sub_messages.SenderId into sub_g select sub_g.Count(),
most_unreaded = from sub_messages in db.Messages where sub_messages.SenderId == g.Key && !sub_messages.IsReaded group sub_messages by sub_messages.SenderId into sub_g select sub_g.Min(x => x.DateCreate)
};
foreach (var chat in chats) // so, too, has tried: chats.Include(x=>x.unreaded_messages).Include(x => x.most_unreaded)
{
}
get error in foreach (var chat in chats)
An unhandled exception occurred while processing the request.
ArgumentException: must be reducible node
System.Linq.Expressions.Expression.ReduceAndCheck()
I tried otherwise:
var chats = db.Messages.AsNoTracking().FromSql(
"SELECT" +
" message.SenderId AS sender_id," +
" COUNT(*) AS all_count_messages," +
" SUM(CAST(" +
" CASE" +
" WHEN IsReaded = 0" +
" THEN 1" +
" ELSE 0" +
" END AS int)) as count_unreaded_messages," +
" MIN(message.DateCreate) AS most_unreaded " +
"FROM " +
" Messages AS message " +
"GROUP BY " +
" message.SenderId " +
"ORDER BY" +
" most_unreaded ");
foreach (var chat in chats)
{
}
get error in foreach (var chat in chats)
InvalidOperationException: The required column 'Id' was not present in the results of a 'FromSql' operation.
Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
You need to add Id column in the select statement to your query. It should be like
"SELECT" +
" message.Id AS Id," +
" message.SenderId AS sender_id," +
...
thank #ilkerkaran for the link https://stackoverflow.com/a/28627991/2630427 there I found and slightly altered method of dynamic request
a little finished realisation .net asp core 2.2:
public class AppDbContext : DbContext
{
public DbSet<Message> Messages { get; set; }
IOptions<AppConfig> app_config;
public AppDbContext(DbContextOptions<AppDbContext> options, IOptions<AppConfig> _app_config)
: base(options)
{
app_config = _app_config;
Database.EnsureCreated();
}
public IEnumerable<dynamic> DynamicListFromSql(string Sql, Dictionary<string, object> Params = null)
{
using (var cmd = Database.GetDbConnection().CreateCommand())
{
cmd.CommandText = Sql;
if (cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); }
if (Params != null)
foreach (KeyValuePair<string, object> p in Params)
{
DbParameter dbParameter = cmd.CreateParameter();
dbParameter.ParameterName = p.Key;
dbParameter.Value = p.Value;
cmd.Parameters.Add(dbParameter);
}
using (var dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
var row = new ExpandoObject() as IDictionary<string, object>;
for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++)
{
row.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]);
}
yield return row;
}
}
}
}
}
string query =
"SELECT" +
" message.SenderId AS sender_id," +
" COUNT(*) AS all_count_messages," +
" (SELECT COUNT(*) FROM Messages AS sub_message WHERE sub_message.SenderId = message.SenderId AND sub_message.IsReaded = 0) AS unreaded_messages," +
" (SELECT MIN(sub_message.DateCreate) FROM Messages AS sub_message WHERE sub_message.SenderId = message.SenderId) AS most_unreaded " +
"FROM " +
" Messages AS message " +
"GROUP BY " +
" message.SenderId " +
"ORDER BY" +
" most_unreaded ";
//"OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY", offset, fetch);
foreach (var chat in db.DynamicListFromSql(query))
{
}

How do INSERT INTO Firebird, with autoincrement for the primary key?

How do INSERT INTO Firebird, with autoincrement for the primary key?
For the table fields I have:
fstPriority VARCHAR(30), fstInfo VARCHAR(100), fstDateCreated VARCHAR(30), fstDateModified VARCHAR(30), fiKeyID INTEGER PRIMARY KEY
For the INSERT INTO I have:
FbConnection fbConn = new FbConnection(stOpenConn))
fbConn.Open();
...
FbTransaction fbTransaction = fbConn.BeginTransaction();
FbCommand fbCmd = new FbCommand("INSERT INTO " + stTableName + "(" + stFieldNames + ") VALUES ( #p0, #p1, #p2, #p3, #p4 ) RETURNING fiKeyID ", fbConn, fbTransaction);
but am uncertain what should be used for the
fbCmd.Parameters.AddWithValue
fbCmd.Parameters.AddWithValue("#p0", "1st value");
fbCmd.Parameters.AddWithValue("#p1", "2nd value");
fbCmd.Parameters.AddWithValue("#p2", "3rd value");
fbCmd.Parameters.AddWithValue("#p3", "4th value");
Then what? For fiKeyID, do I add
fbCmd.Parameters.AddWithValue("#p4", "");
Also, I see at http://www.firebirdfaq.org/faq29/ creating an autoincrement column, but am uncertain how to do this in C# ... Firebird ADO.NET ... FirebirdClient.5.8.0 ... Visual Studio 2013.
CREATE GENERATOR ...;
SET GENERATOR ...;
set term !! ;
CREATE TRIGGER ...
are not recognized by the Visual Studio compiler.
An important thing is that SET TERM is not part of the Firebird statement syntax, instead it is a client-side feature to set the statement terminator in query tools like ISQL. This terminator is necessary to know when a statement is complete and can be sent to the server. By default these tools do that on a semi-colon (;), but that doesn't work with PSQL (stored procedures, triggers), because PSQL code uses the semi-colon as well. To address this, these tools have SET TERM to switch this terminator.
Using the Firebird ADO.net provider however, you need to execute statements one at a time, so a statement terminator is irrelevant.
To be able to generate a primary key you can use the following solutions:
Firebird 3 has an identity type column, so you don't need to create a sequence and trigger yourself:
create table withgeneratedid(
id integer generated by default as identity primary key,
column2 varchar(100)
)
For Firebird 2.5 and earlier you will need to create a sequence and trigger:
create table withgeneratedid(
id integer primary key,
column2 varchar(100)
);
create sequence seq_withgeneratedid;
set term #;
create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end#
set term ;#
When you insert values into a table and want to have a generated key, you should not include the id column in the column-list. Including the id column allows you to override the key value, but that might lead to future inserts generating a duplicate key!. If you do include the id column, then no key will be generated in the Firebird 3 example, in the Firebird 2.5 example a key will be generated if the value of the column is null, otherwise it will take the provided value.
In ADO.net you'd normally need to execute the statements individually (and not use set term). Alternatively, you could use FbScript to parse a DDL script and execute the parse statements. Note that FbScript does support (and even requires) set term.
To execute this with the Firebird ADO.net provider, you can do something like the example below. I have included three alternatives for creating the table Firebird3, Firebird2_5, and FbScriptFB2_5 (which is the same as Firebird2_5 but uses FbScript). It also show how to retrieve the generated key:
namespace FbGeneratedKeys
{
class Program
{
private static SolutionType solutionType = SolutionType.FbScriptFB2_5;
static void Main(string[] args)
{
var connectionString = new FbConnectionStringBuilder
{
Database = #"D:\temp\generatedkey.fdb",
ServerType = FbServerType.Default,
UserID = "SYSDBA",
Password = "masterkey",
}.ToString();
FbConnection.CreateDatabase(connectionString, pageSize: 8192, overwrite : true);
using (FbConnection connection = new FbConnection(connectionString))
using (FbCommand cmd = new FbCommand())
{
connection.Open();
cmd.Connection = connection;
switch (solutionType) {
case SolutionType.Firebird3:
Firebird3Example(cmd);
break;
case SolutionType.Firebird2_5:
Firebird2_5Example(cmd);
break;
case SolutionType.FbScriptFB2_5:
FbScriptFB2_5Example(cmd);
break;
}
cmd.CommandText = #"insert into withgeneratedid(column2) values (#column2) returning id";
cmd.Parameters.AddWithValue("#column2", "some value");
cmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
cmd.ExecuteNonQuery();
Console.WriteLine("Id:" + cmd.Parameters[1].Value);
Console.ReadLine();
}
}
private static void Firebird3Example(FbCommand cmd)
{
// Firebird 3 identity column
cmd.CommandText = #"create table withgeneratedid(
id integer generated by default as identity primary key,
column2 varchar(100)
)";
cmd.ExecuteNonQuery();
}
private static void Firebird2_5Example(FbCommand cmd)
{
// Firebird 2.5 and earlier normal primary key with trigger to generate key
// Table
cmd.CommandText = #"create table withgeneratedid(
id integer primary key,
column2 varchar(100)
)";
cmd.ExecuteNonQuery();
// Sequence
cmd.CommandText = "create sequence seq_withgeneratedid";
cmd.ExecuteNonQuery();
// Trigger
cmd.CommandText = #"create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end";
cmd.ExecuteNonQuery();
}
private static void FbScriptFB2_5Example(FbCommand cmd)
{
string script = #"
create table withgeneratedid(
id integer primary key,
column2 varchar(100)
);
create sequence seq_withgeneratedid;
set term #;
create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end#
set term ;#
";
FbScript fbScript = new FbScript(script);
fbScript.Parse();
FbBatchExecution exec = new FbBatchExecution(cmd.Connection);
exec.AppendSqlStatements(fbScript);
exec.Execute();
}
}
enum SolutionType
{
Firebird3,
Firebird2_5,
FbScriptFB2_5
}
}
Definitions:
public const string stMAIN_TABLE_NAME = " OrgTable ";
public const string stDELETED_TABLE_NAME = " BackupTable ";
public const string stFIELD_DEFINITIONS = " fstPriority VARCHAR(30)" +
", fstInfo VARCHAR(100)" +
", fstDateCreated VARCHAR(30)" +
", fstDateModified VARCHAR(30)" +
", fiKeyID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ";
public const string stFIELD_NAMES = " fstPriority" +
", fstInfo" +
", fstDateCreated" +
", fstDateModified" +
", fiKeyID ";
public const string stFIELD_NAMES_NO_KEY_ID = " fstPriority" +
", fstInfo" +
", fstDateCreated" +
", fstDateModified ";
Code:
//------------------------------
static private bool boCreateDatabaseTables(string stPathFilename,
string stUserID,
string stPassword,
List<string> liststTableNames,
List<string> liststFieldDefinitions)
{
bool boErrorFlag = false;
int iTablesCount = liststTableNames.Count();
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using (FbConnection fbConn = new FbConnection(stOpenConn)) {
try {
fbConn.Open();
FbTransaction fbTransaction = fbConn.BeginTransaction();
for (int ii = 0; ii < iTablesCount; ii++) {
string stSql = "CREATE TABLE " + liststTableNames[ii] + "( " + liststFieldDefinitions[ii] + ")";
FbCommand fbCmd = new FbCommand(stSql, fbConn, fbTransaction);
fbCmd.ExecuteNonQuery();
}
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boCreateDatabaseTables ... " + ex.Message);
}
}
return boErrorFlag;
}//boCreateDatabaseTables
//------------------------------
//------------------------------
static public bool boAddRow(string stPathFilename,
string stUserID,
string stPassword,
string stTableName,
string stFieldNamesNoKeyId,
List<string> liststFieldValuesNoKeyId)
{
bool boErrorFlag = false;
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using(FbConnection fbConn = new FbConnection(stOpenConn)) {
fbConn.Open();
try {
string stValuesPlaceHolder = "#p0";
for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
stValuesPlaceHolder += ", #p" + (iii).ToString();
FbTransaction fbTransaction = fbConn.BeginTransaction();
string stCmd = "INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " ) RETURNING fiKeyID ";
FbCommand fbCmd = new FbCommand(stCmd, fbConn, fbTransaction);
for (int iii = 0; iii < liststFieldValuesNoKeyId.Count; iii++) {
string stPlaceHolder = "#p" + (iii).ToString();
string stValue = liststFieldValuesNoKeyId[iii];
fbCmd.Parameters.AddWithValue(stPlaceHolder, stValue);
}
fbCmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
fbCmd.ExecuteNonQuery();
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boAddRow ... " + ex.Message);
}
}
return boErrorFlag;
}//boAddRow
//------------------------------

Why am I getting, "cannot find table 0"?

At program startup, I'm creating a SQL Server CE table if it doesn't exist, and adding a record to it:
if (dbconn.isValidTable("hhs_settings") == -1) // <-- (IOW, it's *not* a valid table)
{
DBUtils.CreateSettingsTable();
AppSettings.WriteSettingsVal("beltprinter", "None");
}
public static void CreateSettingsTable()
{
try
{
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)";
dbconn.DBCommand(sqlddl, false);
}
catch (SqlCeException sqlcex)
{
MessageBox.Show("sqlcex - CreateSettingsTable " + sqlcex.Message);
}
catch (Exception ex)
{
MessageBox.Show("CreateSettingsTable " + ex.Message);
}
}
...but then when I open a form that reads from the table:
lblSelectedPrinter.Text = AppSettings.ReadSettingsVal("beltprinter");
...it fails with "cannot find table 0"
public static string ReadSettingsVal(string settingName)
{
string retVal = string.Empty;
string sqldml = string.Format("select setting_value from hhs_settings where setting_name = {0}", " + settingName + ");
// There should only be one value returned, but using the existing getDataSet() method for now...
DataSet dsSettingVal = frmCentral.dbconn.getDataSet(sqldml);
foreach (DataRow row in dsSettingVal.Tables[0].Rows)
{
if (retVal == string.Empty)
{
retVal = row["setting_value"].ToString();
}
}
return retVal;
}
Am I missing a step here, so that the table (hhs_settings) is not being created? Or...???
UPDATE
I'm still getting the same err msg even after changing all of the goofily formatted strings so that they are now:
string sqldml = string.Format("insert into hhs_settings (setting_name, setting_value) values('{0}', '{1}')", settingName, settingVal);
string sqlqry = string.Format("select setting_value from hhs_settings where setting_name = '{0}'", settingName);
And, I'm still seeing the msg "about to create hhs_settings" even though, presumably, the createtable sql was already good:
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)";
...and so, the table should have been created (which should have made the isValidTable() method return something other than -1 (the value it returns when the table is not found).
UPDATE 2
It helped (it works now) when I changed my ddl to:
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name nvarchar(40) not null, setting_value nvarchar(63) not null)";
string.Format("select setting_value from hhs_settings where setting_name = {0}", " + settingName + ");
The result of it is
select setting_value from hhs_settings where setting_name = + settingName +
what is obviously not a correct SQL. You need to use something like this
string.Format("select setting_value from hhs_settings where setting_name = '{0}'", settingName);
(also note quotation marks around the parameter placeholder)
but it much more better if you use parameters instead of generating SQL with all identifiers embedded
If you are using stored procedures, check out the name of the passing queries in it. It should exactly match with the front end and backend.

update sql statement with unknown name/amount of params

I have a classic ASP site, that I am slowly upgrading. I would like to create a function to securely update a SQL database without specifying parameters manually. Something just a tad more dynamic.
(I do not want to use entity framework or Linq)
Here is the code so far:
string updateSql = "UPDATE sometable" + "SET test1= #testData1 " + "WHERE a = #aData1";
SqlCommand UpdateCmd = new SqlCommand(updateSql, conn);
UpdateCmd.Parameters.Add("#testData1 ", SqlDbType.NVarChar, 10, "testData1 ");
UpdateCmd.Parameters.Add("#aData1", SqlDbType.NVarChar, 20, "aData1");
UpdateCmd.Parameters["#testData1 "].Value = "21515";
UpdateCmd.Parameters["#aData1"].Value = "32t3t";
UpdateCmd.ExecuteNonQuery();
pseudo-code (what I would like to achieve)
Create an Ilist covering all variables {get; set:} [validate type/length here]
For every variable that contains a value (without validation issues) create sql update string.
Execute it.
Possible problem:
The only problem I can foresee, is that the list may have 500 variables, but each SQL update may only have only 2 or 3 columns being updated. Is this not efficient?
you need to do something like this....needs more coding obviously....
static void Main(string[] args)
{
var values = new Dictionary<string, object>( );
values.Add( "name", "timmerz" );
values.Add( "dob", DateTime.Now );
values.Add( "sex", "m" );
SqlUpdate( "sometable", values );
}
public static void SqlUpdate( string table, Dictionary<string,object> values, string where )
{
var equals = new List<string>( );
var parameters = new List<SqlParameter>( );
var i = 0;
foreach( var item in values )
{
var pn = "#sp" + i.ToString( );
equals.Add( string.Format( "{0}={1}", item.Key, pn ) );
parameters.Add( new SqlParameter( pn, item.Value ) );
i++;
}
string command = string.Format( "update {0} set {1} where {2}", table, string.Join( ", ", equals.ToArray( ) ), where );
var sqlcommand = new SqlCommand(command);
sqlcommand.Parameters.AddRange(parameters.ToArray( ) );
sqlcommand.ExecuteNonQuery( );
}
I'm not sure I fully understand what you're trying to do, but this might be close to what you're looking for. You can create an arbitrarily long list of parameters and respective values, then build the corresponding UPDATE dynamically from that list.
//set up SqlCommand
SqlCommand UpdateCmd = new SqlCommand();
UpdateCmd.Connection = conn;
//build your dictionary (probably happens elsewhere in your code)
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("col1", "col1 value");
parameters.Add("col2", 42);
parameters.Add("col3", DateTime.Now);
//build a command string and add parameter values to your SqlCommand
StringBuilder builder = new StringBuilder("UPDATE sometable SET ");
foreach(KeyValuePair<string, object> parameter in parameters) {
builder.Append(parameter.Key).Append(" = #").Append(parameter.Key).Append(",");
UpdateCmd.Parameters.AddWithValue("#" + parameter.Key, parameter.Value);
}
builder.Remove(builder.Length - 1,1);
//set the command text and execute the command
UpdateCmd.CommandText = builder.ToString();
UpdateCmd.ExecuteNonQuery();
If you are using SQL Server 2008 you have the option of passing in the parameters and their values as a table to a Stored Procedure.
Inside the Stored Procedure you can join the table to be updated with the table passed in. That would probably be more efficient than creating hundreds of sep update statements.
Here is a link that may help http://msdn.microsoft.com/en-us/library/bb675163.aspx
And here is some sample code based on the code you posted in your question
First Create a table to play with and populate it with some data
CREATE TABLE [dbo].[sometable](
[Test1] [nvarchar](100) NULL,
[a] [nvarchar](100) NULL
) ON [PRIMARY]
Insert sometable Select 'rerere', '122342'
Insert sometable Select 'sfsfw', '343'
Insert sometable Select 'sfdrgss', '434545'
Insert sometable Select 'srgegrgeg', '3939932'
Then Create the Type in SQL Server
Create TYPE dbo.ParamsType AS TABLE
( Test1 nvarchar(100), a nvarchar(100) )
Then Create the Stored Procedure that accepts the type as a parameter
CREATE PROCEDURE usp_UpdateSomeTable
#Parameters dbo.ParamsType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE sometable
SET sometable.Test1 = p.Test1
FROM sometable INNER JOIN #Parameters as p
ON sometable.a = p.a;
END
GO
To test from SQL Server Management Studio you can run
Declare #t as ParamsType
Insert #t Select 'newValue1', '122342'
Insert #t Select 'morenew ', '343'
Insert #t Select 'again', '434545'
Insert #t Select 'OnceMore', '3939932'
exec usp_UpdateSomeTable #Parameters=#t
To Test from C# Try
static void Main(string[] args)
{
System.Data.DataTable YourData = new DataTable("Parameters");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "Test1";
YourData.Columns.Add(column);
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "a";
YourData.Columns.Add(column);
row = YourData.NewRow();
row["Test1"] = "newValue1";
row["a"] = "122342";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "morenew";
row["a"] = "343";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "again";
row["a"] = "434545";
YourData.Rows.Add(row);
SqlConnectionStringBuilder connString = new SqlConnectionStringBuilder();
connString.DataSource = "127.0.0.1";
connString.InitialCatalog = "SO";
connString.IntegratedSecurity = true;
using (SqlConnection conn = new SqlConnection())
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "usp_UpdateSomeTable";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter p = cmd.Parameters.AddWithValue("#Parameters", YourData);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ParamsType";
cmd.Connection = conn;
conn.ConnectionString = connString.ConnectionString;
conn.Open();
cmd.ExecuteNonQuery();
}
}

Categories