Meaning of a linq statement - c#

I saw this code and trying to understand the ling. feater.groupId = 30
var myData = await _source.GetDataByIdNumber(staffId);
if(!myData.select(x=>x.Id).contains(feater.groupId))
{
status.IsValid = false;
}
How do I interprete this line in sql or english?
if(!myData.select(x=>x.Id).contains(feater.groupId))

"If" check is satisfied if none of the myData entities have an Id value of 30
Assuming standard C# Linq operators as per comments!
if(!myData.Select(x=>x.Id).Contains(feater.groupEntityId));
Selects the Id property of all entities. Determines if value 30 is contained within the selected sequence.
Direct T-SQL translation is:
SELECT CASE
WHEN 30 IN (
SELECT Id
FROM <YourTable>
)
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
So, if the supplied value is equal to any of the Ids returned by the select sub-query, return true, else return false.

Related

How to use JSON_VALUE in WHERE clause with type conversion in CodeFirst

I have an order with specific data. Orders stores in dbo.Orders table which have SpecificData field (nvarchar(max)) for json-data.
I use EF Code first. For JSON_VALUE I wrote DBFunction translation using this answer (https://stackoverflow.com/a/50490674/1411598) but I faced with a problem when I tried to add WHERE condition with INT type.
if (specificFilter.RegionIds != null && specificFilter.RegionIds.Count() > 0)
{
query = query.Where(g => specificFilter.RegionIds.Contains(DbFunc.JsonValue(g.specificData, "$.RegionId")));
}
Because JsonValue function return only string I cannot add clause with "contains".
Think a little bit I try to write convert UDF function and translate to EF object.
Here is my code:
if (specificFilter.RegionIds != null && specificFilter.RegionIds.Count() > 0)
{
query = query.Where(g => specificFilter.RegionIds.Contains(DbFunc.ConvertToInt(DbFunc.JsonValue(g.specificData, "$.RegionId"))));
}
UDF is very simple
CREATE FUNCTION [dbo].[ConvertToInt] (#value nvarchar(100))
returns int
AS
BEGIN
return convert(INT, #value)
END
It's works fine, but query is too slow:
WHERE (0 = [Extent1].[Status])
AND ([dbo].[ConvertToInt](JSON_VALUE([Extent1].[specificData], N''$.RegionId'')) IN (1))
It's bad because if I have a lot of rows query works very slow (UDF runs for each row)
So, can you help me to find the solution?
I want to finally get such query:
WHERE (0 = [Extent1].[Status])
AND (CAST(JSON_VALUE([Extent1].[specificData], N''$.RegionId'') as int) IN (1))
By the other words can I translate CAST or CONVER function in EF DBFunction? Or may be there are some other ways?

NullReferenceException using COALESCE in SQL query

Perhaps I'm misunderstanding COALESCE, but in essence, what I'm trying to do is run a select query that if it returns NULL instead of an int, instead return 0 for the purposes of ExecuteScalar().
SQL Server query:
SELECT TOP 1 COALESCE(SeqNo,0)
FROM tblProjectChangeOrder
WHERE ProjectID = XXXXX
ORDER BY SeqNo DESC
If the supplied ProjectID exists in the Change Order table, it returns the expected highest SeqNo. However, if the supplied ProjectID has no existing Change Orders (thus returns NULL for SeqNo), rather than the COALESCE returning 0, I am still getting NULL.
Am I just getting the syntax wrong or is what I want to do possible with COALESCE? The other option I see is to have my ExecuteScalar() pass to a nullable int, then follow that with a ?? to coalesce in my C# codebehind.
As john has mentioned in the comments, COALESCE operates at row level. If a table contains no rows, or a statement returns no rows, then no rows will be returned. Take the simple example below:
CREATE TABLE #Sample (ID int);
SELECT COALESCE(ID, 0)
FROM #Sample;
DROP TABLE #Sample;
Notice that nothing is returned.
Instead, one method is to use a subquery. For your query, that would result in:
SELECT COALESCE(SELECT TOP 1 SeqNo
FROM tblProjectChangeOrder
WHERE ProjectID = XXXXX
ORDER BY SeqNo DESC),0) AS SeqNo;
This also assumes that Seqno has a data type of int; otherwise you're likely to get a conversion error.
My guess is that the null reference exception occures on the code and has nothing to do with the sql query. It might just be that your code is not handling that you return no rows (or no scalar in your case) but you might be trying to access it somewhere in c#.
Show us the line of code that is throwing this exception in c# so we might be able to confirm this.
regards
Edit : From this similar topic
In your c# code you might want to try ("cmd" being your "SqlCommand" object):
int result = 0;
int.TryParse(cmd.ExecuteScalar(), out result);
or in one line
int.TryParse(cmd.ExecuteScalar(), out int result);
I don't know if it is the most suitable solution for you but I hope it is a start.
As covered null and no row are not the same.
This sample covers it.
set nocount on;
select isnull(1, 0)
where 1 = 1;
select isnull(1, 0)
where 1 = 2;
select isnull(null, 0)
where 1 = 1;
select isnull(null, 0)
where 1 = 2;
-----------
1
-----------
-----------
0
-----------
this should work
select top 1 isnull(seq, 0)
from (select null as seq
union all
select max(seq) from tblProjectChangeOrder where ProjectID = XXXXX
) tt
order by seq desc

'Contains' dose not works with string?

I am using dynamic linq to parse some conditions. I wrote stored procedure and want to filter it dynamicly.
this is my procedure:
;WITH cte
AS
(
SELECT
ID
,[NO]
,Firstname
,Lastname
,PersonalNO
,ReferanceID
,CAST('' AS VARCHAR(MAX)) AS ReferanceNO
FROM dbo.Employees WHERE ReferanceID IS NULL
UNION ALL
SELECT
c.ID
,c.[NO]
,c.Firstname
,c.Lastname
,c.PersonalNO
,c.ReferanceID
,CASE
WHEN ct.ReferanceNO = ''
THEN CAST(ct.[NO] AS VARCHAR(MAX))
ELSE CAST(ct.[NO] AS VARCHAR(MAX))
END
FROM dbo.Employees c
INNER JOIN cte ct ON ct.ID = c.ReferanceID
)
SELECT * FROM cte
and in C# I am calling this procedure;
public List<Employees> GetEmployees(string searchValue, int skip, int pageSize, string sortColumn, string sortColumnDir)
{
var query = DB.sp_GetConsultants().ToList();
var totalRecords = query.Count;
query = query.Where(searchValue).ToList(); // if the searchValue is value
//"PersonalNO.Contains(\"15\")" it filters, with this kind of value
//"Lastname.Contains(\"fish\")" it dose not, but with "Fish" it does. Is the matter with uppercase?
}
and i uploaded table picture:
What is the problem?
string.Contains is case sensitive; as you noticed, searching for "fish" won't return "Fisher", even though searching for "Fish" will. There doesn't seem to be a case insensitive version in .NET (even though you can compare strings case insensitively as an option).
As a workaround, you can convert both strings to lowercase or uppercase (ToLower / ToUpper) before comparing. This might have some issues with certain non-Latin characters, however.
I think there is also a collation option in SQL Server which lets you specify the case sensitivity for strings, if you want to do the comparison at the database level instead.

LINQ to SQL complex query problem

I have 3 tables: Principal (Principal_ID, Scale), Frequency (Frequency_ID, Value) and Visit (Visit_ID, Principal_ID, Frequency_ID).
I need a query which returns all principals (in the Principal table), and for each record, query the capacity required for that principal, calculated as below:
Capacity = (Principal.Scale == 0 ? 0 : (Frequency.Value == 1 ? 1 : Frequency.Value * 1.8) / Principal.Scale)
I'm using LINQ to SQL, so here is the query:
from Principal p in ShopManagerDataContext.Instance.Principals
let cap =
(
from Visit v in p.Visits
let fqv = v.Frequency.Value
select (p.Scale != 0 ? ((fqv == 1.0f ? fqv : fqv * 1.8f) / p.Scale) : 0)
).Sum()
select new
{
p,
Capacity = cap
};
The generated TSQL:
SELECT [t0].[Principal_ID], [t0].[Name], [t0].[Scale], (
SELECT SUM(
(CASE
WHEN [t0].[Scale] <> #p0 THEN (
(CASE
WHEN [t2].[Value] = #p1 THEN [t2].[Value]
ELSE [t2].[Value] * #p2
END)) / (CONVERT(Real,[t0].[Scale]))
ELSE #p3
END))
FROM [Visit] AS [t1]
INNER JOIN [Frequency] AS [t2] ON [t2].[Frequency_ID] = [t1].[Frequency_ID]
WHERE [t1].[Principal_ID] = [t0].[Principal_ID]
) AS [Capacity]
FROM [Principal] AS [t0]
And the error I get:
SqlException: Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.
And ideas how to solve this, if possible, in one query?
Thank you very much in advance!
Here are 2 ways to do this by changing up your approach:
Create a user defined aggregate function using the SQL CLR. This may not be the right solution for you, but it's a perfect fit for the problem as stated. For one thing, this would move all of the logic into the data layer so LINQ would be of limited value. With this approach you get effeciency, but there's a big impact on your architecture.
Load Visit and Fequency tables into a typed DataSet and use LINQ to datasets. This will probably work using your existing code, but I haven't tried it. With this approach your achitecture is more or less preserved, but you could have a big efficency hit if Visit and Frequency are large.
Based on the comment, I've an alternative suggestion. Since your error is coming from SQL, and you aren't using the new column as a filter, you can move your calculation to the client. For this to work, you'll need to pull all the relevant records (using DataLoadOptions.LoadWith<> on your context).
To further your desire for use with binding to a DataGrid, it'd probably be easiest to bury the complexity in a property of Principal.
partial class Principal
{
public decimal Capacity
{
get
{
return this.Scale == 0 ? 0 : this.Visits.Select(v =>
(v.Frequency.Value == 1 ? 1 : v.Frequency.Value * 1.8) / this.Scale).Sum();
}
}
}
Then your retrieval gets really simple:
using (ShopManagerDataContext context = new ShopManagerDataContext())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Principal>(p => p.Visits);
options.LoadWith<Visit>(v => v.Frequency);
context.LoadOptions = options;
return (from p in context.Principals
select p).ToList();
}

Combine two counting queries into one Boolean query

I can't think of a good way to write this as a single query.
int count1 = query1.Count();
int count2 = query2.Count();
return count1 > count2;
Please note that I'm interested in ways to write a single query that returns a Boolean value and gets evaluated once on the server.
Please note that I'm interested in ways to write this with LINQ, not with SQL.
Try
return query1.Count() > query2.Count();
It's not worth trying to run two unrelated queries in the same server call, but here it is:
SELECT CASE WHEN (SELECT COUNT(*) FROM Products)
> (SELECT COUNT(*) FROM Orders)
THEN CAST(1 as bit)
ELSE CAST(0 AS BIT)
However, I seriously doubt you can get LINQ to create this kind of query. You would have to call a stored procedure instead.
What's wrong with
var result= query1.Count() > query2.Count();
return result; // result is of type bool
EDIT:
If you really want to execute it as one query on the server, you could use DataContext.ExecuteQuery to execute a dynamic query.

Categories

Resources