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
Related
I have a list of items with different ids which represent a SQL table's PK values.
Is there any way to build an efficient and safe statement?
Since now I've always prepared a string representing the statement and build it as I traversed the list via a foreach loop.
Here's an example of what I'm doing:
string update = "UPDATE table SET column = 0 WHERE";
foreach (Line l in list)
{
update += " id = " + l.Id + " OR";
}
// To remove last OR
update.Remove(update.Length - 3);
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
Which feels very unsafe and looks very ugly.
Is there a better way for this?
So yeah, in SQL you've got the 'IN' keyword which allows you to specify a set of values.
This should accomplish what you would like (syntax might be iffy, but the idea is there)
var ids = string.Join(',', list.Select(x => x.Id))
string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
However, the way you're performing your SQL can be considered dangerous (you should be fine as this just looks like ids from a DB, who knows, better to be safe than sorry). Here you're passing parameters straight into your query string, which is a potential risk to SQL injection which is very dangerous. There are ways around this, and using the inbuilt .NET 'SqlCommand' object
https://www.w3schools.com/sql/sql_injection.asp
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0
It would be more efficient to use IN operator:
string update = "UPDATE table SET column = 0 WHERE id IN (";
foreach (Line l in list)
{
update += l.Id + ",";
}
// To remove last comma
update.Remove(update.Length - 1);
// To insert closing bracket
update += ")";
If using .NET Core Framework, see the following library which creates parameters for a WHERE IN. The library is a port from VB.NET which I wrote in Framework 4.7 years ago. Clone the repository, get SqlCoreUtilityLibrary project for creating statements.
Setup.
public void UpdateExample()
{
var identifiers = new List<int>() { 1, 3,20, 2, 45 };
var (actual, exposed) = DataOperations.UpdateExample(
"UPDATE table SET column = 0 WHERE id IN", identifiers);
Console.WriteLine(actual);
Console.WriteLine(exposed);
}
Just enough code to create the parameterizing SQL statement. Note ActualCommandText method is included for development, not for production as it reveals actual values for parameters.
public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{
using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
using var cmd = new SqlCommand() { Connection = cn };
cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);
cmd.AddParamsToCommand("p", identifiers);
return (cmd.CommandText, cmd.ActualCommandText());
}
For a real app all code would be done in the method above rather than returning the two strings.
Results
UPDATE table SET column = 0 WHERE id IN (#p0,#p1,#p2,#p3,#p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)
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();
}
I have a requirement where I need to read queries from Access DB in c# and check if the access db query has any keyword like "KEY" if it has keywords I need to enclose that in square brackets"[]".just like how it is done in SQL.
Could someone suggest me how to do that?
You can retrieve the query text like this:
string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\...\myDB.mdb";
using (var conn = new OleDbConnection(connString )) {
conn.Open();
string[] restrictions = new string[] { null, null, "myQuery" };
DataTable schema = conn.GetSchema("Views", restrictions);
if (schema.Rows.Count > 0) {
DataRow row = schema.Rows[0];
string queryText = (string)row["VIEW_DEFINITION"];
Console.WriteLine(queryText);
}
}
If you drop the restrictions argument with the query name, conn.GetSchema("Views") returns one row for each query. If you query conn.GetSchema("Procedures") other types of queries like insert, update and DDL statements that are not considered as queries are returned in row["PROCEDURE_DEFINITION"].
View (query) names are returned in row["TABLE_NAME"] and procedure names in row["PROCEDURE_NAME"].
And you can update the query like this:
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var cmd = new OleDbCommand("DROP PROCEDURE myQuery", conn);
cmd.ExecuteNonQuery();
cmd = new OleDbCommand("CREATE PROCEDURE myQuery AS SELECT * FROM myTable", conn);
cmd.ExecuteNonQuery();
}
Strangely enough the OleDb CREATE DDL (Data Definition Language) designates the queries as 'procedures' but the schema table returns a 'VIEW_DEFINITION' and the query name is returned in the column 'TABLE_NAME'. SELECT queries must be retrieved as "Views", other types of queries as "Procedures"; however, both types are created as PROCEDUREs.
While I was testing the answer that #Olivier Jacot-Descombes provided, I was not able to retreive all the queries text representation. Therefore I applied some other method where you open the existing Ms Access database instance and read the queries that are stored in it.
Here is the class I used:
public class MsAccess
{
private Microsoft.Office.Interop.Access._Application _oAccess;
public MsAccess(string path)
{
_oAccess = (Microsoft.Office.Interop.Access._Application)System.Runtime.InteropServices.Marshal.BindToMoniker(path);
}
public string ReturnSqlQueryText(string queryName)
{
string queryDef = null;
var qdefs = _oAccess.CurrentDb().QueryDefs;
foreach (QueryDef qdef in qdefs)
{
if(qdef.Name.Equals(queryName))
queryDef = qdef.SQL;
}
return queryDef;
}
}
Using this code might require you adding using Microsoft.Office.Interop.Access.Dao and Microsoft.Office.Interop.Access both (15.0.0.0) where you can find them under Extension on the reference menu
In a EF6 context, I want to filter multiples entities by dynamic fields/attributes. I'm new to EF and my perspective is very corrupted by T-SQL and stored procedures and dynamic SQL queries.
For instance in a ERP environment, the user can filter by a code, and the system should return the:
Customer with CustomerID = code
Supplier with SupplierID = code
User with UserID = code
Orders with CustomerID/SupplierID = code
etc.
But can not only be a code, can multiples concepts to filter for: a name, a city, a date, ... and may all not apply to all the entities.
So since each entity has different attributes names to refer to that "code" concept, I've thought that the best solution is to use EntityCommand instead of LinQ.
And the code should look something like:
// Create a query that takes two parameters.
string eSqlCustomerQuery =
#"SELECT VALUE Contact FROM AdventureWorksEntities.Customer AS Customer";
string eSqlCustomerQuery =
#"SELECT VALUE Contact FROM AdventureWorksEntities.Customer AS Customer";
// Create a list of parameters
var param = new SortedList<string, EntityParameter>();
// for each clauses add a pamater and build the query command dynamically.
if(!code)
{
eSqlCustomerQuery += "WHERE Customer.CustomerID = #CODE";
eSqlSupplierQuery += "WHERE Supplier.SupplierID = #CODE";
//... more entities to
param["CODE"].ParameterName = "CODE";
param["CODE"].Value = code;
}
// more parameters here...
using (EntityConnection conn =
new EntityConnection("name=AdventureWorksEntities"))
{
conn.Open();
using (EntityCommand cmd = new EntityCommand(eSqlCustomerQuery, conn))
{
cmd.Parameters.Add(param["CODE"]);
cmd.Parameters.Add(param["DATE"]);
// more parameters here...
}
// the same for each query...
// ...
// run all the queries ...
// ...
// Etc.
conn.Close();
}
My questions are 3:
At the time I'm doing cmd = new EntityCommand(eSqlCustomerQuery, conn) can I use something like the System.Data.SqlClient.SqlCommandBuilder.DeriveParameters(cmd);?
Since this dynamic query it's so dynamic that it can be cached or have a reusable execution plan, how can it be improved?
Is it possible to do it with LinQ in a cleaner way?
Use LINQ like that:
//define base LINQ
Contracts = from R in AdventureWorks.Customer select R; //there is IQueryable, not actually materialized
//tune filters, no records will fetched
Result = Contracts;
if (code!=null) Result = Result.Where(_=>_.Code==code);
if (date!=null) Result = Result.Where(_=>_.Date==date);
//materialize records
Records = Result..Select(_=>_.Contract).ToArray();
I'm kinda new to LINQ, so sorry if my question is dumb.
I need to retrieve some values from a database and put them in textBoxes. Just that. Something like the code below, but using LINQ:
EDIT: actually, I want to retrieve more than one field. Like that:
SqlDataReader dr = new functionThatReturnsDataReader();
if (dr.HasRows) {
dr.Read();
txtId = dr["Id"].ToString();
txtName = dr["Name"].ToString();
}
I've found this solution online:
IDbCommand command = dc.GetCommand(query);
command.Connection = dc.Connection;
command.Connection.Open();
IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
However, it seems like I'm trowing away everything that LINQ stands for if I mix it with a standard executeReader. There's no reason in building a data context and query and them execute them like ADO...
How can I achieve the same without using IDbCommand and IDataReader?
I think you need to create a LINQ to SQL class from your database:
http://msdn.microsoft.com/en-us/library/bb384428.aspx
Then you can treat the database table like an object and query it with linq... without the specifics of the query you are trying to run, I cannot construct the linq query for you, but creating the DBML file is the first step.
One thing to think about when comparing a LINQ (or Entity Framework, or other ORM) solution to an ADO.NET solution is that the ORM's are--generally--strongly typed. This means that you need to apply object-oriented principles to them.
If you're dealing with the context, query, and results all in the same function, you can do this:
using(var context = new YourContext())
{
txtId.Text = (from t in context.YourTable
where t.Conditions
select t.Id).FirstOrDefault();
}
If they're not in the same function (and really, they shouldn't be), then something like this would work:
string FunctionThatReturnsId()
{
using(var context = new YourContext())
{
return (from t in context.YourTable
where t.Conditions
select t.Id).FirstOrDefault();
}
}
...
txtId.Text = FunctionThatReturnsId();
If you are using entity Framework then you dont need any query just write id in text box and hit search button it will show you all records in text box of that id.
EntityFramework_mvcEntities db = new EntityFramework_mvcEntities();
int i =Convert.ToInt32( txtsrch.Text);
Employee p = db.Employees.Find(i);
TextBox1.Text = p.Name;
TextBox2.Text = p.Email;
TextBox4.Text = p.Mobile;
db.SaveChanges();
}
var q = from c in context.GetTable<tbl_user>()
where c.user_ID == lbuserid.Text.ToString()
select new
{
c.Username,
c.firstname
};
foreach (var item in q)
{
lbusername.Text = item.Username;
lbfirstname.Text = item.firstname;
}