string query = #"SELECT ColA, ColXML FROM TableT WHERE ColXML.exist('/SuperNode/Node/SubNode[.=({0})]') = 1";
string param = "''value1'',''value2'',''value3''";
string sQ = string.Format(query, param);
A: dbContext.ExecuteQuery(sQ);
B: dbContext.ExecuteQuery(query, param);
A executes and returns result but B doesn't.
Any reason for this? Also, does the param gets validated for common SQL injection patterns?
Thanks for any pointers!
http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx
You are trying to use the overloaded version of ExecuteQuery that receive parameters. Parameters must be passed as an Object array, for example:
object[] param = new Object[] { "value1", "value2", "value3" };
Anyway, your query receives only ONE parameter:
string query = #"SELECT ColA, ColXML FROM TableT WHERE ColXML.exist('/SuperNode/Node/SubNode[.=({0})]') = 1";
It seems that you want to pass a single parameter composed by three xml values. I am not an XQuery expert but you can try this:
object[] param = new Object[] { "''value1'', ''value2'', ''value3''" };
string query = #"SELECT ColA, ColXML FROM TableT WHERE ColXML.exist('/SuperNode/Node/SubNode[.=({0})]') = 1";
For anyone stumbling upon this via google as I did, ExecuteQuery does not simply pass the command and parameters to string.Format as that would create an injection vulnerability.
It replaces the {0}, {1}, ... in the command with "#p0", "#p1" etc, and then adds your parameters as parameters to the sql command. This can be confirmed by setting the .Log property on the data context to see the command actually executed.
So the OP's example doesn't work because
f(x) where x = "a,b,c"
is only equivalent to
f(a,b,c)
if we're doing a straightforward string substitution. If x is a "proper" SQL parameter then it doesn't work.
Related
I am currently developing an application using C# (Visual Studio 2019)and SQL Server 2017 using Dapper. Below is a routine that works fine right now to execute a stored procedure in SQL Server.
The C# code uses the MVVM framework, and I currently have 60 model classes to map the tables in the SQL Server database. There are about 120 stored procedures that produce results that are mapped to the model classes using Dapper.
Consider the following code snippet as pseudocode (but it actually works to execute the stored procedure and return the correct results).
public List<SomeDefinedModel> ExecuteSQLSPROC(string SPROCName, SqlParameter[] parameters)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(DBHelper.CNNVal("MYLocalServer")))
{
System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand();
command.Connection = (SqlConnection)connection;
command.CommandText = SPROCName;
DynamicParameters parms = new DynamicParameters();
for (int i = 0; i < parameters.Length; i++)
{
var parmname = parameters[i].ParameterName;
var parmvalue = parameters[i].Value;
parms.Add(parmname, parmvalue);
}
var output = connection.Query<SomeDefinedModel>(SPROCName, parms, commandType: CommandType.StoredProcedure).ToList();
return output;
}
}
I want to change this routine so the routine so that it method signature is as follows:
public List<TheNameOfTheModelToMap> ExecuteSQLSPROC(string SPROCName, SqlParameter[] parameters, TheNameOfTheModelToMap)
That is, the designation of the model to which Dapper is to map is a variable - it can be any valid model. I have define the variable TheNameOfTheModelToMap as type object, and the return value as var, as List<object>, and so forth. They produce errors because of this syntax:
var output = connection.Query<TheNameOfTheModelToMap>(SPROCName, parms, commandType: CommandType.StoredProcedure).ToList();
I want to comply with the principles of DRY and code just one method to accept ANY model designation, ANY stored procedure designation and have Dapper map the query results. Any possibilities?
I think you just need one generic method like so:
public List<T> ExecuteSQLSPROC<T>(string SPROCName, SqlParameter[] parameters)
{
using (var connection = new System.Data.SqlClient.SqlConnection(DBHelper.CNNVal("MYLocalServer")))
{
var parms = new DynamicParameters();
foreach (var parameter in parameters)
parms.Add(parameter.ParameterName, parameter.Value);
var output = connection.Query<T>(
SPROCName,
parms,
commandType: CommandType.StoredProcedure
)
.ToList();
return output;
}
}
And every other method becomes a specialized invoke of it:
public List<SomeDefinedModel> ExecuteOneProc(SqlParameter[] parameters)
=> ExecuteSQLSPROC<SomeDefinedModel>("OneProcName", parameters);
Is this ok for your use case?
If you need one procedure for single values (which would not use Dapper's Query method) you would just create specialized variations of the ExecuteSQLSPROC like ExecuteStoredProcSingleRow or something along those lines.
I have a .NET program with a dataset to an access/a sql DB.
I wrote a query and used 2 parameters, but I got an error:
Error in WHERE clause near '#'.
Unable to parse query text.
My query is:
SELECT DocID, DocCustomerNumber,
DocSessionID, DocTitle, DocKlaser, DocBarcodes
FROM VTblASMCustomersDocsAndGroupCodes
WHERE DocCustomerNumber = #cusNum AND
DocSessionID = #asmNum
Microsoft Access doesn't use named parameters. It uses positional parameters. So the order of the parameters is important when you set the values of the parameters.
Change your query to this:
SELECT DocID, DocCustomerNumber,
DocSessionID, DocTitle, DocKlaser, DocBarcodes
FROM VTblASMCustomersDocsAndGroupCodes
WHERE DocCustomerNumber = ? AND
DocSessionID = ?
Then use this code to pass the parameters:
cmd.Parameters.AddWithValue("param1", param1); // param1 = value of DocCustomerNumber
cmd.Parameters.AddWithValue("param2", param2); // param2 = value of DocSessionID
I've just checked the new futures in Entity Framework Core 2.0. There is a really nice feature in it called "String interpolation in raw SQL methods" which is described here.
It says that this code:
var city = "Redmond";
using (var context = CreateContext())
{
context.Customers.FromSql($#"
SELECT *
FROM Customers
WHERE City = {city}");
}
creates this query:
SELECT *
FROM Customers
WHERE City = #p0
It is really strange to me! How FromSql method is written as it has just and input of type string.
How does it understand it is an interpolated string, and then create a parameter #p0 query for it? How can I write a method like FromSql aware of how its string parameters are created?
The way it woks is FromSql( accepts a FormattableString.
When you use $"..." that returns a FormatableString, that class allows you to inspect the passed in string and see all of the { } blocks inside of and the objects they represent. This allows the framework to replace those { } blocks with a placeholder parameter #p0 then create a new parameter using something similar to new SqlParameter("#p0", formatableString.GetArgument(0))
I am trying to get output value from the dbcontext using the method FromSql(...).
If i execute it on SSMS, it works but not working in EF core.
My SP param:
#totalItems VARCHAR(MAX) OUTPUT
I've removed other params for readable and point out the issue. The records are coming but the OUTPUT parameter is always null.
Linq
IQeryable<T> = _context.Set<T>().FromSql("Sp_Todo #totalItems OUTPUT", // i also tried with OUT or without both Out/Output but no luck
new SqlParameter("#totalItems", SqlDbType.Varchar)
{ Direction = ParameterDirection.Output});
I don't' have access to your whole code, but nothing ever gets executed against your queryable source until you try to enumerate it. So, probably it didn't run the Stored Procedure when you tried to get the OUTPUT.
To force immediate query evaluation you can do it:
IQueryable<T> foo = _context.Set<T>().FromSql("Sp_Todo #totalItems OUTPUT", new SqlParameter("#totalItems", SqlDbType.Varchar) { Direction = ParameterDirection.Output });
//totalItems output still null
var bar = foo.ToList()
//totalItems output not null anymore
Here's how I'm doing it:
var _companyCode = new SqlParameter("CompanyCode", "HST");
var _sMsg = new SqlParameter("sMsg", "")
{
Direction = ParameterDirection.Output,
SqlDbType = SqlDbType.VarChar
};
var sql = "exec temp_get_company #CompanyCode, #sMsg OUTPUT";
var result = context.Set<Company>().FromSql(sql, _companyCode, _sMsg).ToList();
var yourOutput = _sMsg.Value.ToString();
How can I execute a stored procedure that takes in parameters without having to specify the prameters name? The name of the parameter in the stored procedure may change from CustomerID to CustID so I don't want to have to keep changing my code.
Rather than doing what is provided below where you specify the parameter name -
command.Parameters.Add("#dtStart", SqlDbType.DateTime);
command.Parameters["#dtStart"].Value = startDate;
command.Parameters.Add("#CustomerID", SqlDbType.NChar);
command.Parameters["#CustomerID"].Value = customerID;
I am looking to do something like this -
command.Parameters.Add(startDate, customerID);
The name of the parameter in the stored procedure may change from CustomerID to CustID
Slap the person who does that.
Parameter names are your reliable way of identifying a parameter. The other option is sequence, seems a lot more flaky.
I don't think you can create a SqlParameter object without specifying its name. However, you should be able to use the DeriveParameters method (see MSDN) to get a collection of parameters with the names automatically retreived from the SQL server.
You can find an example here. It looks roughly like this:
SqlCommand command = // create a command for calling the stored procedure
SqlCommandBuilder.DeriveParameters(command);
// Now you can set values of parameters in a loop
for(int i = 0; i < command.Parameters.Length; i++) {
var parameter = command.Parameters[i]
// Set value of ith parameter
}
You can create a nameless SQL parameter if you force its name to null or empty after it's been added to the Parameters collection, something like this:
var par = cmd.CreateParameter();
par.Value = myValue;
cmd.Parameters.Add(par); // this will change the name to "ParameterX"
par.ParameterName = null;
Use Parameter Discovery, scroll down on: http://msdn.microsoft.com/en-us/library/ff664692(PandP.50).aspx
Using Unnamed parameters is only possible with OdbcCommand and OleDbCommand object parameters.
You could use SQL's exec, which does not ask for parameter names:
command.CommandText = string.Format(
"exec dbo.YourProcedure {0}, '{1}'",
intParameter,
stringParameter.Replace("'","''")
);
command.ExecuteNonQuery();
If your parameter source is untrustworthy, be sure to escape single quotes in string parameters. It's done for stringParameter in the snippet above.