Select single dummy row in Entity Framework - c#

I'm trying to do something like this in entity framework.
SELECT (SELECT COUNT(*) FROM Log Where LogTypeId =1) as Log1,
(SELECT COUNT(*) FROM Log Where LogTypeId =2) as Log2
Note the lack of from. In essence, I just want to select a single row that contains aggregation of the Log table. I tried something like this.
(from x in ctx.Logs
select new {
Log1 = ctx.Logs.Count((x) => x.LogTypeId == 1),
Log2 = ctx.Logs.Count((x) => x.LogTypeId == 2)
}).First();
However, this returns a null if ctx.Logs doesn't contain any row (new system, or recently archived). While it's not that difficult check for a null return and simply create a blank entry, I want to know if there's a way to handle it so I don't have to do "If null create a blank aggregate with all field set to 0" everywhere I want to create this query (since I will have several similar style of select in multiple places).
I also want to avoid having to do multiple select, to prevent multiple round trips to the database.

Okay, feel a bit silly and figured out that I can use DefaultIfEmpty.
(from x in ctx.Logs.DefaultIfEmpty()
select new {
Log1 = ctx.Logs.Count((x) => x.LogTypeId == 1),
Log2 = ctx.Logs.Count((x) => x.LogTypeId == 2)
}).First();

To avoid null value you need to use one on the null handling function:
!.nvl
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions105.htm
2.nvl2
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions106.htm
3.coalesce
https://docs.oracle.com/cd/B28359_01/server.111/b28286/functions023.htm#SQLRF00617
Usage:
nvl(yourcolumn,0)
try like this in your code or google for these functions i hope you get some help from my side.

Related

Group data and retrieve every line of the grouping with Entity Framework

I was thinking that maybe, once the grouped data are retrieved in the C# part, I would be able loop through the list of items that were grouped.
var res = db.Commandes.Where(t => t.idMatiere == mod.idMatiereChoisie).GroupBy(t => t.UA_idCa);
foreach(var group in res)
{
foreach(var groupedLines in group)
{
// Always a single line, this loop is useless
}
}
It seems the logic applied here is more like SQL than C#: the grouping result in a single line and you won't see all the grouped items.
It's not a problem that I can't overcome
Tactic I will use: instead of grouping, I'll just query all the lines, and then, while looping, I will verify if UA_idCa is different form the previous data and that will means the next "group" has been reached.
But I wonder... How does someone normally do this cleanly, if it's possible?
Do you have to query again to retrieve a group's content?
Or is the "Tactic I will use" closer to what's best?
This problem is a matter of the combination of SQL server AND Entity Framework.
Seems like one of the value in the grouped part (a value that is different for all the line inside the group) must be marked as not null.
Because when looking for what could be a key, entity doesn't give a damn about nullable values : they could be unique, they could be never null, EF won't even check that.
Once it is marked as NOT NULL in the sql part, EF suddenly understand that there could multiple different unique values in the grouped part...
So basically This :
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,sc.id, 0 as idSousCahier /* THIS IS WHAT I COULD NOT COMPLETELY RETRIEVE
because it could be null ? */
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO
As been changed to this:
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,isnull(sc.id, 0) as idSousCahier /* WOW, NOW EF UNDERSTAND
THERE COULD BE MULTIPLE DIFFERENTS VALUES ONCE DATA ARE GROUPED*/
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO

Create complex query to execute queries stored in column?

I am trying to get some information from a table, when a (one or more than one) condition(s) is(are) met but this conditions are stored in other table.
Here is a screenshot of the 3 tables:
So I need a query to do this:
Given a Status (IDStatus) and an Action (IDAction)
If there is an IDCondition (it can be NULL, one, or more than one)
A. If there is one, use the IDocField as a column from a table called IDoc, using ConditionOperator as the condition (read this as =, <, >) and the value on ConditionValue.
B. If there are more than one, the same as before, but use each condition with AND.
C. If it is NULL, directly go to point 3
Get Subject, SendTo, CC, CCO, and FileTemplate that matched all of the above explained.
I want to try this because it would reduce my C# code, but if this is too complex or not plausible, I will do multiple queries like what I explained above.
I am asking this, because I am not sure how to get multiple conditions in a SELECT. I mean by this:
If there is more than one condition, how can I execute them or get all of them in a single query but that has all the returning things on point 3 ?
Example:
SELECT *
FROM WF_MailCondition
WHERE IDCondition = 1
this will return all conditions (keys can repeat) that has IDCondition equal to 1. But then I have to use all the three columns to create a condition like ID = 1 been ID in IDocField, = in ConditionOperator and 1 in ConditionValue.
I know there is some inconsistencies in the image, like IDCondition is NN (not null) but I said that it can be null (I am fixing this things).
My SQL:
SELECT
Subject, SendTo, CC, CCO, FileTemplate
FROM
WF_Mail AS M
INNER JOIN
WF_MailStatusAction AS S ON S.IDMail = M.IDMail
WHERE
(SELECT "HERE SELECT THE 3 COLUMNS TO USE A CONDITION" FROM WF_MailCondition WHERE IDCondition = S.IDCondition)
AND (S.IDAction = "ACTION FROM OUTSIDE" AND S.IDStatus = "STATUS FROM OUTSIDE")
How to do the SELECT "HERE SELECT 3 COLUMNS..." and if the IDCondition is NULL directly get the columns requested in the first SELECT (Subject, SendTo, etc.)
The "ACTION FROM OUTSIDE" is a parametrized statement.
Forget about foreign keys, we are not using it (boss' decisions...).
I am doing this because is for an "event" thing. I call a method in my C# code that will see if there are mails to send. But this mails can have none, one or more than one conditions. This is to have more options than all have to send a mail. Maybe in the workflow there is no need to send email but in code is "hardcoded" so with this I am totally free to do what I need about that feature.
Maybe all of this can be a function or a stored procedure but I am very ignorant about that and performance with them.
If you need more info, please tell me.
From what I understand, I would write a query like this:
select m.subject, m.cc
from wf_mail m
where exists (select 'true'
from wf_mailstatusaction s, wf_mailcondition c
where s.idmail=m.idmail
and s.idcondition = c.idcondition
and c.idocfield||c.conditionoperator||c.conditionValue = 'id>500'
group by s.idcondition
having count(s.idcondition) = 1)
or exists (select 'true'
from wf_mailstatusaction s, wf_mailcondition c
where s.idmail=m.idmail
and s.idcondition = c.idcondition
and c.idocfield||c.conditionoperator||c.conditionValue IN ('id>500', 'id<200')
group by s.idcondition
having count(s.idcondition) > 1)
or exists (select 'true'
from wf_mailstatusaction s
where s.idmail=m.idmail
and s.idcondition is null)
I would suggest the following:
- Create a QueryBuilder Class
Where you have methods like:
* AddSelect()
* AddFrom()
* AddWhere()
* ToString()
...
And then use the "middlelayer" to handle the query cunstruction based on the conditions
That way you have the most control over everything going on

Linq correlated subquery to same table on multiple columns

I've looked at several other questions related to correlated subqueries but it's still not clear to me how to accomplish what I need. I'm using Entity Framework and C#, and have a table called STEWARDSHIP with the following columns:
STEWARDSHIP_ID (the primary key)
SITE_ID
VISIT_DATE
VISIT_TYPE_ID
I need to identify cases where the same combination of SITE_ID, VISIT_DATE, VISIT_TYPE_ID exists more than once because it could represent a duplicate entry made by end users in error, and then I need to report on the details of these entries. In SQL I would do this by joining to the temporary result of a GROUP BY/HAVING like so:
SELECT * FROM stewardship AS s2,
(SELECT site_id, visit_type_id, CAST(visit_date AS DATE) AS visit_date
FROM stewardship
GROUP BY site_id, visit_type_id, CAST(visit_date AS DATE)
HAVING COUNT(*) > 1) AS s
WHERE s2.site_id = s.site_id
AND s2.visit_type_id = s.visit_type_id
AND CAST(s2.visit_date AS DATE) = s.visit_date
What's the best way to accomplish this in Linq?
Since you're open to a different approach that should be more performant, here is the new SQL to get what I think you're after.
select distinct s1.*
from stewardship s1
inner join stewardship s2 on
s1.stewardship_id <> s2.stewardship_id and
s1.site_id = s2.site_id and
s1.visit_type_id = s2.visit_type_id and
cast(s1.visit_date as date) = cast(s2.visit_date as date)
order by s1.site_id, s1.visit_type_id
Now, to translate that to LINQ, you can use the following statement.
var duplicates = (
from s in Stewardships
join s2 in Stewardships
on new { s.Site_id, s.Visit_type_id, s.Visit_date.Date } equals new { s2.Site_id, s2.Visit_type_id, s2.Visit_date.Date }
where s.Stewardship_id != s2.Stewardship_id
select s)
.Distinct()
.OrderBy(s => s.Site_id)
.ThenBy(s => s.Visit_type_id)
Note that you cannot use anything other than an equijoin for expression joins, so I had to put the non-equijoin (ensuring our matches aren't on the same record via PK) in the where expression. You could also accomplish this with lambdas via the Except() extension method.
The order by is there for readability of the results and to match the SQL statement above.
I hope this helps!
It would be fairly similar to what you've already got.
from s in context.stewardships
group s by new {s.site_id, s.visit_type_id, visit_date} into g
where g.Count() > 1
select g;
This would give you groups of stewardships with similar values. You could "flatten" those results with a SelectMany afterward, but you might find them more useful to work with in groups.
Note that you may need to use SqlFunctions or something to do the equivalent of the cast to date.

C# Recursive Query

I'm working on a project where i've used C# to populate a single MSSQL table of URLs from multiple sources.
The table contains link redirect info (example structure below).
RequestedURL, RedirectedURL
www.123.com, www.123.com/123
www.123.com/123, www.123.com/1234/link.asp
www.123.com/1234/link.asp, www.123.com/12345/link.asp
I'm very new to C# and need to write some sort of recursive Query to go through each redirectedurl, if it is in the requestedurl then to find the associate redirectedurl. Some URLs may have multiple redirects.
Since you have this data in your SQL Server database, one possible approach would be CTE's with recursion. This explanation looks a little confusing at first, but I think if you scroll down to the example it will be clear how to do this.
Without repeating the entire explanation here, this is an example of such a query:
USE AdventureWorks2008R2;
GO
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
AS
(
-- Anchor member definition
SELECT e.ManagerID, e.EmployeeID, e.Title, edh.DepartmentID,
0 AS Level
FROM dbo.MyEmployees AS e
INNER JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON e.EmployeeID = edh.BusinessEntityID AND edh.EndDate IS NULL
WHERE ManagerID IS NULL
UNION ALL
-- Recursive member definition
SELECT e.ManagerID, e.EmployeeID, e.Title, edh.DepartmentID,
Level + 1
FROM dbo.MyEmployees AS e
INNER JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON e.EmployeeID = edh.BusinessEntityID AND edh.EndDate IS NULL
INNER JOIN DirectReports AS d
ON e.ManagerID = d.EmployeeID
)
-- Statement that executes the CTE
SELECT ManagerID, EmployeeID, Title, DeptID, Level
FROM DirectReports
INNER JOIN HumanResources.Department AS dp
ON DirectReports.DeptID = dp.DepartmentID
WHERE dp.GroupName = N'Sales and Marketing' OR Level = 0;
GO
You could create a dictionary with RequestedUrl as the key and RedirectedUrl as the value. So once you find the requestedUrl you could find its redirectedURL and if that redirectedURL has a redirectedURL, you could find that too.
If I get you right, you want a neat small C# function to find the last redirection, right?
In that case this should do it:
string GetRedirectionFromDatabase(string requestUrl)
{
// Fetch redirect form DB, or if none exists return null
}
string GetFinalUrl(string requestUrl)
{
var redirection = GetRedirectionFromDatabase(requestUrl);
return redirection != null ? GetFinalUrl(redirection) : requestUrl;
}

How to check the null of an embedded linq to entities statement

Using linq to entities i am connecting to a database, the database has tables in it that has payments that have a multi to multi relationship with jobs. This is acheived via an allocs table. I want a list box with all the jobs that has a column called due price which takes all of the allocations of payments for this job and takes that away from the job price. However, using the below linq to entities statement. The problem is that if the job has no allocations it returns null and therefore the due payment is empty. What i really want is for the due payment to be the job price if there are no allocations however, i cannot think of a way around this. Please help before i finally go insane :-(
var jobs = from j in data.jobs
where j.property.customer.id == customerid
&& j.completed != null
select new
{
j.id,
j.price,
dueprice = j.price - ( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum(),
lineone = j.property.lineone,
postcode = j.property.postcode,
jobtype = j.jobtype.name,
j.completed
};
You can also use the Null Coalescing operator to simplify code like this:
dueprice = j.price - (( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum() ?? 0)
This operator returns the first value which is not null, so myNum ?? 0 will return myNum if it is not null, and if it is null the operator will return 0.
select a.amount ).Concat(/* array with one zero element */).Sum()
I actually found an answer my self, i will try oleg's as well as it seems to be a little more precise but i thought of doing this
dueprice = j.price - (( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum())==null ? j.price:
//sets the due price to the normal sum if it has allocs
j.price - ( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum(),
however i like the idea of only adding on more line of code instead of my several(plus it's repeated code) i will try this and let you all know. Thanks for the response.

Categories