How to structure a count query that includes children - c#

I have a hierarchy of message types: Request which can contain Offers, which can contain Dialogs. I'm trying to query the database to determine how many of each chain contains a message that has not been read by a given User.
Here's the working query in SQL (but I'd like to be able to do it in a LINQ to Entities query, if possible):
SELECT COUNT(msgID)
FROM tblMessage
WHERE msgTypeID = 1 // Request type
AND msgUserID = 7 // The creator of the Request
AND ( msgWhenRead IS NULL // That they didn't read the top Request, OR has any Offers, etc that haven't been read
OR 0 < ( SELECT COUNT(msgID)
FROM tblMessage
WHERE msgTypeID = 2
AND msgRecipientID = 7 // Looking for Recipient here, as the creator is the person who is making the offer, but the Recipient is being set to the person who made the parent Request.
AND ( msgWhenRead IS NULL
OR 0 < ( SELECT COUNT(msgID)
FROM tblMessage
WHERE msgTypeID = 3 // Is a Dialog
AND msgRecipientID = 7 // Same deal with the Recipient here as with Offers.
AND msgWhenRead IS NULL
)
)
)
)
This is my attempt that is not working with EntityFramework:
response.Requests = repository.Messages
.OfType<Request>()
.Where(r => r.Creator.Id == request.UserId &&
r.dbIsDeleted == "N" &&
r.dbStatusId != (int)RequestStatus.Inappropriate &&
( r.WhenRead == null ||
r.Offers.Select(o => o.RecipientId == request.UserId &&
o.dbIsDeleted == "N" &&
o.dbStatusId != (int)OfferStatus.Inappropriate &&
( o.WhenRead == null ||
o.Dialogs.Select(d => d.RecipientId == request.UserId &&
d.dbIsDeleted == "N" &&
d.dbStatusId != (int)DialogStatus.Inappropriate &&
d.WhenRead == null)
.Any()
))
.Any()
))
.Count();
I tried changing to SelectMany at both r.Offers.Select() and o.Dialogs.Select(), but was getting compiler errors. I was thinking perhaps that was the issue.
What I'm getting right now is that with everything having been read, the Entity Framework query is still finding the top Request, and I'm not sure why. The structure of the EF SQL that gets generated isn't close to that of the one I wrote myself. I can provide the EF SQL if people think that may help.

Related

C# Linq to EF Separate Subquery into Expression

I have a complex where clause in my EF linq statement which repeats a subquery expression, on _db.OPESRRecoveryElements, but with different parameters, one of which is depending on records from the main entity, OPCases/OPCaseDto.
The query as it is works, but its hard for people to read. Ideally I'd like to be able to create an expression which could be re-used at the 3 necessary points and would still allow it to execute as a single, server-side SQL statement.
Is there a way to create an Expression / IQueryable definition which can be used for a subquery like this?
List<OPCaseDto> opCases = await _db.OPCases
.ProjectTo<OPCaseDto>(_autoMapperConfig, null, requestedExpands)
.Where(c =>
c.OPStatusId == OPStatusIds.AwaitingRecoveryElement
&& (
(c.OPCategoryLetter == "B"
// Only need a gross pensionable element if there is an outstanding gross pensionable figure
&& (c.GrossOverpaidPensionable - c.GrossRecoveredPensionable == 0
|| _db.OPESRRecoveryElements.Any(e => !e.NonPensionable && e.OPRecoveryMethod.OPTypeLetter == "G"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo)))
// Only need a gross non-pensionable element if there is an outstanding gross non-pensionable figure
&& (c.GrossOverpaidNonPensionable - c.GrossRecoveredNonPensionable == 0
|| _db.OPESRRecoveryElements.Any(e => e.NonPensionable && e.OPRecoveryMethod.OPTypeLetter == "G"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo))))
|| (c.OPCategoryLetter == "D"
// Don't need to check for an outstanding net figure - if the case is net and isn't complete, there will be one!
&& _db.OPESRRecoveryElements.Any(e => e.OPRecoveryMethod.OPTypeLetter == "N"
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery)
&& rp.AssignmentNo == c.RecoveryAssignmentNo)))
)
)
.AsNoTracking()
.ToListAsync();
If it wasn't for the c.RecoveryAssignmentNo part, I could easily create an expression like:
public Expression<Func<OPESRRecoveryElement, bool>> NoActiveRecoveryPlans(string opType, bool nonPen)
{
return e => e.OPRecoveryMethod.OPTypeLetter == opType
&& e.NonPensionable == nonPen
&& !e.OPRecoveryPlans.Any(rp
=> (rp.RecoveryStatus == OPRecoveryStatuses.NotStarted || rp.RecoveryStatus == OPRecoveryStatuses.InRecovery));
}
and use it like:
(c.OPCategoryLetter == "B"
// Only need a gross pensionable element if there is an outstanding gross pensionable figure
&& (c.GrossOverpaidPensionable - c.GrossRecoveredPensionable == 0
|| _db.OPESRRecoveryElements.Any(NoActiveRecoveryPlans("G", false)))
and it would get executed before the query to get the OPCases.
I could also fetch all the OPCaseDto records and OPESRRecoveryElements as separate queries and filter in memory, but I don't want to do that.
If I add a parameter to the function, string assignmentNo, I (unsurprisingly) get an error - "Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression3' to type 'System.Linq.Expressions.LambdaExpression'"

EF6 Get Record where info from two different tables equal x

I am guessing this is a JOIN issue - which I don't understand anyway.
but I have to get a Record from EUReporteds if the Where clause is correct and if the Where clause on another table is NOT correct.
The coding I am using is truly horrific
do
{
// Get the next Unprocessed.
if (skip == 0)
eur = de.EUReporteds.Where(r => r.Processed == false && r.ReportProcessingCount < 11).FirstOrDefault();
else
eur = de.EUReporteds.Where(r => r.Processed == false && r.ReportProcessingCount < 11).OrderBy(ob => ob.id).Skip(skip).FirstOrDefault();
// Have we personally already done this?
pr = de.ProcessingResults.Where(p => p.UserId == CurrentUser.UserId && p.id == eur.id).FirstOrDefault();
skip++;
} while (pr != null || eur == null); // If so repeat
So what I am trying to do here is:
Get a record from EUReporteds
WHERE the record has not been processed
AND WHERE the RecordProcessingCount is less than 11
Check to see if this logged in user has already processed that record by
Getting The ProcessingResults Record where The CurrentUser exists
AND WHERE the ID of EUReporteds also exists
IF ProcessingResults Record Exists then we have done this record find the next until The ProcessingResults record can not be found.
Could someone please show me how to write this properly please.
Try this(using linq):
var firstUnprocessedRecord = (from eur in de.EUReporteds
where !eur.Processed && eur.ReportProcessingCount < 11
&& !(de.ProcessingResults.Any(o=>o.UserId == CurrentUser.UserId && o.id == eur.id))
orderby eur.id
select eur).FirstOrDefault();

A method that searches for entities, a certain property of which matches a given criteria in Entity Framework

Okay, I'm not sure whether the title of the question is clear enough, but I couldn't think of a simpler explanation in one sentence.
I'm making a search page for a ASP.NET web application. I want to allow the users to search using different filters.
I came up with a method that filters the Publications (these are the entities that the user will retrieve) based on the type of Categories that the user has chosen.
List<Publication> publications =
(from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
The above linq query is equivalent to the following sql one:
select * from Publication p where p.CategoryId in (#chosenCategoryIds)
Everything works perfect but the problem is that I want to allow the user to choose from more filters, for example, not only categories, but users, tags, titles and etc.
So, if I want this, I should make a lot of conditional statements checking whether the user has provided given filters or not, something like:
if (categories != null)
{
var publications = (from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
if (users != null)
{
publications = (from p in publications
where users.Contains((int)p.UserId)
select p).ToList();
}
}
else
{
// Handle other situations..
}
So, it turns out that I'm doing basically one and the same thing for different filters. My question is whether somebody knows or can think of a better way to achieve this? If not, let's take a look at the following method:
public List<InterpretumDAL.Publication> FilterPublications(List<InterpretumDAL.Publication> oldList, int[] values)
{
List<InterpretumDAL.Publication> newList =
(from p in oldList
where values.Contains((int)p.CategoryId)
select p).ToList();
return newList;
}
What I try to achieve with the method above is to filter a given list by a given array of values. The thing is that I don't know how to tell the method which property to compare, so I can call it something like this:
newList = FilterPublications(publications, categories, CATEGORY_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, users, USER_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, tags, TAG_PROPERTY_OF_PUBLICATION);
Edit:
According to LiquidPony's answer I did it this way:
var publications = (from p in GetPublications()
where (
(categories == null || categories.Length == 0 || (p.CategoryId.HasValue && categories.Contains((int)p.CategoryId)))
&& (users == null || users.Length == 0 || users.Contains(p.UserId))
&& (tags == null || tags.Length == 0 || tags.Contains(p.TagId))
)
select p).ToList();
Taking LiquidPony's answer, I think it would be better to do it like this by using ternary operators.
var query = from p in GetPublications()
where (
(categories != null ? categories.Contains((int)p.CategoryId) : true)
&& (users != null ? users.Contains((int)p.UserId) : true)
&& (tags != null ? tags.Contains((int)p.TagId) : true)
) select p;
If one of the variables isn't provided, that statement will simply evaluate to true and not be included in the SQL evaluation (as far as I know).
You want to avoid using OR's unless absolutely necessary. They are quite inefficient in SQL, and you can usually always find an alternative to get what you want without using them.
The simplest way would be to do something like this:
var query = from p in GetPublications()
where (
(categories == null || categories.Contains((int)p.CategoryId))
&& (users == null || users.Contains((int)p.UserId))
&& (tags == null || tags.Contains((int)p.TagId))
) select p;
That would work if you want to match all criteria. You can switch the && to || if you want to return entities which only match one criteria.

Linq query to check for duplicate values

In one of my pages I need to check if the entered information about a customer consists duplicate PAN NO,Email,Mobile No which may have been entered previously.Currently I am trying it using this Linq To SQL statement
var duplicate = (from dup in dt.Data_Customer_Logs
where dup.cPanGirNo == panno
|| dup.cEmail == email
|| dup.nMobileNo.ToString() == mobno
select dup).Any();
It is working but can anyone help me as to what is the correct method to solve my issue.Also if there are no records found what would be the result.
Any suggestions are welcome.
bool duplicateExists = dt.Data_Customer_Logs.Any(x =>
x.cPanGirNo == panno
|| x.cEmail == email
|| x.nMobileNo.ToString() == mobno);
This is a tad cleaner if you just want to know if such records exist or not. And I think it will avoid bringing back multiple records to the client side and then doing IEnumerable<T>.Any on the results.
If you need to also get back the records that match the criteria, you can use IQueryable<T>.Where:
var duplicates = dt.Data_Customer_Logs.Where(x =>
x.cPanGirNo == panno
|| x.cEmail == email
|| x.nMobileNo.ToString() == mobno);
if(duplicates.Any())
{
// use duplicates...
foreach(var dup in duplicates)
{
//use dup.cEmail, dup.nMobileNo, etc.
try this
var duplicate = (from dup in dt.Data_Customer_Logs
where dup.cPanGirNo == panno
|| dup.cEmail == email
|| dup.nMobileNo.ToString() == mobno
select dup).FirstOrDefault();
if(duplicate != null && duplicate.Any())
//here logic of what should happend if there is something in db

LINQ Syntax for Current Revision

How can I write this SQL statement using C# and LINQ? I am quering an Oracle database and the table has multiple revisions of the records. Therefore, I want onyl the current revision of each record contained in the table.
The SQL looks like this:
select TP_ID, TP_TEXT, TP_DEFN_SAKEY
from TP_DEFN tp1
where tp1.TP_ACTIVE_FLAG = 'Y' and
tp1.FAMILY_ID = 1 and
tp1.TP_DEFN_REV_DTS = (select max(TP_DEFN_REV_DTS)
from TP_DEFN tp2
where tp2.family_id = tp1.family_id and tp2.tp_id = tp1.tp_id )
order by TP_ID
TP_DEFN_REV_DTS is the date time field that stores the current revision.
I am a beginner with LINQ and have been struggling to find an workable solution. Every time that I try grouping in the LINQ query I get an error
GroupBy is not supported
Try something like this:
var res =
from tp1 in TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y" &&
tp1.FAMILY_ID == 1 &&
tp1.TP_DEFN_REV_DTS == (from tp2 in TP_DEFN
where tp2.FAMILY_ID == tp1.FAMILY_ID &&
tp2.TP_ID == tp1.TP_ID
select tp2.TP_DEFN_REV_DTS).Max()
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
Off the top of my head, and not knowing which LINQ provider you're using...
var q = from tp1 in Context.TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y"
&& tp1.FAMILY_ID == 1
&& tp1.TP_DEFN_REV_DTS
== Context.TP_DEFN.Where(tp2 => tp2.FAMILY_ID == tp1.FAMILY_ID
&& tp2.TP_ID == tp1.TP_ID)
.Max(tp2 => tp2.TP_DEFN_REV_DTS)
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
If you're using entity framework or linq-to-sql, you can just pass the direct sql if you want (although that'll prevent change tracking, at least by default).
For EF, use ObjectContext.ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx
For L2S, use DataContext.ExecuteQuery: http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx

Categories