EF on IDBcommandInterceptor and DBDataReader - entity-framework

I am trying to Mock EF on IDBcommandInterceptor
For insert / update operations it is enough simple - I can return a DbDataReader made of a single field or an int
However, for select operations, if there are some "include", then the shape of the sql result is pretty ... awesome
How could I obtain from
ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> the fields and the names and the corresponding entities for the DbDataReader result?
Thank you,
Example : Trying to read Department(Id, Name) from Id with include on Employee(Id, Name .IDDepartment, DateModification, DateCreation, User)
The Command that obtains the DBDataReader for an include is below .
I want to know the fields names (like C1, ID1, Name1 and others) to be capable of Mocking.
SELECT
[Project2].[Id] AS [Id],
[Project2].[Name] AS [Name],
[Project2].[C1] AS [C1],
[Project2].[Id1] AS [Id1],
[Project2].[Name1] AS [Name1],
[Project2].[IDDepartment] AS [IDDepartment],
[Project2].[DateModification] AS [DateModification],
[Project2].[DateCreation] AS [DateCreation],
[Project2].[User] AS [User],
[Project2].[Archive] AS [Archive]
FROM ( SELECT
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
[Extent2].[IDDepartment] AS [IDDepartment],
[Extent2].[DateModification] AS [DateModification],
[Extent2].[DateCreation] AS [DateCreation],
[Extent2].[User] AS [User],
[Extent2].[Archive] AS [Archive],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM (SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[Department] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0 ) AS [Limit1]
LEFT OUTER JOIN [dbo].[Employee] AS [Extent2] ON [Limit1].[Id] = [Extent2].[IDDepartment]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC

You should be able to get the column names from the reader by doing something like this
var columns = Enumerable.Range(0, reader.FieldCount) .Select(reader.GetName).ToList();
That in the case you only need the names. If you get the whole schema for the result table, you could get it by doing this:
DataTable schemaTable = reader.GetSchemaTable();
And then iterate through the collection of rows in the datatable to get the properties of each column.
Does that help to answer your question?

Related

Left-Outer-Join to same table with Linq/EntityFramework

I need to create a left-outer-join to the same table:
SELECT Top(20) s.Id as ParentId, CASE WHEN cr.Id IS NOT NULL THEN 'False' ELSE 'True' END AS HasChangeRequest
FROM [Platos.PRM.Dev].[dbo].[Section] s
left outer join [Section] cr on cr.ParentSectionId = s.Id
For this I wrote the following Linq statement:
var query = from section in this.prmDbContext.Sections
join changeRequest in this.prmDbContext.Sections.Where(x => x.ParentSection != null && !x.IsInactive) on section.Id equals changeRequest.ParentSection.Id into changeRequestJoin
from changeRequest in changeRequestJoin.DefaultIfEmpty()
select new
{
Id = section.Id,
HasChangeRequest = changeRequest != null
};
var result = query.Take(20).ToList();
The linq statement works and deliver the correct result, but is very slow.
SQL statement: some millisconds - LINQ statement: 5 seconds
Could anyone tell me, why the performance with this linq statement is bad?
Thanks!
Update:
After adding a navigation property (ChangeRequests) I update the query to the following linq statement:
var query = from section in this.prmDbContext.Sections
select new
{
Id = section.Id,
HasChangeRequest = section.ChangeRequests.Any()
};
var result = query.Take(20).ToList();
Now the EntityFramework creates the following sql query:
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[Id] AS [Id],
[Limit1].[C2] AS [C2]
FROM ( SELECT TOP (20)
[Extent1].[Id] AS [Id],
1 AS [C1],
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Section] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[ParentSectionId]
)) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C2]
FROM [dbo].[Section] AS [Extent1]
) AS [Limit1]
Now the query is faster, but still slower than the left-outer-join from the native sql query. So is it possible, to optimize the linq statement?

sql recursion: find tree given middle node

I need to get a tree of related nodes given a certain node, but not necessary top node. I've got a solution using two CTEs, since I am struggling to squeeze it all into one CTE :). Might somebody have a sleek solution to avoid using two CTEs? Here is some code that I was playing with:
DECLARE #temp AS TABLE (ID INT, ParentID INT)
INSERT INTO #temp
SELECT 1 ID, NULL AS ParentID
UNION ALL
SELECT 2, 1
UNION ALL
SELECT 3, 2
UNION ALL
SELECT 4, 3
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, NULL
UNION ALL
SELECT 7, 6
UNION ALL
SELECT 8, 7
DECLARE #startNode INT = 4
;WITH TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM #temp
WHERE ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
;WITH Up(ID,ParentID)
AS (
SELECT t.id, t.ParentID
FROM #temp t
WHERE t.ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN Up c ON t.id = c.ParentID
)
--SELECT * FROM Up
,TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM Up
WHERE ParentID is null
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
thanks
Meh. This avoids using two CTEs, but the result is a brute force kludge that hardly qualifies as "sleek" as it won’t be efficient if your table is at all sizeable. It will:
Recursively build all possible hierarchies
As you build them, flag the target NodeId as you find it
Return only the targeted tree
I threw in column “TreeNumber” on the off-chance the TargetId appears in multiple hierarchies, or if you’d ever have multiple values to check in one pass. “Depth” was added to make the output a bit more legible.
A more complex solution like #John’s might do, and more and subtler tricks could be done with more detailed table sturctures.
DECLARE #startNode INT = 4
;WITH cteAllTrees (TreeNumber, Depth, ID, ParentID, ContainsTarget)
AS (
SELECT
row_number() over (order by ID) TreeNumber
,1
,ID
,ParentID
,case
when ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp
WHERE ParentId is null
UNION ALL
SELECT
tr.TreeNumber
,tr.Depth + 1
,t.id
,t.ParentID
,case
when tr.ContainsTarget = 1 then 1
when t.ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp t
INNER JOIN cteAllTrees tr
ON t.ParentID = tr.ID
)
SELECT
TreeNumber
,Depth
,ID
,ParentId
from cteAllTrees
where TreeNumber in (select TreeNumber from cteAllTrees where ContainsTarget = 1)
order by
TreeNumber
,Depth
,ID
Here is a technique where you can select the entire hierarchy, a specific node with all its children, and even a filtered list and how they roll.
Note: See the comments next to the DECLAREs
Declare #YourTable table (id int,pt int,name varchar(50))
Insert into #YourTable values
(1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8')
Declare #Top int = null --<< Sets top of Hier Try 2
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
Declare #Filter varchar(25) = '' --<< Empty for All or try 4,6
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500))
,ID
,pt
,Lvl=1
,name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(pt,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500))
,r.ID
,r.pt
,p.Lvl+1
,r.name
From #YourTable r
Join cteP p on r.pt = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Distinct
A.R1
,B.R2
,A.ID
,A.pt
,A.Lvl
,name = Replicate(#Nest,A.Lvl-1) + A.name
From cteR1 A
Join cteR2 B on A.ID=B.ID
Join (Select R1 From cteR1 where IIF(#Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',#Filter+','))>0) F on F.R1 between A.R1 and B.R2
Order By A.R1

T-SQL: Determine upline and downline from hierarchy without the Parent ID

I have this self-referencing table where-in I should get the upline and downline and hierarchy levels without the Parent ID provided.
Any ideas?
Have you tried using recursive CTE?
for example:
assume you have table tbl(EmpId, Name, MngrId) which has self-referencing relationship
create table tbl
(
EmpId int not null,
Name nvarchar(100),
MngrId int null)
insert into tbl(EmpId, Name, MngrId)
values (1,'Adel',Null),
(2,'Ali',1),
(3,'Shaban',1),
(4,'Mark',3),
(5,'John',3),
(6,'Tony',Null),
(7,'Peter',6)
You can create some view like that:
create view Employees
Begin
with cte
as
(
Select EmpId,Name, Null as MngrId, cast(null as nvarchar(100)) as MngrName, 1 as EmpLevel
from tbl
where MngrId is Null
Union All
Select t.EmpId, t.Name, c.EmpId as MngrId, c.Name as MngrName, c.EmpLevel + 1 as EmpLevel
from tbl t
inner join cte c
on t.MngrId = c.EmpId
)
Select *
from cte
order by EmpLevel, EmpId
End
You can now use EmpLevel to jump between different levels and MngrName to get information about parent node

getting distinct rows based on two column values

I am trying to get distinct rows from a temporary table and output them to an aspx page. I am trying to use the value of one column and get the last entry made into that column.
I have been trying to use inner join and max(). However i have been unsuccessful.
Here is the code i have been trying to do it with.
Declare #TempTable table (
viewIcon nvarchar(10),
tenderType nvarchar(20),
diaryIcon int,
customerName nvarchar(100),
projectName nvarchar(100),
diaryEntry nvarchar(max),
diaryDate nvarchar(20),
pid nvarchar(20)
)
insert into #TempTable(
viewIcon,
tenderType,
diaryIcon,
customerName,
projectName,
diaryEntry ,
diaryDate ,
pid
)
select p.viewicon,
p.[Tender Type],
1 diaryicon,
c.[Customer Name],
co.[Last Project],
d.Action,
co.[Diary Date],
p.PID
From Projects2 p Inner Join
(select distinct Pno, max(convert(date,[date of next call],103)) maxdate from ProjectDiary group by Pno
) td on p.PID = td.Pno
Inner Join contacts3 co on co.[Customer Number] = p.[Customer Number]
Inner Join Customers3 c on p.[Customer Number] = c.[Customer Number]
Inner Join ProjectDiary d on td.Pno = d.Pno
Where CONVERT(Date, co.[Diary Date], 103) BETWEEN GETDATE()-120 AND GETDATE()-60
DECLARE #contactsTable TABLE
(pid nvarchar(200),
diaryDate date)
insert into #contactsTable (t.pid, t.diarydate)
select distinct pid as pid, MAX(CONVERT(DATE, diaryDate, 103)) as diaryDate from # TempTable t group by pid
DECLARE #tempContacts TABLE
(pid nvarchar(200))
insert into #tempContacts(pid)
select pid from #contactsTable
DECLARE #tempDiaryDate TABLE (diaryDate date)
insert into #tempDiaryDate(diaryDate)
select distinct MAX(CONVERT(DATE, diaryDate, 103)) from #TempTable
select t.* from #TempTable t inner join (select distinct customerName, M AX(CONVERT(DATE, diaryDate, 103)) AS diaryDate from #TempTable group by customerName) tt on t t.customerName=t.customerName
where t.pid not in
(select Pno from ProjectDiary where convert(date,[Date Of Next Call],103) > GETDATE())
and t.viewIcon <> '098'
and t.viewIcon <> '163'
and t.viewIcon <> '119'
and t.pid in (select distinct pid from #tempContacts)
and CONVERT(DATE, t.diaryDate, 103) in (select distinct CONVERT(DATE, diaryDate, 103) f rom #tempDiaryDate)
order by CONVERT(DATE, tt.diaryDate, 103)
I am trying to get all the distinct customerName's using the max date to determine which record it uses.
Use a subquery. Without going through your entire sql statement, the general idea is:
Select [Stuff]
From table t
Where date = (Select Max(Date) from table
where customer = t.customer)

SQL Table Paging PERFORMANCE ...is EF4 + Linq SKIP +TAKE equal in performance than using TSQL params to request "paging" on a SQL Table?

If I want to retrieve records from 20 to 39, or from 40 to 59 from an MSSQL Table.
Since I'm using MVC and EF4, is performance the same if I just do a Linq query and Skip() and Take() procedure to request paging ....or is it better to do it on a GetList() Stored Procedure itself?
If you execute this linq query:
var data = context.Posts.OrderBy(p => p.Id).Skip(20).Take(20).ToList();
It will produce this SQL:
SELECT TOP (20)
[Extent1].[Id] AS [Id],
[Extent1].[Text] AS [Text]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Text] AS [Text],
row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[Posts] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 20
ORDER BY [Extent1].[Id] ASC
It is not such nice like custom SQL you would write in your stored procedure but in the meaning of performance it is the same. Pagining is done on database.