I am trying to use interceptor to add WITH (NO LOCK) for some queries (not for all queries, so ReadUncommitted is not a choice).
Code looks like this:
var rawSql = sql.ToString();
if (!rawSql.Contains(IQueryOverExtensions.QueryHintNoLockString))
return sql;
var noWhere = rawSql.Substring(0, rawSql.IndexOf(WhereKeyword, StringComparison.InvariantCulture));
var from = noWhere.Substring(noWhere.IndexOf(FromKeyword, StringComparison.InvariantCulture));
var fromWithNoLock = from.Replace("_ ", $"_ {WithNoLock} ");
var sqlWithNoLock = rawSql.Replace(from, fromWithNoLock);
return base.OnPrepareStatement(new SqlString(sqlWithNoLock));
Here, I take part form FROM clause to WHERE clause and to each alias add WITH (NO LOCK)
The issue is, that the final SQL's parameters are all "?" and exception is thrown that SQL is not valid. Why parameters are not filled in and how to fix it?
Thanks in advance
Finally I got it. I had to get number of parameters by calling sql.GetParameterCount() and then replace all question marks with #p0, #p1, #p2, etc.
Related
I have a difficult SQL query, and I don't want to rewrite it on Linq. The problem is: I have two equal tables and I must use one of them in depending on the some condition. So, to pass parameter (the name of table) I use this:
List<Variables> lst = db.Database
.SqlQuery<Variables>(s, new SqlParameter("tableSource", sourceTable))
.ToList();
And My query like this:
SELECT #tableSource.PlanId,
#tableSource.PlanSmall AS PlanImg,
#tableSource.NOb,
...
It Doesn't works, could someone help me, please?
You can't use SqlParameter for this. It is meant to be used to set values for parameters in WHERE clauses for instance. You may consider using a construct like
if (someParam.Equals("thisValue")
{
return "SELECT * FROM thisTable";
} else if (someParam.Equals("thatValue")
{
return "SELECT * FROM thatTable";
}
Hey I was using parametrized queries for my application which worked just fine but now (I don't know why) they aren't replaced anymore with the values...
So instead of running something like "SELECT [TABLE_NAME] FROM [MyDefinetelyExistingDatabase]"; it tries to execute "SELECT [TABLE_NAME] FROM [#targetDatabase]"; which, of course, will fail.
var dataBaseToGetTablesFrom = "MyDefinetelyExistingDatabase";
var results = new List<string>();
const string query = #"SELECT
[TABLE_NAME] AS tableName
FROM
[#targetDatabase].[INFORMATION_SCHEMA].[TABLES] ;";
using (var context = new ConnectionHandler(true))
{
if (context.Connection.State != ConnectionState.Open)
throw new ConnectionFailedException(context.Connection.State);
using (var command = new SqlCommand(query, context.Connection))
{
command.Parameters.AddWithValue("#targetDatabase", dataBaseToGetTablesFrom);
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
return results.ToArray();
while (reader.Read())
results.Add(reader.GetString(0));
}
}
}
return results.ToArray();
I now tried different formats and things to add the parameters but it results in the same...
I don't want to do this by inserting the values into the query directly via string.Format eg but I want to have those parameters (which work properly at different places in the code (???) but not where I want.
In fact, I need to use parameters in every statement and must be able to address different databases by calling them like [DB].[Table-Schema].[Table]
[EDIT]
Hey guys, figured the problem some days ago and thought I share it with you.
As far as I have noticed, my problem at the whole was to try to replace the databasename and / or in some other examples, the table name as well.
So this won't work which makes clearly sense to me as the server can't prepare to execute a statement if it doesn't even know on which table it should work and therefore doesn't know anything about the structure etc.
So I changed my statements to fit my new knowledge and it worked as expected like a charm.
I don't know what ConnectionHandler is, but if that is your own code you can implement it with SqlConnectionStringBuilder which will allow you to use a variable to assign the InitialCatalog instead of putting the database name in the query. This would be preferable to dynamic sql which requires careful sanitization.
You would need dynamic sql for this something like.....
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' SELECT [TABLE_NAME] AS tableName '
+ N' FROM ' + QUOTENAME(#targetDatabase) + N'.[INFORMATION_SCHEMA].[TABLES]'
Exec sp_executesql #Sql
I'm trying to fix a problem in an ASP.NET application which uses DataContext.ExecuteQuery to execute sql statement. The query has this part:
LEFT OUTER JOIN dbo.Contacts ON dbo.Accounts.SalesRepId = dbo.Contacts.ContactId WHERE " + string.Join(" AND ", whereClauses) + #"
where "whereClauses" is a List of strings. This way, a search functionality is implemented into the query. Problem appears when an open search is run for a string like "O'Donnel" which obviously run query into an error. I have been asked to fix this problem. My approach was to substitute the where statement with "{0}" in the query and then pass it as a parameter in ExecuteQuery method. What I achieved is:
LEFT OUTER JOIN dbo.Contacts ON dbo.Accounts.SalesRepId = dbo.Contacts.ContactId {0}
var where = "WHERE 1=1";
_db.ExecuteQuery<AccountModel>(query, where)
but it doesn't work? When I substitute the whole where part with just a parameter for it everything is fine:
LEFT OUTER JOIN dbo.Contacts ON dbo.Accounts.SalesRepId = dbo.Contacts.ContactId WHERE 1={0}
var where = "1";
_db.ExecuteQuery<AccountModel>(query, where)
My question - is it then possible to inject the whole where statement dynamically into DataContext.ExecuteQuery method, or can I pass specific parameters only using this method?
Here's the help page for the ExecuteQuery method.
You can't pass the whole where clause as a parameter because a query like select * from myTable #param isn't valid SQL, whereas select * from myTable where myField = #param is valid SQL.
So, in answer to your specific question - no, you can't do this by passing parameters to ExecuteQuery.
However, why not build up the query in code before you pass it to ExecuteQuery?
e.g.
var query = "select * from myTable where 1=1" + string.Join(" AND ", whereClauses);
var result = _db.ExecuteQuery<AccountModel>(query);
I am running into a very interesting bug (or feature).
I have a ADO Command object that makes a call to the database.
The call looks similar to:
cmd.CommandText = "uspMySearch";
cmd.CommmandType = Command.StoredProcedure.
cmd.Parameters.AddWithValue("#SearchBy", searchBy)
// The value of searchBy is: '( FORMSOF (INFLECTIONAL, steve''s) AND FORMSOF (INFLECTIONAL, game) )'
int result = (int)cmd.ExecuteScalar();
// The result returned is 0. I was expecting 1.
When I execute the same query in SQL/Query Analyzer, I get a different result.
The sql looks like this:
EXEC uspMySearch #SearchBy = '( FORMSOF (INFLECTIONAL, steve''s) AND FORMSOF (INFLECTIONAL, game) )'
// The result returned is 1. This is the expected result.
In order to confirm I was calling the correct stored procedure, I modifed uspMySearch to return a random number. I was calling the right Sp!
Anyone have any insights as to whats going on here?
Thanks.
Steve
Environment
SQL/Server 2008 R2
.NET 4.0
I believe you've got an extraneous single quote in your searchBy variable in the C#, try:
// note steve's rather than steve''s, shouldn't need to escape the single quote
string searchBy =
"( FORMSOF (INFLECTIONAL, steve's) AND FORMSOF (INFLECTIONAL, game) )";
...
cmd.Parameters.AddWithValue("#SearchBy", searchBy);
int result = (int)cmd.ExecuteScalar();
Another possibility, raised by #FlyingStreudel in a comment, is that your stored procedure is not using SELECT to return the value. If you're using RETURN, you should instead try:
var retval = new SqlParameter("#RETVAL", SqlDbType.Int);
retval.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(retval);
...
cmd.ExecuteNonQuery();
int result = Convert.ToInt32(retval.Value);
ExecuteScalar is not for executing Stored Procedures - for that you you use ExecuteNonQuery combined with Parameters with direction Output / InputOutput / Return.
http://msdn.microsoft.com/en-us/library/system.data.parameterdirection.aspx
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx
EDIT - perhaps the following works too (although wouldn't recommend it):
cmd.CommandText = "uspMySearch";
cmd.CommmandType = Command.StoredProcedure;
cmd.Parameters.AddWithValue("#SearchBy", searchBy).Direction = ParameterDirection.InputOutput;
int result = (int)cmd.ExecuteScalar();
var Result = cmd.Parameters ["#SearchBy"].Value;
I'm going to throw out a wild guess, but I'm guessing it has something to do with your cast. Can you change the code to:
object result = cmd.ExecuteScalar();
And use the debugger to inspect the result variable to see what it is (and what it's type is). This may give you (or me) a clue as to what's going on.
When you say the result returned is 1 - how is it returned; resultset or printed out?
Normally when using ExecuteScalar() I use a query which selects a single column value (i.e. of the format SELECT ... FROM ...).
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.