I'm having troubles with my param string on my WHERE clause of my method. I'm trying to do a query like this:
string query = "SELECT * FROM table WHERE Name #Filter OR Email #Filter";
And then inside my switch their cases will be like:
public async Task<List<DTO>>Get(FilterDTO filter)
{
foreach(var f in filter.fields)
{
switch(f.Operator)
{
case 1:
if(f.Value == null)
{
f.Value = "IS NULL";
queryResult = connection.Query<Model>(query, new {Filter = f.Filter});
break;
}
else
goto case 2;
I'm having a lot of troubles with my param string because I cannot see what is failing on debug
Notice my real value name is FilterValue (this code it's a simplification)
I get this error:
Incorrect syntax near #Filter
So I know something is wrong about my param string input but I cannot see what is it
Rewrite the query to:
SELECT * FROM table WHERE (#Filter is null and Email is null or #Filter = Email)
OR (#Filter is null and Name is null or #Filter = Name)
In that case you can set the parameter Filter = null and get the expected result.
Probably Dapper is using this query:
SELECT *
FROM table
WHERE Name = IS NULL OR Email = IS NULL
which is invalid.
First create a profiler to see what query dapper is actually executing on the database (is easier to debug this situations - I recommend MiniProfiler lib).
Second, the IS NULL filter is meant to be on the query string OR use: Name = NULL rather than IS NULL
I don't think you can use parameters from Dapper like this.
To have a valid query you'd need to have
SELECT * FROM table WHERE Name = #Filter OR Email = #Filter;
But that won't work with null.
You actually need to create the query, not just the parameters
const string query = "SELECT * FROM table WHERE Name {0} OR Email {0}";
var result = conn.Query(string.Format(query, filterValue));
Edit:
If you can use Dapper.SqlBuilder you can build the query with Dapper as well.
Something like this:
var sqlBuilder = new SqlBuilder();
string myFilterValue = null;
if (string.IsNullOrEmpty(myFilterValue))
{
sqlBuilder.OrWhere("Name IS NULL").OrWhere("Email IS NULL");
}
else
{
sqlBuilder.OrWhere("Name = #Filter", new { Filter = myFilterValue }).OrWhere("Email = #Filter", new { Filter = myFilterValue });
}
var template = sqlBuilder.AddTemplate("select * from table /**where**/");
using var conn = new SqlConnection("");
var result = conn.Query(template.RawSql, template.Parameters);
Related
I am trying to do resource filtering the same way I would be using queryable in EF Core but this time with Dapper. In my case, I wish to get all reports and want to be able to query based on the passed filter parameters. This is so far what I have:
public async Task<List<IssueReport>> GetAll(int? userId = null, bool? onlyOpenIssues = null, int? issueType = null)
{
await using var connection = new SqlConnection(_connectionString);
var baseQuery = "SELECT * FROM IssueReports";
//TODO: Add resource filtering logic here
var result = await connection.QueryAsync<IssueReport>(baseQuery);
return result.ToList();
}
What can I do in Dapper to dynamically handle filtering for all those passed parameters as well as handling the case of them being null and not querying them in those cases?
What can I do in Dapper to dynamically handle filtering for all those passed parameters as well as handling the case of them being null and not querying them in those cases?
You can check my demo:
Almost the same in efcore, use sql command to execute the query,open database and fill
the result to a list, then close database and return the result.
In this example, when I pass userId=1 to this action, it will search the table where Id=1:
public async Task<List<IssueReport>> GetAll(int? userId = null)
{
var result =new List<IssueReport>();
var baseQuery = #"SELECT * FROM IssueReport WHERE Id = #Id";
using (SqlConnection connection = new SqlConnection("***your connection string**"))
{
connection.Open();
result = (List<IssueReport>)connection.Query<IssueReport>(baseQuery,new { Id = userId }); //#Id is defined like this
connection.Close();
}
return result.ToList();
}
Result:
I'm trying to get a return value from an insert query using Dapper.
Here's how I try to make it work:
// the query with a "returning" statement
// note : I have a trigger that sets the Id to a new value using the generator IF Id is null...
string SQL = "UPDATE OR INSERT INTO \"MyTable\" (\"Id\", \"Name\") " + "VALUES (#Id, #Name) RETURNING \"Id\"";
using (var conn = new FbConnection(MyConnectionString)) {
var parameters = new DynamicParameters();
parameters.Add("Id", null, System.Data.DbType.Int32);
parameters.Add("Name", "newName", System.Data.DbType.String);
// --- also add the returned parameters
parameters.Add("retval", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
// execute the query with Dapper....
conn.Execute(SQL, parameters);
// expecting the new ID here but it is ALWAYS null....!!!
var newId = parameters.Get<object>("retval");
}
Now to make sure my query is ok and not the source of the problem here, I implemented a similar code with my actual connector (Firebird in this case), as follows:
using (var conn = new FbConnection(MyConnectionString)) {
FbCommand cmd = new FbCommand(SQL, conn);
cmd.Parameters.Add("Id", null);
cmd.Parameters.Add("Name", "newName");
FbParameter pRet = cmd.Parameters.Add("retval", FbDbType.Integer);
pRet.Direction = ParameterDirection.ReturnValue;
conn.Open();
cmd.ExecuteNonQuery();
// => the new value is NOT null here, it returns the correct id!!
var newId = Convert.ToInt32(pRet.Value);
conn.Close();
}
What is my mistake in the Dapper code? Why is one version OK and NOT the other? I've read that Dapper executes ExecuteNonQuery() so I'm not expecting this to be the cause.
The returning clause acts like select, in that it returns data in a results grid. As such, your query should be executed as a query. This also has the advantage that it significantly simplifies the calling code:
var newId = conn.QuerySingle<int>(SQL, new { Id = (int?)null, Name = "newName" });
If you need additional fields, this can be extended to use a custom return type that matches the columns coming back, or a value-tuple. For example:
var row = conn.QuerySingle<MyTable>(SQL, new { Id = (int?)null, Name = "newName" });
or
var row = conn.QuerySingle<(int id, string name)>(SQL, new { Id = (int?)null, Name = "newName" });
-- edit
You can access the returned values by
int iVal = row.Result.id;
string sVal = row.Result.name;
The biggest drawback to Dapper's Execute() is that it returns "number of rows impacted" (by updates, deletes, etc)... even if all occurs in a transaction which, after an error occurred, was cancelled via ROLLBACK. The return-value still holds the impacted-row-number before Rollback, tho the transaction was not committed. Yikes!!
DynamicParameters() was more complex, but worked. But in Moq Tests, I encountered a number of exceptions that I couldn't easily resolve.
My solution (similar to Marc and neggenbe's) followed these steps:
In the SQL stored-procedure, return an integer-value via,
SELECT -1 -- 0 for success, -1 for error
note--> SQL-Returns (ie. RETURN(1)) are ignored for some reason.
Use Dapper as such,
int result = conn.QueryFirst<int>(SProcName, new { id = req.Id, value = req.Value }, commandType: CommandType.StoredProcedure);
note--> Other commands work as well with differing return types:
QueryFirst: result = key/value where value=[return value]
QueryFirst<int>: result = integer
QuerySingle: Detailed by Marc and neggenbe's answer.
Check result appropriately, as the above examples.
I am unable to use WebMatrix database method Query() with 2 interpolate string.
Actual Code: (Not working)
#{
var db = Database.Open("WebPagesMovies") ;
var selectCommand = "SELECT * FROM Movies";
var item = "";
var search = "";
if(!Request.QueryString["searchTitle"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE #0 LIKE #1";
item = Request.QueryString["item"];
search = "%" + Request.QueryString["search"] + "%";
}
var selectedData = db.Query(selectCommand, item, search); // **NOT WORKING**
var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
}
Working
#{
var db = Database.Open("WebPagesMovies") ;
var selectCommand = "SELECT * FROM Movies";
var search = "";
if(!Request.QueryString["searchTitle"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE Title LIKE #0";
item = Request.QueryString["item"];
search = "%" + Request.QueryString["search"] + "%";
}
var selectedData = db.Query(selectCommand, item, search);
var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
}
Why am I unable to interpolate 2 string like item and search in Query() method? Does it not supported in ASP.NET?
On debugging I can see that values are assigned to item and search. But values are not passed to Query() method.
Official Doc:
https://www.asp.net/web-pages/overview/getting-started/introducing-aspnet-web-pages-2/form-basics
https://msdn.microsoft.com/en-us/library/webmatrix.data.database.query(v=vs.111).aspx
That's a very interesting approach. What your code is doing is using parameterized SQL. The parameters are #0, #1 and so. The db.Query statement passes the parameters into SQL. I believe there is a significant restriction on where these parameters can be used. They can only be used where data can go, not where SQL commands or table names or fields can go. So because Title is a field name, it can't be parameterized.
So guessing that sometimes you want to search on the title, sometimes on the release date, and so on, you have several choices.
One is to examine the incoming item variable, maybe in a switch statement, and then have several sql statements each which handles just one item. So you code would look like this psuedo code:
switch (item)
{
case 'title':
selectCommand = "SELECT * FROM Movies WHERE Title LIKE #0";
break;
case 'releaseDate':
selectCommand = "SELECT * FROM Movies WHERE ReleaseDate = #0";
break;
...
}
That leads to one of the reasons that a parameterized field name doesn't make sense. Because 'Title' is string, it can use the LIKE command, but a date couldn't.
This query works; that is, it returns the expected results:
var r = sql.Query<T>("select * from TableName where Name = '" + name + "'");
but, if one of the 'names' values contains an apostrophy (which is true), then an exception is thrown '{"Incorrect syntax near 'Resources'.\r\nUnclosed quotation mark after the character string ''."}' - in an attempt to fix that problem, my query no longer returns any results; but it should.
I've attempted to change the code in a few ways, but no results are returned with either of the following changes:
var r = sql.Query<T>("select * from TableName where Name = '#name'", new { name });
or
var args = new DynamicParameters(name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.AddDynamicParams(new { name });
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.Add("#name", name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
This is probably something trivial that I have simply just not yet grasped the concept for ... but I'm at the point of having spent too much time trying to figure it out - hence the question.
Using a parameter is the right way to go. You absolutely don't want to put the value into the query itself as you did in your first snippet. However, you've put the #name in quotes, which means it's being treated as a string literal... it's looking for a name value of exactly #name rather than the value of the #name parameter. You want:
var r = sql.Query<T>("select * from TableName where Name = #name", new { name });
(That's probably the simplest way of passing the parameters, though the other approaches should work too.)
Now I haven't actually used Dapper myself, but that's what I'd expect...
I need to use the value of a variable as one of my fields in my SQL insert query that I am generating.
In PHP I do this:
if($currency = "USD")
{
$columntoinsert = "USD_Currency";
}
else
{
$columntoinsert = "";
}
Then in my SQL statement I would execute the query like so:
$sqlinsertpayment = sqlsrv_query($connect,"INSERT INTO Payments_TBL(column1,column2,$columntoinsert,columnn)VALUES(????)",$params) or die("An error occurred trying to execute the statement");
I want to do the same in C#. I have the following code to set the column I want to use
var amountfield = "";
if (save_currency != "USD")
{
amountcolumn = "Amount";
}
else
{
amountcolumn = "USD_Amount";
}
But I can't execute my sql query in since trying to use amountcolumn in the query string generates an Invalid column name error which is expected. How can I use the variable in this manner:
var sql = new sqlCommand("INSERT INTO Payments_TBL(column1,column2,myc#variablevaluehere,column3,....)Values(#value,#value,#value)",new sqlConnection(conn));
Open to other alternatives. All help will be appreciated.
Because the value for amountfield comes from your code logic, you can safely do this:
var sql = new sqlCommand(String.Format("INSERT INTO Payments_TBL(column1,column2,{0},column3,....)Values(#value,#value,#value)", amountcolumn),new sqlConnection(conn));
But never do things like this with strings coming from your users because that would make you open for SQL injection.
Its
var amountfield = save_currency != "USD" ? "Amount" : "USD_Amount";
var sql = new sqlCommand("INSERT INTO Payments_TBL(column1,column2,"
+ amountcolumn +
",column3,....)Values(#value,#value,#value)",new sqlConnection(conn));
OR
var sql = new sqlCommand(
String.Format("INSERT INTO Payments_TBL(column1,column2,
{0},column3,....)Values(#value,#value,#value)", amountfield),
new sqlConnection(conn));