I'm working on a WPF application and using SQLite database. I can do every CRUD operation with Entity Framework, but in some specific cases I have to use raw SQL queries, and sometimes it's not returning what I need.
Here is a sample code:
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.ExecuteSqlCommand(sql);
}
I simplified the example. Here the result, what I got is -1. I copied the sql string value (after it's built) and executed in SQLiteStuido on the same database and it returned the correct value.
The DatabaseFilePath is correct. The connection is set correctly. I'm checking the same databases (in code and in SQLiteStudio). Any other idea?
Try this:
var result = db.Database.SqlQuery<int>(sql).First();
You have to call SqlQuery method and not ExecuteSqlCommand method. Since SqlQuery returns an IEnumerable you have to call Single. This is a the way to retreive scalar values from a query.
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.SqlQuery<int>(sql).Single();
}
Related
I have been using Dapper as my ORM for my .NET Core Web Api.
When using Dapper to query a stored procedure from my database with one parameter, it works exactly as expected. When I add more than one parameter, it does not return anything back to my datamodel like it should.
I suspect this has to do either with my syntax or the way I am structuring the query. The stored procedure I am using below works as expected when executed inside a SSMS query window.
Here is my method containing the Dapper Query in my DAL:
public List<Players> C_GetAllActivePlayersInSport(int orgID, int sportID)
{
using (IDbConnection db = new SqlConnection(_connectionString))
{
var returnedData = db.Query<Players>("dbo.spPlayers_GetAllActivePlayers_by_Sport #orgID, #sportID", new { orgID = orgID, sportID = sportID }).ToList();
return returnedData;
}
}
The values passed in make it to the method and query above, but after the query executes, it returns a list with a count of 0.
Any help would be greatly appreciated!
Try:
var returnedData = db.Query<Players>(
"dbo.spPlayers_GetAllActivePlayers_by_Sport",
new { orgID, sportID }, commandType: CommandType.StoredProcedure).ToList();
(note: .AsList() would be marginally preferable)
SQL Server provides output for inserted and updated record with the 'inserted' keyword.
I have a table representing a processing queue. I use the following query to lock a record and get the ID of the locked record:
UPDATE TOP (1) GlobalTrans
SET LockDateTime = GETUTCDATE()
OUTPUT inserted.ID
WHERE LockDateTime IS NULL
This will output a column named ID with all the updated record IDs (a single ID in my case). How can I translate this into EF in C# to execute the update and get the ID back?
Entity Framework has no way of doing that.
You could do it the ORM way, by selecting all the records, setting their LockDateTime and writing them back. That probably is not safe for what you want to do because by default it's not one single transaction.
You can span your own transactions and use RepeatableRead as isolation level. That should work. Depending on what your database does in the background, it might be overkill though.
You could write the SQL by hand. That defeats the purpose of entity framework, but it should be just as safe as it was before as far as the locking mechanism is concerned.
You could also put it into a stored procedure and call that. It's a little bit better than the above version because at least somebody will compile it and check that the table and column names are correct.
Simple Example #1 to get a data table:
I did this directly against the connection:
Changed the command.ExecuteNonQuery() to command.ExecuteReader()
var connection = DbContext().Database.Connection as SqlConnection;
using (var command = connection.CreateCommand())
{
command.CommandText = sql;
command.CommandTimeout = 120;
command.Parameters.Add(param);
using (var reader = command.ExecuteReader())
{
var resultTable = new DataTable();
resultTable.Load(reader);
return resultTable;
}
}
FYI, If you don't have an OUTPUT clause in your SQL, it will return an empty data table.
Example #2 to return entities:
This is a bit more complicated but does work.
using a SQL statement with a OUTPUT inserted.*
var className = typeof(T).Name;
var container = ObjContext().MetadataWorkspace.GetEntityContainer(UnitOfWork.ObjContext().DefaultContainerName, DataSpace.CSpace);
var setName = (from meta in container.BaseEntitySets where meta.ElementType.Name == className select meta.Name).First();
var results = ObjContext().ExecuteStoreQuery<T>(sql, setName, trackingEnabled ? MergeOption.AppendOnly : MergeOption.NoTracking).ToList();
T being the entity being worked on
How can I use dynamic queries in C# ? From what I've searched its similiar to when we use SqlCommand with parameters to prevent sql injection(example below).
using (SQLiteConnection DB_CONNECTION = new SQLiteConnection(connectionString))
{
DB_CONNECTION.Open();
string sqlquery = "UPDATE table SET Name =#Name, IsComplete=#IsComplete WHERE Key =#Key;";
int rows = 0;
using (SQLiteCommand command = new SQLiteCommand(sqlquery, DB_CONNECTION))
{
SQLiteParameter[] tableA = { new SQLiteParameter("#Key", todo.Key), new SQLiteParameter("#Name", table.Name), new SQLiteParameter("#IsComplete", table.IsComplete) };
command.Parameters.AddRange(tableA);
rows = command.ExecuteNonQuery();
}
DB_CONNECTION.Close();
return (rows);
}
I'm new to c# and i wondering how can I make this work, thanks in advance.
Basically just build up the string sqlQuery based on a set of conditions and ensure that the appropriate parameters have been set. For example, here is some psuedo-C# (not tested for bugs):
//Set to true, so our queries will always include the check for SomeOtherField.
//In reality, use some check in the C# code that you would want to compose your query.
//Here we set some value we want to compare to.
string someValueToCheck = "Some value to compare";
using (SQLiteConnection DB_CONNECTION = new SQLiteConnection(connectionString))
{
DB_CONNECTION.Open();
string sqlquery = "UPDATE MyTable SET Name =#Name, IsComplete=#IsComplete WHERE Key =#Key";
//Replace this with some real condition that you want to use.
if (!string.IsNullOrWhiteSpace(someValueToCheck))
{
sqlquery += " AND SomeOtherField = #OtherFieldValue"
}
int rows = 0;
using (SQLiteCommand command = new SQLiteCommand(sqlquery, DB_CONNECTION))
{
//Use a list here since we can't add to an array - arrays are immutable.
List<SQLiteParameter> tableAList = {
new SQLiteParameter("#Key", todo.Key),
new SQLiteParameter("#Name", table.Name),
new SQLiteParameter("#IsComplete", table.IsComplete) };
if (!string.IsNullOrWhiteSpace(someValueToCheck)) {
//Replace 'someValueToCheck' with a value for the C# that you want to use as a parameter.
tableAList.Add(new SQLiteParameter("#OtherFieldValue", someValueToCheck));
}
//We convert the list back to an array as it is the expected parameter type.
command.Parameters.AddRange(tableAList.ToArray());
rows = command.ExecuteNonQuery();
}
DB_CONNECTION.Close();
return (rows);
}
In this day and age it would probably be worth looking into LINQ to Entities, as this will help you to compose queries dynamically in your code - for example https://stackoverflow.com/a/5541505/201648.
To setup for an existing database - also known as "Database First" - see the following tutorial:
https://msdn.microsoft.com/en-au/data/jj206878.aspx
You can skip step 1 since you already have a database, or do the whole tutorial first as practice.
Here is some psuedo-C# LINQ code to perform roughly the same update as the previous example:
//The context you have setup for the ERP database.
using (var db = new ERPContext())
{
//db is an Entity Framework database context - see
//https://msdn.microsoft.com/en-au/data/jj206878.aspx
var query = db.MyTable
.Where(c => c.Key == todo.Key);
if (!string.IsNullOrWhiteSpace(someValueToCheck))
{
//This where is used in conjunction to the previous WHERE,
//so it's more or less a WHERE condition1 AND condition2 clause.
query = query.Where(c => c.SomeOtherField == someValueToCheck);
}
//Get the single thing we want to update.
var thingToUpdate = query.First();
//Update the values.
thingToUpdate.Name = table.Name;
thingToUpdate.IsComplete = table.IsComplete;
//We can save the context to apply these results.
db.SaveChanges();
}
There is some setup involved with Entity Framework, but in my experience the syntax is easier to follow and your productivity will increase. Hopefully this gets you on the right track.
LINQ to Entites can also map SQL stored procedures if someone one your team objects to using it for performance reasons:
https://msdn.microsoft.com/en-us/data/gg699321.aspx
OR if you absolutely ust compose custom queries in the C# code this is also permitted in Entity Framework:
https://msdn.microsoft.com/en-us/library/bb738521(v=vs.100).aspx
I am trying to perform a dapper query like this:
string query = "select * from MyTable where someNumber in #Nums;";
...
connection.Query<ReturnObj>(query, new {Nums = nums})
And I am getting a MySql syntax error if nums is empty. It looks like Dapper changes the query to look like this: WHERE 1 = 0) so I am guessing it the left ( is missing, which is causing the syntax error. Yes, I realize I could just check if the collection is empty before executing the query, but I would rather not if I don't have to.
This is a bug in Dapper where it creates a SQL statement that is invalid for MySQL Server 5.6 (and earlier).
Workarounds:
Upgrade to MySQL Server 5.7 (which accepts the SQL Dapper generates and returns the expected results)
As you said, check if the collection is empty before executing the query
A variant of checking if the collection is empty (that can be useful if you have a complex query, NOT IN, etc.):
var numsSql = nums.Any() ? "#Nums" : "(select null)";
var query = $"select * from MyTable where someNumber in {numsSql};";
conn.Query(query, new { Nums });
Is this query safe against sql injection in combination with Dapper?
If not, what would be the correct way to write it under MySql?
Or is there a better version without using concat?
string sql = "SELECT * from user_profile WHERE FirstName LIKE CONCAT("%",#name,"%");"
var result = connection.query<profile>(sql, new {name});
There isn't a problem with that code, but another approach is to perform the the concat at the caller, i.e.
const string sql = "SELECT * from user_profile WHERE FirstName LIKE #name;";
var result = connection.Query<Profile>(sql, new {name = "%"+name+"%"});
This is safe because you are not building SQL dynamically at all. Name is just a normal parameter. Actually, it has nothing to do with Dapper.
Using a string concat here is the right choice. Alternatively you could use the SUBSTRING_INDEX function.