One of my clients using Dynamic SQL as part of the stored procedures. they don't want to change this. I am building MVC Website that should use that risky stored procedures. therefore, using parameters with Entity Framework will not solve the problem.
When i used the old and good SQL helper, i checked all the parameters inside the ExecuteQuery function and tried to find risky keywords. but now, when i'm using the native .NET Entity Framework 5 i have no shared function i can check there for that.
Using validators for every specific field is not good for me. is there an option to make overadding function for the Entity Framework part which execute the SP or Any other ideas how to solve that problem?
You do not say whether the dynamic SQL within the stored procedures uses parameters or not. Assuming not, the best solution would be to encode strings for single quotes at the point of query execution
e.g. create a methodstring EncodeSqlString(string s) { return s.replace("'", "''"); }
Then call this method
cmd.CommandText = "SP_FOO";
cmd.CommandType = CommandType.StoredProcedure;
EntityParameter param = new EntityParameter();
param.Value = EncodeSqlString(myString);
param.ParameterName = "MyParam";
cmd.Parameters.Add(param);
This would be the safest way as you are only encoding the string values as passed to the stored procedure, you are not using the values elsewhere and in contexts where the quote encoding isn't appropriate, and you will be mitigating the risk of truncation (as long as there is no truncation happening within the SPs). This will also only work correctly if the SPs only use the values for constructing SQL queries - if they do anything else with them then this probably isn't the way to go.
Only pass string values into this method. For other, non-quoted types you should ensure that they are the correct type before passing them into the parameter. e.g. for an int
string number = Request.QueryString["Number"];
if (int.TryParse(number, out myInt))
{
cmd.CommandText = "SP_BAR";
cmd.CommandType = CommandType.StoredProcedure;
EntityParameter param = new EntityParameter();
param.Value = myInt;
param.ParameterName = "MyParam";
cmd.Parameters.Add(param);
}
else
{
// handle appropriately but do not use value
}
Related
I have a simple SQL statement query that is executed as command from C# code. It is targetting DB2. I created variables for the server/schemas as follows. It throws error.
private const string DB2Query
= #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
I get this error.
ERROR [37000] [IBM][CLI Driver] CLI0118E Invalid SQL syntax. SQLSTATE=37000
However, I don't get that error when executing from SQL as follows:
SELECT Name as Name
FROM MyServer..FOR3.Application
WHERE ID = 'MOM'
To support this, I tried to also do something like below in code, still throws different error.
private const string DB2Query
= #"SELECT Name as Name FROM {ServerName}..{Schema}.Application WHERE ID = ?";
It throws error on this line of code:
DataApplicationBlockHelper<string>.Get(db, dbCommand, Obj);
UPDATE
I found the culprit. It's not replacing the {Schema} placeholder. When I actually removed that from query and placed the schema name, it worked like a charm. It's a .net thing I believe? Can someone please help how to replace {Schema} with a value fetched from web.config?
While I can't really speak to the syntax of DB2 queries themselves, so I'll rely on your assertion that the query itself should work...
What you have in C# is simply a string and nothing more:
private const string DB2Query = #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
Note that there's no need for the # operator in this string definition, so let's simplify:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
While this string appears intuitively to have a placeholder that can be replaced with a value, if there's no code which does that anywhere then it won't happen. For that you have a few options. For example, you can use a placeholder that string.Format() understands:
private const string DB2Query = "SELECT Name as Name FROM {0}.Application WHERE ID = ?";
And then later in a method somewhere, when you want to use that string, apply the format value to it:
var sql = string.Format(DB2Query, someVariable);
In this case someVariable (which doesn't even need to be a variable and could be a string literal) would be used to replace the placeholder in the string.
Or, if you want to keep the named placeholder, you can potentially replace it manually:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
and later in a method:
var sql = DB2Query.Replace("{Schema}", someVariable);
This would observably accomplish the same thing, perhaps with an extremely minor performance difference.
You could also take advantage of both approaches by using the more recent language feature of string interpolation. This would use the $ operator to apply format placeholders in place directly. I don't think you can use this in a const, it's more for a local variable. Something like this:
var sql = $"SELECT Name as Name FROM {someVariable}.Application WHERE ID = ?";
This would still perform the same replacement, putting someVariable where the placeholder is, it's just using a more concise syntax than a call to string.Format(). One thing to note about this syntax is that it makes it look more like this interpolation is happening directly in-place on the string. It's still a multi-step process behind the scenes, which is why it likely won't work on a const or on class members at all (and should I imagine produce a compiler error).
Remember that strings are immutable, so any operation you perform which modifies a string would be returning a new string rather than modifying the existing one in place.
In any case, you'll of course also need to apply your query parameter for the ? placeholder. Note that what C# considers to be a placeholder in a string formatting/interpolating operation and what DB2 considers to be a placeholder for a query parameter are two entirely different things which happen at different times in different environments. (One in the .NET runtime, one in the database server's query execution.) But again, I'm relying on your assertion that the database query itself works and the only problem we're focusing on here is the C# string placeholder.
I need to log every change of variable values in a SSIS, at package level.
I have a SQL Task Editor that receives, as parameters, System::VariableName and System::VariableValue. I use this parameter to log a line in a DB log table.
Everything is working fine BUT... so far i only have numeric variables so, in the "Parameter Mapping", i specificed that the variable with name "System::VariableValue" is a NUMERIC DataType.
What happens when I will have a Datetime variable or a varchar variable? How can I handle variables of different types in this case?
Thankx
Make the datatype sql_variant, which will accommodate the different datatypes.
More on that here: https://msdn.microsoft.com/en-us/library/ms173829.aspx
Also, SSIS does have a on variable changed event which maybe helpful for you, though it sounds like you have a good custom solution.
m
EDIT:
So there is some documentation on MSDN that seems to suggest nvarchar maps to sql_variant. This worked for strings, but left the value empty for numeric values. Looking at the type for Variable.Value in the variables collection in a script task, it notes the type is empty. So I tried setting the data type to null, which did not work at all.
Finally, I created a script task to do the job and this worked correctly. The script task must call a stored proc in order for it to handle all the data types. I tested it with string, int, double and bool, all of which worked correctly.
Here is the table I used:
create table logging (myval sql_variant);
Here is the proc definition:
Create proc [dbo].[insertLog]
#myVal sql_variant
AS
Begin
set nocount on;
insert into logging values(#myVal);
end;
In the OnVariableValueChanged event handler, I added a script task with the following code:
using System.Data.OleDb;
public void Main()
{
// TODO: Add your code here
object val = Dts.Variables["System::VariableValue"].Value;
OleDbConnection conn = new OleDbConnection(Dts.Connections[".\\sql2016.Test"].ConnectionString);
using (conn)
{
conn.Open();
OleDbCommand cmd = new OleDbCommand("insertLog", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#myVal", val);
cmd.ExecuteNonQuery();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
".\sql2016.Test" is the name of the connection manager in the package.
Please see if this will work for you,
m
In one of our application the parameters passed to a stored procedure in this way
Dim parm As New SqlParameter("searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
and the procedure contains a parameter as #searchText
i.e. the parameter name passed from the code is searchText and that in the stored procedure is #searchText .
But it is working properly, I am always getting the required results.
So my question is like so there is no need to specify # before the parameter? Whether it will append #, can anyone please give an answer for this.
According to the documentation, the name must start with an #:
The ParameterName is specified in the form #paramname.
According to the source code (have a look at SqlCommand and SqlParameter.ParameterNameFixed in the reference source), an # is added automatically, if needed.
So yes, it works, but it's an undocumented feature. Best practice recommends that you do not rely on this and manually prefix your parameter name with an #.
Ref: SqlParameter.ParameterName Property and IDataParameter.ParameterName Property
The ParameterName is specified in the form #paramname. You must set ParameterName before executing a SqlCommand that relies on parameters. If you are using Sql Server as Database then you must specify # before
the parameter name.
your parameter name must be same as at backend eg. you have #searchText then in your parameter specification it must be SqlParameter("#searchText" ..
your code should be like this
Dim parm As New SqlParameter("#searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
Note: Oracle and SqLite use different use different character to specify parameter and there may be # symbol is not used specified by the specification of ado.net.
Edit: By comments
As you specified the link, it is also some sort of fix, but as per the msdn documentation, you must specify the positional parameter with '#' whether you are using any data provider oledb, sql, odbc. Ref
if (0 < parameterName.get_Length() && '#' != parameterName.get_Chars(0))
{
parameterName = "#" + parameterName;
}
Its not compulsory to specify the #. However, its a best practice.
Its similar in analogy to strings. There certainly is no harm in defining strings as such in .NET:
string s;
//Rest of the code follows;
But again, its a best practice to define them as :
string s = string.Empty;
You see, its a question of conventions and best practices!!!
I recommended you to use add "#" marker with your parameter name.
SqlParameter helps to add automatically, but others' parameter might not to.
Is the "#" symbol required? Yes and No. When you add a parameter using DbCommand, it's optional regardless of whether you're using SQL Server or not:
// Look Ma no # required!
DbCommand command = database.GetStoredProcCommand("StoredProctologistAndGambler");
database.AddInParameter(command, "Bet", DbType.Int32, fromLineNumber);
database.AddOutParameter(command, "Diagnosis", DbType.String, -1);
If you're going to reference the command later, however, the "#" prefix is required. Microsoft figured it was to hard to carry it over to the rest of the API.
var examResult = command.Parameters["#Diagnosis"]; // Ma! Microsoft lied! It requires the "#" prefix.
I am using Enterprise Library to query database and usin GetStoredProcCommand to query database. The problem arises when I want to use output parameter.
DbCommand cmd = db.GetStoredProcCommand("storedProcedureName", param1, param2, param3, ...);
Now, if I want to add output parameter, I can't simply write db.AddOutParameter(cmd, "#TotalNumber", DbType.Int64, sizeof(Int64));
I would have to add all the input parameters using db.AddInParameter(cmd, "param1", DbType.Int32, param1);
If you have 10 input parameters, adding them one by one in your code does not like alright and you just wish you could go back to adding them all in the same function call at once. Is there a way of doing this while also use output parameter?
Basically all you need to do is pass a null (or anything really) for the output parameter and you don't have to bother with db.AddOutParameter
From the comments on DbCommand GetStoredProcCommand(string storedProcedureName,
params object[] parameterValues)
The parameters for the stored procedure will be discovered and the values are assigned in positional order.
What they don't tell you is that count of the Db Parameters isn't the same as the params you'll get a Resources.ExceptionMessageParameterMatchFailure So for every output DB Parameter you still need to pass somthing.
What they also don't tell you is that when its assigning the value it checks the direction of the database parameter. If its output it just skips over your value.
This is probably fairly straightforward but i can't seem to find a reasonable explanation in any documentation.
I'm trying to use an NHibernate.ISQLQuery and using SetResultTransformer() to return a custom set of results from a custom SQL query. Like so:
public virtual IList<T> GetSQLObject<T>(string sql, IDbParameter[] parameters = null)
{
ISQLQuery qry = _sess.CreateSQLQuery(sql);
qry.SetResultTransformer(Transformers.AliasToBean(typeof(T)));
if (parameters != null) {
foreach (IDbParameter parameter in parameters) {
qry.SetParameter(parameter.Name, parameter.Value);
}
}
return qry.List<T>();
}
From looking at the examples, it seems that in the sql query I have to use parameters in the format :param1 instead of #param1 as I would in a standard SQL query. If i use the latter syntax in the query, it throws an error at qry.SetParameter().
Is there a reason why ISQLQuery/NHibernate requires them in this format and won't work with the normal syntax?
SQL Server uses #param, but not every other database does. For example, MySQL uses ?param
NHibernate allows you to swap out 1 database implementation for another with little to no reworking of your DAL. It sets the parameters based on the database you configured when you setup the NH Configuration.
Edit: Also I think :param came about from Hibernate being targeted at Oracle when it was initially developed, since Oracle uses :param
Phil has answered the "why"; so perhaps I can recommend a "how"; why not just add a new extension method to the IDbParameter type (something like .GetNHibernateName() ) that will return the parameter name with the "#" replaced with a ":"; that should be trivial to implement.