Entity Framework naming queries - c#

We're analyzing Azure "Query Performance Insight" to look for expensive queries, the problem is that, there is no way to relate SQL generated vs Entity Framework query.
Is there any extension method or anything else to do something like this:
SQL generated:
-- BlahMethod
SELECT Id
FROM Table1
Entity Framework cmd:
Context.Table1.Naming("BlahMethod").ToList()
Or even better:
Context.Table1.ToList() // intercept sql generated by EF and put through reflection the Method and Namespace "MyAssembly.Foo.MyMethodName"
SQL Generated:
-- MyAssembly.Foo.MyMethodName
SELECT Id
FROM Table1

Yes, look at this article Logging and Intercepting Database Operations.
It can be as simple as using Console.Write:
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
// Your code here...
}
Or you can use a log class:
using (var db = new MyDBContext())
{
db.Database.Log = s => Log.TraceVerbose("DB Context:{0}", s);
...

Related

C# retrieve data from SQL Server using SqlDataReader to Master Detail List

I have Master and Detail classes:
class Master
{
public int ID { get; set; }
public string Name { get; set; }
public List<Detail> Details { get; set; }
}
class Detail
{
public Description { get; set; }
public Amount { get; set; }
}
I use below approach and working fine now.
List<Master> result = new List<Master>();
// SQL Connection
string sqlCommand = "SELECT * FROM Master LEFT JOIN Detail on Master.ID = Detail.ID";
using (System.Data.SqlClient.SqlDataReader dr = db.DbDataReader as System.Data.SqlClient.SqlDataReader)
{
if (dr.HasRows)
{
Master LastMaster = null;
while (dr.Read())
{
if (LastMaster == null || Convert.ToInt(dr["ID"]) != LastMaster.ID)
{
Master h = new Master();
h.ID = Convert.ToInt(dr["ID"]);
h.Name = Convert.ToString(dr["Name"]);
result.Add(h);
LastMaster = h;
}
if (dr["Description"] == DBNull.Value)
continue;
if (h.Detail == null)
h.Detail = new List<Detail>();
Detail d = new Detail();
d.Description = dr["Description"] as string;
d.Amount = Convert.ToDouble(dr["Amount"]);
LastMaster.Detail.Add(d);
......
}
}
.....
}
Is there any better approach to fill list of list objects in C# ? I appreciate any suggestion. Thanks.
You can use Dapper (a micro ORM) for your scenario. Below is a sample code
const string createSql = #"
create table #Users (Id int, Name varchar(20))
create table #Posts (Id int, OwnerId int, Content varchar(20))
insert #Users values(99, 'Sam')
insert #Users values(2, 'I am')
insert #Posts values(1, 99, 'Sams Post1')
insert #Posts values(2, 99, 'Sams Post2')
insert #Posts values(3, null, 'no ones post')";
using(var connection = new SqlConnection("database connection string"))
{
connection.Execute(createSql);
try
{
const string sql =#"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post; }).ToList();
}
catch(Exception ex){}
}
Ibram commented about EF and Dapper and Abu gave an example for Dapper (but I'm not sure it demos generating a graph with a single master and multiple detail per master, as you have - dapper can do so if you want to explore it)
In EF we could do something like:
install EF core power tools - as you have a db already we will use it to generate classes from. This operation can just be done with the command line but EFCPT makes a lot of operations easier
right click your project, choose EF Core Power Tools .. Reverse Engineer
fill in a new connection string detail
choose the database objects you wish to turn into classes
set other options as appropriate (you can find out more about them later, maybe only use the pluralize one for now, if your db tables are like Orders, Customers, Companies and you want your classes called Order/Customer/Company (classes should not have plural names). Tick on "put connectionstring in code" for now- you can remove it to config file later
finish. Eventually you'll get some classes and a context that has a load of code in OnModelCreating that lays out a description of everything in the tables, the columns, keys, relationships..
Now you can run some query like:
var c = new YourContext();
var ms = c.Masters.Include(m => m.Details).ToList();
That's basically the equivalent of what you posted
You can get more trick by shaping a more involved linq query:
var q = c.Masters.Include(m => m.Details)
.Where(m => m.Name.StartsWith("Smith"))
.OrderBy(m => m.Name);
var ms = q.ToList();
It will be translated into something like
SELECT * FROM master join detail on ...
WHERE name LIKE 'Smith%'
ORDER BY m.name
You can see the generated query if you inspect the DebugView property of q
You could make changes:
ms[0].Details.Clear(); //causes delete of all details for this master
ms[1].Details.Add(new Detail { someprop = some value}); //causes insert of new details for this master
ms[2].Name = "Hello"; //causes update of this master name
c.SaveChanges(); //carries out the above, in sql, to affect the db
When you manipulate the returned objects and save, EF will delete/insert/update as appropriate to sync the db to what happened to the objects. It is important that you understand that EF tracks what happens to all the objects it creates, so that it can do this
When would you use EF and when would you use Dapper? Well, it doesn't have to be mutually exclusive; you can use them in the same project. Generally I'd say use EF (or some other ORM like it - nHibernate is another popular one, works on a similar concept of translating linq expressions to sql and tracking the data back into an object) for stuff where the sql is so simple that it's a productivity boost to not have to write it, track it, and write the changes back. What it is not intended for, is forming as hoc queries that don't map well to client side objects. For that you can use Dapper, or you could form client side objects and add them to EF's model and then run raw sql that populates them. Dapper is fast, because it doesn't do any of that tracking changes, mapping or wiring up complex object graphs; you do all that manually. Dapper makes a convenient abstraction over raw sql and creates classes, but EF goes much further; it comes at a cost - EF is highly convenient but much more heavy weight.

convert a SQL update statement to a LINQ to Entities

Without writing an entire foreach loop is there a way to do a Update/Set in LINQ to Entities?
Using EF 6.x
Simple update query:
UPDATE stop_detail
SET cap_unique_id = b.Delivery_Location_Id
FROM order_detail b
WHERE Stop_Detail.CAP_Unique_Id IS NULL AND ((b.customer_id = 20 OR b.customer_id = 291) AND b.id = stop_detail.order_detail_id AND stop_type = 1)
all the context name are the same.
I normally end up writing about 30 lines of C# code to do this and I know there has to be a better way!
Whether you can and whether you should are two different things.
Here's how you can.
Example from EF6 Raw SQL Queries
using (var context = new BloggingContext())
{
context.Database.ExecuteSqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
}
Hint: you probably shouldn't

Querying Data in a System-Versioned Temporal Table in Entity Framework Core

We are implementing a solution to query a temporal table.
When enabling a temporal table on SQL server for any table, SQL will automatically add a second table with extra “_History” at the end of the table to track history. For example, if we have a “student” table, SQL server will add “student_History” table.
To query the student history, all that we need is querying student table and add FOR SYSTEM_TIME AS OF '2015-09-01 T10:00:00.7230011'; at the end of the statement.
So instead of write:
Select * from student
We will write:
Select * from student FOR SYSTEM_TIME AS OF '2015-09-01 T10:00:00.7230011'
Is there any way to automatically append this statement at the end of the query?
It is like intercepting the query and applying query filter like a soft table, but now it is not filtered, it is just statement at the end of the statement.
it could be done by an extension method, I found piece of code that may help you :
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Linq;
namespace core
{
public static class Extensions
{
public static void AddTemporalTableSupport(this MigrationBuilder builder, string tableName, string historyTableSchema)
{
builder.Sql($#"ALTER TABLE {tableName} ADD
SysStartTime datetime2(0) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL,
SysEndTime datetime2(0) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL,
PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime);");
builder.Sql($#"ALTER TABLE {tableName} SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = {historyTableSchema}.{tableName} ));");
}
public static DbContext GetDbContext<T>(this DbSet<T> dbSet) where T : class
{
var infrastructure = dbSet as IInfrastructure<IServiceProvider>;
return (infrastructure.Instance.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext).Context;
}
public static string GetTableName<T>(this DbSet<T> dbSet) where T : class
{
var entityType = dbSet.GetDbContext().Model.GetEntityTypes().FirstOrDefault(t => t.ClrType == typeof(T))
?? throw new ApplicationException($"Entity type {typeof(T).Name} not found in current database context!");
var tableNameAnnotation = entityType.GetAnnotation("Relational:TableName");
return tableNameAnnotation.Value.ToString();
}
public static IQueryable<T> ForSysTime<T>(this DbSet<T> dbSet, DateTime time) where T : class
{
return dbSet.FromSql($"SELECT * FROM dbo.[{dbSet.GetTableName()}] FOR SYSTEM_TIME AS OF {{0}}", time.ToUniversalTime());
}
}
}
Usage :
var date = DateTime.Parse("2018-08-28 16:30:00");
var students = ctx.student.ForSysTime(date);
this extension method was written by Mirek , you can find the complete article here.
The latest version of Entity Framework Core (6) supports temporal tables.
As mentioned in here Microsoft devBlogs, EF Core 6.0 supports:
The creation of temporal tables using EF Core migrations
Transformation of existing tables into temporal tables, again using migrations
Restoring data from some point in the past
Querying historical data
Querying historical data can be seen here
you may try this nuget pacakge https://www.nuget.org/packages/EntityFrameworkCore.SqlServer.TemporalTable/
It support:
create temporal query from Linq
migrations for temporal table.(work with Add-Migration, Script-Migration)

Can Entity Framework Core run non-query calls?

Unfortunately my EF application has to call stored procedures I am unable to change. While this is not ideal I can normally get around it. However, I have a stored proc that does return anything. How does EF core deal with this? I know in previous versions you could run ExecuteNonQuery but I haven't been able to find anything similar in EF Core.
I normally run my queries through a helper as such, where T is a class that maps to a return type EF can serialize to:
context.Set<T>()
.AsNoTracking()
.FromSql(query, args)
.ToListAsync();
However it looks like Set always requires a type as does .Query. Nothing else I've seen off of context would allow you to make a non-queryable call. Am I missing something?
I am using Microsoft.EntityFrameworkCore: 1.2.0
You can use the DbContext.DatabaseExecuteSqlCommand method
using(var context = new SampleContext())
{
var commandText = "INSERT Categories (CategoryName) VALUES (#CategoryName)";
var name = new SqlParameter("#CategoryName", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
}
Or you can revert to ADO.NET calls off the Context:
using (var context = new SampleContext())
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "DELETE From Table1";
context.Database.OpenConnection();
command.ExecuteNonQuery();
}

EF: Avoiding multiple update statements

Code like this:
var compIds = from p in packinglist.List
select p.ComponentId;
var components = from c in context.Components
where compIds.Contains(c.Id)
select c;
foreach (var item in components)
{
item.CurrentSiteId = packinglist.DestinationId;
}
context.SaveChanges();
Ends up issuing lots of SQL Statements like
update [dbo].[Components] set [CurrentSiteId] = #0 where ([Id] = #1)
Is there a way to instruct EF (Code First) to issue the following statement:
update [dbo].[Components] set [CurrentSiteId] = #0 where ([Id] in (....))
Or should I look into using the one of the SQLQuery methods available, or a seperate tool like Dapper or massive or ...?
There is not currently a way to perform bulk updates in EF 4 out of the box. There are some very long, complicated work arounds that end up generating SQL though. I suggest using a stored procedure or T-SQL. Here's a quick T-SQL snippet that I've used in the past:
using (var context = new YourEntities())
{
context.ExecuteStoreCommand(
#"UPDATE Components SET CurrentSiteId = 1 WHERE ID IN(1,2,3,4)");
}
The simplest answer for this is just to write that query and use DbContext.SQLQuery() to run it. As mentioned, there's no way to do this in EF itself.

Categories