How to exclude from query's result in linq? - c#

In the first picture I have the result of first query, the highlighted part indicates the rows that would be excluded by applying the filter on the second query, in the second I have the result of query select * from exlusion_table
I have to make a change to the first query to have it exclude the items retrieved from the second query
the first query:
var u = from a in cx.VW_LIST
where (a.PRJ == codPrj) && (a.DATE > date_ || a.DATE == null || date_ == null)
&& (x.Contains(a.CODE) || x.Count() == 0)
select a)
the second query:
var y = from esc in cx.EXCLUSION select esc
The first query should be modified to exclude all the rows that have the value fcode = the fcode of the second query (in the case in which the fscode of the second query = null) or that (fcode = fcode of the second query && fscode = fscode of the second query )

You can use Any(). ie:
var u = from a in cx.VW_LIST
where (a.PRJ == codPrj)
&& (a.DATE > date_ || a.DATE == null || date_ == null)
&& (x.Contains(a.CODE) || x.Count() == 0)
&& (!cx.EXCLUSION.Any( y => x.fscode == y.fscode && x.fcode == y.fcode ))
select a)

There are two approaches to this, one is to use ! and ANY() to filter out records found within the other list, this will compile into WHERE NOT EXISTS(_exclusion_) filter expression
var excluded = cx.EXCLUSION.AsQueryable();
var query = from vw in cx.VW_LIST
where vw.PRJ == codPrj
where vw.DATE == null || date_ == null || vw.DATE > date_
where !x.Any() || x.Contains(vw.CODE)
where !excluded.Any(esc => vw.fcode == esc.fcode
&& (esc.fscode == null || vw.fscode == esc.fscode))
select vw;
var results = query.ToList();
The tricky element is the null for fscode in the excluded table, that needs to act as wildcard match, or negate the fscode comparison.
It is not necessary to split the excluded query out into it's own query, we could have referenced to the cx.EXCLUSION table directly and it would have exactly the same effect, this shows you an encapsulation technique for building the LINQ query in a way that you could easily increase the complexity of the exclusion lookup without creating a mess of your overall query.
You may also find need to conditionally build the query, this is where fluent syntax provides a more modular approach:
bool filterExcludedRecords = true;
...
var excluded = cx.EXCLUSION.AsQueryable();
var query = cx.VW_LIST.Where(vw => vw.PRJ == codPrj)
.Where(vw => vw.DATE == null || date_ == null || vw.DATE > date_)
.Where(vw => !x.Any() || x.Contains(vw.CODE));
if(filterExcludedRecords)
query = query.Where(vw => !excluded.Any(esc => vw.fcode == esc.fcode
&& (esc.fscode == null || vw.fscode == esc.fscode)));
var results = query.ToList();
OUTER JOIN
Another method is to use a LFET OUTER JOIN where the exclusion match is not found:
var excluded = cx.EXCLUSION.AsQueryable();
var query = from vw in cx.VW_LIST
where vw.PRJ == codPrj
where vw.DATE == null || date_ == null || vw.DATE > date_
where !x.Any() || x.Contains(vw.CODE)
from esc in excluded.Where(e => vw.fcode == e.fcode
&& (e.fscode == null || vw.fscode == e.fscode))
.DefaultIfEmpty()
where esc.fscode == null
select vw;
var results = query.ToList();
The WHERE NOT EXISTS is often superior in terms of performance, OUTER JOIN may provide better response in an un-optimised table or when the number of rows in the exclusion list is significantly small and the number of rows in the main table is very large.
I include this query option for completeness, it is not well known that you can create simple outer joins by adding a new from clause to the query.

Related

FirstOrDefault LINQ query with a condition

I am new to LINQ queries and want to use FirstOrDefault in my existing LINQ query.
List<vUserAll> employee = (from o in db.vUsersAll
where (o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName) && o.Domain == "dExample"
select o).ToList();
What's the correct way to do this?
This can be simplified further as:
var filtered = db.vUsersAll.FirstOrDefault(u => u. Field == filter);
If the above mentioned is the case, then you can use a labmda as in the following:
var firstOrDefaultResult = db.vUsersAll.Where(o=>
(o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName)
&& o.Domain == "dExample").FirstOrDefault()
If you want to use the same above expression then,
vUserAll employee = (from o in db.vUsersAll
where (o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName) && o.Domain == "dExample"
select o).FirstOrDefaul();
It's a lot easier if you only use LINQ extensions. For example,
var filtered = all users.Where(u => u. Field == filter).FirstOrDefault();

Write LINQ query without select or group clause using extension method syntax

I'm writing a function that builds a query based on a condition. What I want to do is have a different select statements for each condition (the query in a separate method, then each user can have his\her own select statements).
Kinda like this
var q = BuildQuery();
var nq = q.Select(...);
return nq.ToList();
Will write a query using extension method syntax solves the ' a query body must end with a select or group clause'.
Here's my query
return (from ScheduleItemAttendee SIA in context.ScheduleItemAttendees
join AttendeeService AS in context.AttendeeServices on SIA.Id equals AS.AttendeeId into x
from Att in x.DefaultIfEmpty()
where (Att == null || !Att.IsDeleted)
&& !(SIA == null || SIA.IsDeleted)
&& ((string.IsNullOrEmpty(criteria.Keyword)) || (SIA.Individual.FullNameAr.Trim().ToLower().Contains(criteria.Keyword) || SIA.Individual.FullNameEn.Trim().ToLower().Contains(criteria.Keyword))));
Try this:
public IQueryable<T> BuildQuery<T>(Expression<Func<ScheduleItemAttendee, AttendeeService, T>> selector)
{
return
from ScheduleItemAttendee SIA in context.ScheduleItemAttendees
join AttendeeService AS in context.AttendeeServices on SIA.Id equals AS.AttendeeId into x
from Att in x.DefaultIfEmpty()
where (Att == null || !Att.IsDeleted)
&& !(SIA == null || SIA.IsDeleted)
&& ((string.IsNullOrEmpty(criteria.Keyword))
|| (SIA.Individual.FullNameAr.Trim().ToLower().Contains(criteria.Keyword)
|| SIA.Individual.FullNameEn.Trim().ToLower().Contains(criteria.Keyword)))
select selector(SIA, null);
}
Then use it like this:
var q = BuildQuery((sia, att) => 42);
(Put something meaningful in the place of 42.)

linq query with or and operator together

I need to run multiple conditions in linq query with or, and operator. First I need to compare with three conditions, which are separated by || operator, once one of the conditions is true I also need to see if check is true which I using && operator but I got all the result
so for example from following query, if ModuleLead == ContextSession.StaffID then record must have ClinicalSupervisorCheck == true
var query_b = (from b in activeAssessmentWithRemidiation
where b.AssessorID == ContextSession.StaffID
|| b.ModuleLead == ContextSession.StaffID
&& b.ClinicalSupervisorCheck == true
|| b.SeniorStaffID == ContextSession.StaffID
&& b.ModuleLeadCheck==true
select b).ToList();
the above query is running on following table records
It could be worth wrapping the AND conditions in parathensis, for example:
var query_b = (from b in activeAssessmentWithRemidiation
where b.AssessorID == ContextSession.StaffID ||
(b.ModuleLead == ContextSession.StaffID && b.ClinicalSupervisorCheck == true) ||
(b.SeniorStaffID == ContextSession.StaffID && b.ModuleLeadCheck==true)
select b).ToList();
Use parenthesis for that. Remember that operator && will be tested before || operators if you don't use parenthesis.
proper use of ( and )
var query_b = (from b in activeAssessmentWithRemidiation
where (((b.AssessorID == ContextSession.StaffID
|| b.ModuleLead == ContextSession.StaffID )
&& b.ClinicalSupervisorCheck == true)
||
(b.SeniorStaffID == ContextSession.StaffID
&& b.ModuleLeadCheck==true))
select b).ToList();

Build a Linq statement with IsNullOrEmpty check

How can I build a single Linq statement where it check returns all records unless the param is passed? If param is empty then ignore specific 'where'.
I have tried using IsNullOrEmpty within the WHERE but I get an error.
Here are the NON-REQUIRED form field for searching for Invoices.
Invoice Id, Check Number, State Issued
var invoices = ctx.Invoices; <-- get all invoiced
if (inputInvoiceId > 0)
invoices = from i in invoices
where i.id == inputInvoiceId
select i;
if (!string.IsNullOrEmpty(inputCheckNumber))
invoices = from i in invoices
where i.checkNumber == inputCheckNumber
select i;
if (!string.IsNullOrEmpty(inputState))
invoices = from i in invoices
where i.state == inputState
select i;
You could build your query by conditionally appending where clauses like this:
var invoices = ctx.Invoices.AsQueryable();
if (inputInvoiceId > 0)
invoices = invoices.Where(x => x.id == inputInvoiceId);
if (!string.IsNullOrEmpty(inputCheckNumber))
invoices = invoices.Where(x => x.checkNumber == inputCheckNumber);
if (!string.IsNullOrEmpty(inputState))
invoices = invoices.Where(x => x.state == inputState);
return invoices.ToList();
Each additional where clause further filters your results, but the query itself won't be executed (nor any data retrieved) until you call ToList().
What #GrantWinney said will work. Alternatively, you can deal with it in a single query, which may or may not have query compilation/cache benefits if you are concerned about such things:
// Query short-circuit parameters
var invoiceNotSpecified = inputVoiceId == 0;
var checkNumberNotSpecificed = String.IsNullOrEmpty(inputCheckNumber);
var stateNotSpecified = String.IsNullOrEmpty(inputState);
// Query
var invoices = from i in ctx.Invoices
where (invoiceNotSpeficied || i.id == inputInvoiceId) &&
(checkNumberNotSpecified || i.checkNumber == inputCheckNumber) &&
(stateNotSpecified || i.state == inputState)
select i;

Use Linq to search through list

Is it possible to use a Linq query to search through a List? In my web app I have to potentially process over 14k records based upon a spreadsheet uploaded by the user. With each record processed, I need to compare that record against what we currently have in our database in order to make sure we either aren't adding a duplicate or I know what record I need to be updating/editing.
Instead of hitting the database 14k times or more, I wanted to pull all the records contained on this table into a List, and then perform a search based on a set of conditions.
Here is the Linq query I currently have that hits the database. The business rules are pretty... complicated so I won't bother you with the details but these are the conditions that I need to satisfy for the search. I've tested this query and it returns the expected results.
var previousZips = (from z in db.ZipCodeTerritory
where (item.ZipCode.Equals(null) ?
z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
SqlFunctions.DateDiff("DAY", z.EndDate, item.EndDate) == 0 :
z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
SqlFunctions.DateDiff("DAY", z.EndDate, item.EndDate) == 0 &&
(z.ZipCode.Equals(null) || z.ZipCode.Equals(item.ZipCode)))
select z).ToList();
What I would like to do, however, is create a List of all the records on the table like this:
List<ZipCodeTerritory> allRecords = (from z in db.ZipCodeTerritory
select z).ToList()
and then use a query similar to this to pull the record I'm looking for from the list:
List<ZipCodeTerritory> previousZips = allRecords.Where(
z => (item.ZipCode.Equals(null)
? z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
SqlFunctions.DateDiff("DAY", z.EndDate,
item.EndDate) == 0
: z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
SqlFunctions.DateDiff("DAY", z.EndDate,item.EndDate) == 0 &&
(z.ZipCode.Equals(null) || z.ZipCode.Equals(item.ZipCode))
)
).ToList();
The query above (from the List), however, throws the following error:
This function can only be invoked from LINQ to Entities.
Answered my own question. The problem here was the SqlFunction. By removing that from the query and re-writing it like this it works
List<ZipCodeTerritory> previousZips = allRecords.Where(
z => (item.ZipCode.Equals(null)
? z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
z.EndDate.Date == item.EndDate.Date
: z.StateCode.Equals(item.StateCode) &&
z.ChannelCode.Equals(item.ChannelCode) &&
z.EndDate.Date == item.EndDate.Date &&
(z.ZipCode.Equals(null) || z.ZipCode.Equals(item.ZipCode))
)
).ToList();
Instead of
SqlFunctions.DateDiff("DAY", z.EndDate,item.EndDate) == 0
use
z.EndDate.Subtract(item.EndDate).TotalDays == 0

Categories

Resources