I am currently converting functions in the DAL of an old app to a new app using Entity Framework/LINQ in its DAL.
There are some instances where I would like to directly pass a string of sql to the database. Is this possible when using LINQ? Here is what I tried atfer research but ExecuteQuery is not available.
using (var context = new DbContext())
{
var sql = #"SELECT DISTINCT * FROM Customer where CustomerId = {0}";
sql = string.Format(sql, customerId);
var query = DbContext.ExecuteQuery<Customer>(sql);
return query.ToList();
}
Doing this seems pretty straight forward but ExecuteQuery is not available to me.
Here is my next attempt at it which seems much better: (please tell me if there is a better way)
StringBuilder sql = new StringBuilder();
sql.AppendLine("SELECT * FROM CUSTOMERS ");
sql.AppendLine("WHERE #CustomerId = null OR CustomerId = #CustomerId ");
sql.AppendLine("AND #CustomerName = null OR CustomerName = #CustomerName ");
var customerList = context.Customers.SqlQuery(sql.ToString(),
new SqlParameter("#CustomerId", customerId),
new SqlParameter("#CustomerName", customerName)).ToList();
Although for your current condition you can use LINQ.
var customer = context.Customers.Where(c => c.CustomerId = id).Distinct();
This is how you do it Entity Framework Raw SQL Queries
Sending raw commands to the database
using (var context = new DbContext())
{
context.Database.SqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
}
Writing SQL queries for entities
using (var context = new DbContext())
{
var customers = context.Customers.SqlQuery("SELECT * FROM dbo.Customers").ToList();
}
Loading entities from stored procedures
using (var context = new DbContext())
{
var customers = context.Blogs.SqlQuery("dbo.GE_Customers").ToList();
}
Writing SQL queries for non-entity types
using (var context = new DbContext())
{
var customerNames = context.Database.SqlQuery<string>(
"SELECT Name FROM dbo.Customers").ToList();
}
Update to answer
You do not need to pass SqlParameter, simply pass the default objects
I think the code below should work fine.
var customerList = context.Customers.SqlQuery(sql.ToString(), customerId, customerName).ToList();
if your real query is
sql.AppendLine("SELECT * FROM CUSTOMERS ");
sql.AppendLine("WHERE #CustomerId = null OR CustomerId = #CustomerId ");
sql.AppendLine("AND #CustomerName = null OR CustomerName = #CustomerName ");
I would suggest you to do it this way
var customers = context.Costomers; // this does not populates the result yet
if (!String.IsNullOrEmpty(customerId))
{
customers = customers.Where(c => c.CustomerId = customerId); // this does not populates the result yet
}
if (!String.IsNullOrEmpty(customerName))
{
customers = customers.Where(c => c.CustomerName = customerName); // this does not populates the result yet
}
// finally execute the query
var custList = customers.ToList();
If there is not limitation or requirement don't use this kind of queries they are vulnerable to sql inject attacks.
You can do nearly every sentence with Entity Framework using linq, like the one you wrote
DbContext.Customer.Where(c => c.CustomerId = id).Distinct();
It's more readable, and more secure.
You can use SqlQuery, but it would be better to pass the parameter instead of using Format:
var sql = #"SELECT DISTINCT * FROM Customer where CustomerId = {0}";
var query = DbContext.SqlSuery<Customer>(sql, customerId);
That way, the parameters are SQL encoded so as not to allow for SQL Injection.
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:
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
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();
Piece of code:
using (var dc = new Database())
{
var results = from a in dc.Article
where a.IdNeswpaper == 12
select new
{
a.Id,
};
dataGrid1.ItemsSource = results;
}
This query is visible in Sql Profiler, but this:
int idNews = 12;
using (var dc = new Database())
{
var results = from a in dc.Article
where a.IdNeswpaper == idNews
select new
{
a.Id,
};
dataGrid1.ItemsSource = results;
}
isn't visible, both are done correctly and the data is displayed.
Why the second query isn't visible in Sql Profiler?
Any ideas?
I hate to tell you, but you must look better. The query IS there. Maybe you filter it out?
Point is - there is no way to pull data out of a database via a connection without submitting SQL text. None. So, to get the data the query has to be there.
I have a function:
public static List<T> EntityCache<T>(this System.Linq.IQueryable<T> q, ObjectContext dc, string CacheId)
{
try
{
List<T> objCache = (List<T>)System.Web.HttpRuntime.Cache.Get(CacheId);
string connStr = (dc.Connection as System.Data.EntityClient.EntityConnection).StoreConnection.ConnectionString;
if (objCache == null)
{
ObjectQuery<T> productQuery = q as ObjectQuery<T>;
string sqlCmd = productQuery.ToTraceString();
using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connStr))
{
conn.Open();
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(sqlCmd, conn))
{
string NotificationTable = q.ElementType.Name;
System.Web.Caching.SqlCacheDependency sqldep = new System.Web.Caching.SqlCacheDependency(cmd);
cmd.ExecuteNonQuery();
objCache = q.ToList();
System.Web.HttpRuntime.Cache.Insert(CacheId, objCache, sqldep);
}
}
}
return objCache;
}
catch (Exception ex)
{
throw ex;
}
}
q can be a table, view or a procedure.
What i want is to find the underlying tables associated with a view or a procedure.
like if q is a join of tow tables i want to get the name of both the tables and finally
execute like:
If there are tw0 tables say A and B
Then i need to make Aggregate Dependency like:
string sqlCmd1 = string.Empty;
string sqlCmd2 = string.Empty;
using (testEntities ctx1 = new testEntities())
{
sqlCmd1 = ((System.Data.Objects.ObjectQuery)(from p in ctx1.A select p)).ToTraceString();
sqlCmd2 = ((System.Data.Objects.ObjectQuery)(from p in ctx1.B select p)).ToTraceString();
}
System.Data.SqlClient.SqlCommand cmd1 = new System.Data.SqlClient.SqlCommand(sqlCmd1, conn);
System.Data.SqlClient.SqlCommand cmd2 = new System.Data.SqlClient.SqlCommand(sqlCmd2, conn);
System.Web.Caching.SqlCacheDependency
dep1 = new System.Web.Caching.SqlCacheDependency(cmd1),
dep2 = new System.Web.Caching.SqlCacheDependency(cmd2);
System.Web.Caching.AggregateCacheDependency aggDep = new System.Web.Caching.AggregateCacheDependency();
aggDep.Add(dep1, dep2);
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
then the query i want to execute is
select * from A;
select * from B;
This i am using for SqlCacheDependency using Linq to Entity.
It works well for views when i hardcode the underlying tables but now i want the code automatically check for the underlying tables
and execute nonquery like
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
and make aggregate dependencies.
Any help is appreciated.
Thanks.
You must use database level tools to find which database objects your views or stored procedures depends on (but it also means you must know their full names in the database). For example SQL server offers sp_depends system stored procedure to track dependencies. This can be quite complicated because dependencies can have multiple levels (procedure can be dependent on view, view can be dependent on another view, etc.).
Be aware that advanced EF mapping also allows writing SQL directly to EDMX and in such case you will have to parse ToTraceString to find database objects.
I have found a solution for the problem i have posted.
There is a query that is valid for sql server 2005 onward.
We need to pass the name of the object and it will return us the name of the tables on which it depends
Example:
The name of the View is say AllProducts_Active_Inactive
;WITH CTE AS (SELECT o.name
, o.type_desc
, p.name
, p.type_desc as B
, p.object_id
FROM sys.sql_dependencies d
INNER JOIN sys.objects o
ON d.object_id = o.object_id
INNER JOIN sys.objects p
ON d.referenced_major_id = p.object_id
where o.name = 'AllProducts_Active_Inactive'
UNION ALL
SELECT o.name
, o.type_desc
, p.name
, p.type_desc as B
, p.[object_id]
FROM sys.sql_dependencies d
INNER JOIN CTE o
ON d.object_id = o.object_id
INNER JOIN sys.objects p
ON d.referenced_major_id = p.object_id
where o.name = 'AllProducts_Active_Inactive'
)
SELECT DISTINCT * FROM [CTE]
where B = 'USER_TABLE'
This post is the modified answer of the question i have posted on the website:
http://ask.sqlservercentral.com/questions/81318/find-the-underlying-tables-assocaited-with-a-view-or-a-stored-procedure-in-sql-server
What i changed is added the line where B = 'USER_TABLE'
Which means only those dependencies are returned who are tables.
And the seconds thing is added a WHERE clause so that a specific object is found.
Thanks