Some part of your SQL statement is nested too deeply - entity-framework

I have the following code
[WebGet]
public Bid GetHighestBidInOpenAuctions(int auctionEventId)
{
var auctionEvent = CurrentDataSource.AuctionEvents.Where(x => x.Id == auctionEventId).FirstOrDefault();
var auctionIds = CurrentDataSource.Auctions.Where(x => x.AuctionEventId == auctionEventId && x.Ends > DateTime.UtcNow).Select(x => x.Id).ToList();
var bids = CurrentDataSource.Bids.Where(x => auctionIds.Any(t => t == x.AuctionId));
// If the auction Event has not yet started or there are no bids then show auction with high pre-sale estimate.
if (bids.Count() == 0 || auctionEvent.Starts > DateTime.UtcNow)
{
return null;
}
var highestBid = bids.Where(b => b.IsAutobid == false).OrderByDescending(b => b.Amount).FirstOrDefault();
return highestBid;
}
This line throws the below exception
if (bids.Count() == 0 || auctionEvent.Starts > DateTime.UtcNow)
Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries.
What's wrong?
EDIT
I have tried doing this
IQueryable<Bid> bids = CurrentDataSource.Bids.Where(b => 0 == 1);
foreach(var auctionId in auctionIds)
{
int id = auctionId;
bids = bids.Union(CurrentDataSource.Bids.Where(b => b.AuctionId == id));
}
But I still get the same error.

Rather than using a subquery, try replacing the bid query with:
var bids = CurrentDataSource.Bids.Where(b => b.AuctionEventId == auctionEventId
&& b.Auction.AuctionEvent.Starts > DateTime.UtcNow
&& b.Auction.Ends > DateTime.UtcNow);
if (bids.Count() == 0
{
return null;
}

It seems when you have too many things in your database then you will get this error (auctionIds in my case) because the generated sql will be too deeply nested. To solve this I came up with this solution. If anyone can do better then do. I'm posting this because someone may have this error in the future and if they do, in the absence of a better solution this might help them.
[WebGet]
public Bid GetHighestBidInOpenAuctions(int auctionEventId)
{
/*
* This method contains a hack that was put in under tight time constraints. The query to
* get bids for all open auctions used to fail when we had a large number of open auctions.
* In this implementation we have fixed this by splitting the open auctions into groups of 20
* and running the query on those 20 auctions and then combining the results.
*/
const int auctionIdSegmentSize = 20;
var auctionEvent = CurrentDataSource.AuctionEvents.Where(x => x.Id == auctionEventId).FirstOrDefault();
var auctionIds = CurrentDataSource.Auctions.Where(x => x.AuctionEventId == auctionEventId && x.Ends > DateTime.UtcNow).Select(x => x.Id).ToList();
int numberOfSegments = auctionIds.Count/auctionIdSegmentSize;
if (auctionIds.Count % auctionIdSegmentSize != 0)
numberOfSegments++;
var bidsList = new List<IQueryable<Bid>>();
for (int i = 0; i < numberOfSegments; i++)
{
int start = i*auctionIdSegmentSize;
int end;
if (i == numberOfSegments - 1)
{
end = auctionIds.Count - 1;
}
else
{
end = ((i + 1)*auctionIdSegmentSize) - 1;
}
var subList = auctionIds.GetRange(start, (end - start) + 1);
bidsList.Add(CurrentDataSource.Bids.Where(b => subList.Any(id => id == b.AuctionId)));
}
// If the auction Event has not yet started or there are no bids then show auction with high pre-sale estimate.
if (IsBidsCountZero(bidsList) || auctionEvent.Starts > DateTime.UtcNow)
{
return null;
}
var highestBid = FindHighestBid(bidsList);
return highestBid;
}
private Bid FindHighestBid(List<IQueryable<Bid>> bidsList)
{
var bids = new List<Bid>();
foreach (var list in bidsList)
{
bids.Add(list.Where(b => b.IsAutobid == false).OrderByDescending(b => b.Amount).FirstOrDefault());
}
bids.RemoveAll(b => b == null);
if (bids.Count == 0)
return null;
bids.Sort(BidComparison);
return bids[0];
}
private int BidComparison(Bid bid1, Bid bid2)
{
if (bid1.Amount < bid2.Amount)
return 1;
if (bid1.Amount > bid2.Amount)
return -1;
return 0;
}
private bool IsBidsCountZero(List<IQueryable<Bid>> bidsList)
{
int count = 0;
foreach (var list in bidsList)
{
count += list.Count();
}
return count == 0;
}

The problem is with auctionIds.Any(t => t == x.AuctionId) where EF cannot create a correct query. You can change it to:
var bids = CurrentDataSource.Bids.Where(x => auctionIds.Contains(x.AuctionId));
Where EF can convert auctionIds to a collection and pass to DB.

Related

Dynamic Search Using Entity Framework

I have a search screen with optional fields using Entity Framework, I want to build a dynamic query without selecting the object and filter on it.
I want to Optimize the following Existing Search, I don't want to select "var query = from p in context.APLCN_TRCKR select p;" at this stage because the application will be used by more than 100 people at once:
using (var context = new VASTEntities())
{
var query = from p in context.APLCN_TRCKR select p;
if (!string.IsNullOrEmpty(searchObj.CUST_NAME_X))
query = query.Where(p => p.CUST_NAME_X == searchObj.CUST_NAME_X.Trim());
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
query = query.Where(p => p.CUST_SURNM_X == searchObj.SURNM_X.Trim());
if (!string.IsNullOrEmpty(searchObj.QUEUE_ID))
query = query.Where(p => p.QUEUE_ID == searchObj.QUEUE_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.BP_ID))
query = query.Where(p => p.BPID == searchObj.BP_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.UserID))
query = query.Where(p => p.CURR_OWNR_USER_ID == searchObj.UserID.Trim());
if (!string.IsNullOrEmpty(searchObj.APLCN_TRCKR_ID))
query = query.Where(p => p.APLCN_TRCKR_ID == searchObj.APLCN_TRCKR_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.APLCN_STTS_ID))
query = query.Where(p => p.APLCN_STTS_ID == searchObj.APLCN_STTS_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CUST_ID))
query = query.Where(p => p.CUST_ID == searchObj.CUST_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CELL_ID))
query = query.Where(p => p.CELL_ID == searchObj.CELL_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.ORIGN_ID))
query = query.Where(p => p.ORIGN_ID == searchObj.ORIGN_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.ORGTN_CHANL_ID))
query = query.Where(p => p.ORGTN_CHANL_ID == searchObj.ORGTN_CHANL_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CR_DCSN_ID))
query = query.Where(p => p.CR_DCSN_ID == searchObj.CR_DCSN_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.SBSA_CUST_I))
query = query.Where(p => p.SBSA_CUST_I == searchObj.SBSA_CUST_I.Trim());
if (!string.IsNullOrEmpty(searchObj.USER_ID_APP_CRTD))
query = query.Where(p => p.USER_ID_APP_CRTD == searchObj.USER_ID_APP_CRTD.Trim());
if (!string.IsNullOrEmpty(searchObj.RGION_ID))
{
int r = int.Parse(searchObj.RGION_ID.Trim());
query = query.Where(p => p.RGION_ID == r);
}
if (!string.IsNullOrEmpty(searchObj.CR_REGION))
{
int x = int.Parse(searchObj.CR_REGION);
if (x == 0)
{
// check 0 - not applicable or null
query = query.Where(p => p.CR_REGION_ID == 0 || p.CR_REGION_ID == null);
}
else
{
query = query.Where(p => p.CR_REGION_ID == x);
}
}
if (!string.IsNullOrEmpty(searchObj.Process_Type))
query = query.Where(p => p.PRCES_TYPE_ID == searchObj.Process_Type.Trim());
query.ToList();
foreach (var a in query)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
if (query.Count() == 0)
{
throw new Exception("No Applications Found.");
}
context.Connection.Close();
return searchAppsObj;
}
I want to do something like this but this one is not working properly:
string cust_name_x = "", surname_x = "", queue_id = "", bp_id = "", user_id = "", aplcn_trckr_id = "",
aplcn_stts_id = "", cust_id = "", process_type = "", cr_region = "", cell_id = "", Origin = "", region = "", channel = "", credit_verdict = "", sbsa_cust_id = "", app_creator_id = "";
if (!string.IsNullOrEmpty(searchObj.CUST_NAME_X))
cust_name_x = searchObj.CUST_NAME_X.Trim();
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
surname_x = searchObj.SURNM_X.Trim();
if (!string.IsNullOrEmpty(searchObj.QUEUE_ID))
queue_id = searchObj.QUEUE_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.BP_ID))
bp_id = searchObj.BP_ID;
if (!string.IsNullOrEmpty(searchObj.UserID))
user_id = searchObj.UserID.Trim();
if (!string.IsNullOrEmpty(searchObj.APLCN_TRCKR_ID))
aplcn_trckr_id = searchObj.APLCN_TRCKR_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.APLCN_STTS_ID))
aplcn_stts_id = searchObj.APLCN_STTS_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.CUST_ID))
cust_id = searchObj.CUST_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.Process_Type))
process_type = searchObj.Process_Type.Trim();
if (!string.IsNullOrEmpty(searchObj.CR_REGION))
cr_region = searchObj.CR_REGION.Trim();
if (!string.IsNullOrEmpty(searchObj.CELL_ID))
cell_id = searchObj.CELL_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.ORIGN_ID))
Origin = searchObj.ORIGN_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.RGION_ID))
region = searchObj.RGION_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.ORGTN_CHANL_ID))
channel = searchObj.ORGTN_CHANL_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.CR_DCSN_ID))
credit_verdict = searchObj.CR_DCSN_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.SBSA_CUST_I))
sbsa_cust_id = searchObj.SBSA_CUST_I.Trim();
if (!string.IsNullOrEmpty(searchObj.USER_ID_APP_CRTD))
app_creator_id = searchObj.USER_ID_APP_CRTD.Trim();
using (var context = new VASTEntities())
{
var query = from p in context.APLCN_TRCKR
where
p.CUST_NAME_X.Contains(cust_name_x) &&
p.CUST_SURNM_X.Contains(surname_x) &&
p.QUEUE_ID.Contains(queue_id) &&
p.BPID.Contains(bp_id) &&
p.CURR_OWNR_USER_ID.Contains(user_id) &&
p.APLCN_TRCKR_ID.Contains(aplcn_trckr_id) &&
p.APLCN_STTS_ID.Contains(aplcn_stts_id) &&
p.CUST_ID.Contains(cust_id) &&
p.PRCES_TYPE_ID.Contains(process_type) &&
p.CELL_ID.Contains(cell_id) &&
SqlFunctions.StringConvert((double)p.CR_REGION_ID).Contains(cr_region) &&
p.ORIGN_ID.Contains(Origin) &&
SqlFunctions.StringConvert((double)p.RGION_ID).Contains(region) &&
p.ORGTN_CHANL_ID.Contains(channel) &&
p.CR_DCSN_ID.Contains(credit_verdict) &&
p.SBSA_CUST_I.Contains(sbsa_cust_id)
select p;
query.ToList();
if (query.Count() == 0)
{
throw new Exception("No Applications Found.");
}
foreach (var a in query)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
context.Connection.Close();
return searchAppsObj;
}
You can just create a collection of lambda expression like below:
var filters = new List<Expression<Func<Application, bool>>>();
if (!string.IsNullOrWhitespace(searchObj.CUST_NAME_X))
filters.Add(application => application .CUST_NAME_X.Contains(searchObj.CUST_NAME_X.Trim());
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
filters.Add(application => application .CUST_SURNM_X.Contains(searchObj.SURNM_X.Trim());
// And so on for all criteria
After that you can do a loop on filters like below:
using (var context = new VASTEntities())
{
var query = context.APLCN_TRCKR;
foreach(var filter in filters)
{
query = query.Where(filter);
}
var result = query.ToList();
if (result.Count() == 0)
{
throw new Exception("No Applications Found.");
}
foreach (var a in result)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
context.Connection.Close();
return searchAppsObj;
}
change
var query = from p in context.APLCN_TRCKR select p;
to
var query = context.APLCN_TRCKR.AsQueryable();
And when the filtering work is done:
await query.ToListAsync() // this one goes to the database
Also have a look at this: Linq query filtering
In Addition: don't call context.Connection.Close(); as this is going to be executed anyway because using behaves like try { ... } finally { // dispose work }

Google Spreadsheet - How to avoid sending email duplicates?

I am having an issue with a script. I used the following script from Google Developers Website in order to do a simple merge mail. See https://developers.google.com/apps-script/articles/mail_merge
I modified a bit the script so to prevent email duplicates. However, even if the script seems to work as it marks 'EMAIL_SENT' in each row every time an email is sent. It does not pay attention if the mail as already been marked and still send the mail.
I believe there is an error at line 16 "var emailSent = rowData[6];"
I would really appreciate if someone could help me. Whoever you are thanks in advance.
Here is the modified script :
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheets()[0];
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 7);
var templateSheet = ss.getSheets()[1];
var emailTemplate = templateSheet.getRange("A2").getValue();
var objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var Resume = DriveApp.getFilesByName('Resume.pdf') var Portfolio = DriveApp.getFilesByName('Portfolio.pdf') var rowData = objects[i];
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "Architectural Internship";
var emailSent = rowData[6];
if (emailSent != EMAIL_SENT) {
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {
attachments: [Resume.next(), Portfolio.next()]
});
dataSheet.getRange(2 + i, 7).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
function fillInTemplateFromObject(template, data) {
var email = template;
var templateVars = template.match(/\${\"[^\"]+\"}/g);
for (var i = 0; i < templateVars.length; ++i) {
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
}
return email;
}
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue;
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty. // Arguments: // - cellData: string function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise. function isAlnum(char) { return char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || isDigit(char); }
// Returns true if the character char is a digit, false otherwise. function isDigit(char) { return char >= '0' && char <= '9'; }
Your code is really hard to read and the functions that return 2 or more objects make it even harder...you are using variable names that are also a bit confusing.... but that is probably a personal pov :-)
Anyway, I think I've found the issue: when you write var rowData = objects[i];
This "object" is actually the result of the getRowData function but if you look at this function, you'll see that it returns 2 objects, the first one being itself the result of another function (getObjects) ...
You are checking the value is the 6th element of the array which is actually an object and compare it to a string. The equality will never be true.
I didn't go further in the analyse since I found it really confusing ( as I already said) but at least you have a first element to check .
I would suggest you rewrite this code in a more simple way and use more appropriate variable names to help you while debugging.
I would recommend logging both values before executing to make sure they are the same. I would also guess that the email_sent and EMAIL_SENT are different data types. Can also try forcing the value to string for comparison.
To clarify:
logger.Log(emailSent);
logger.Log(EMAIL_SENT);
if (emailSent.toString() != EMAIL_SENT.toString())
{...
Error is in this line of code -
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
It's considering only 2 columns in the range. Changed 2 to 3 and it worked fine.

Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to

Basically I retrieved the records from the table and wanted to updated one column.
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.Select(c => new { c.FilePath, c.FileName })
.ToList();
So this query has only two fields: FilePath and FileName, then next I want to assign FilePath = null;
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();
Then I got an error:
Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to -- it is read only
You select from query anonymous class. You cannot set properties of anonymous class. To do what you want you should get whole entity:
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.ToList();
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();

Entity Framework, building query based on criteria

I was wondering if anyone had a better idea how to do this. atm returning IQueryable<Member> as ObjectQuery<Member> seems dirty to me.
namespace Falcon.Business.Repositories
{
using System;
using System.Data.Objects;
using System.Linq;
using Falcon.Business.Criteria;
using Falcon.Business.Entities;
using Falcon.Business.Enums;
using Falcon.Business.Extensions;
using Falcon.Business.Repositories.Interfaces;
using Falcon.Business.Services;
using Falcon.Business.Services.Interfaces;
using Falcon.Core.Extensions;
public class MemberRepository : LinqRepository<Member>, IMemberRepository
{
public Member Fetch(MemberCriteria criteria)
{
ObjectQuery<Member> query = base.CreateQuery();
query = this.AddRelations(query);
query = this.AddCriteria(query, criteria);
query = this.AddCriteriaOrder(query, criteria);
return query.FirstOrDefault();
}
public IPagerService<Member> FetchAll(MemberCriteria criteria)
{
int page = (criteria.Page.HasValue) ? criteria.Page.Value : 1;
int limit = criteria.Limit;
int start = (page * limit) - limit;
int total = this.Count(criteria);
ObjectQuery<Member> query = base.CreateQuery();
query = this.AddRelations(query);
query = this.AddCriteria(query, criteria);
query = this.AddCriteriaOrder(query, criteria);
return new PagerService<Member>(query.Skip(start).Take(limit).ToList(), page, limit, total);
}
public int Count(MemberCriteria criteria)
{
ObjectQuery<Member> query = base.CreateQuery();
query = this.AddCriteria(query, criteria);
return query.Count();
}
public ObjectQuery<Member> AddCriteria(IQueryable<Member> query, MemberCriteria criteria)
{
if (criteria.Title.HasValue())
{
query = query.Where(q => q.Title == criteria.Title);
}
if (criteria.TitleUrl.HasValue())
{
query = query.Where(q => q.TitleUrl == criteria.TitleUrl);
}
if (criteria.EmailAddress.HasValue())
{
query = query.Where(q => q.EmailAddress == criteria.EmailAddress);
}
if (criteria.HostAddress.HasValue())
{
query = query.Where(q => q.HostAddress == criteria.HostAddress);
}
query = query.Where(q => q.Status == criteria.Status);
return query as ObjectQuery<Member>;
}
public ObjectQuery<Member> AddCriteriaOrder(IQueryable<Member> query, MemberCriteria criteria)
{
if (criteria.Sort == SortMember.ID)
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.ID)
: query.OrderByDescending(q => q.ID);
}
else if (criteria.Sort == SortMember.Posts)
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.Posts)
: query.OrderByDescending(q => q.Posts);
}
else if (criteria.Sort == SortMember.Title)
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.Title)
: query.OrderByDescending(q => q.Title);
}
else if (criteria.Sort == SortMember.LastLogin)
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.LastLogin)
: query.OrderByDescending(q => q.LastLogin);
}
else if (criteria.Sort == SortMember.LastVisit)
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.LastVisit)
: query.OrderByDescending(q => q.LastVisit);
}
else
{
query = criteria.Order == SortOrder.Asc
? query.OrderBy(q => q.Created)
: query.OrderByDescending(q => q.Created);
}
return query as ObjectQuery<Member>;
}
private ObjectQuery<Member> AddRelations(ObjectQuery<Member> query)
{
query = query.Include(x => x.Country);
query = query.Include(x => x.TimeZone);
query = query.Include(x => x.Profile);
return query;
}
}
}
I also do not like returning an objectquery, because doing so will make you very dependent on Entity Framwork. Knowing Microsoft they propably make a lot of changes in version 2, so you do not want to do this.
NHibernate uses criteria, a bit like you suggested, but their implementation is a lot more generic. I like the more generic implementation more then you example because then you do not need to build criteria for every object. On the other hand, you implementation is typed, which is also very neat. If you want the best of both, a more generic implementation that is typed, you might want to take a look at the NHibernate implementation but instead of using strings, use lambda functions and .Net generics. I could post an example how to do this, but I'm currently not on my own machine.

conditional where in linq query

I want to return the total sum from a linq query, I pass in 2 parameters that may/may not be included in the query.
OrgId - int
reportType - int
So two questions:
How can I update the query below so that if OrgId = 0 then ignore the organisation field(Return All)?
LocumClaimTF can be True/False/Both, if both then ignore the where query for this field.
Heres what I have done so far, this is working but I'd like something for efficient.
// Using reportType set preferences for LocumClaimTF
bool locumClaimTF1, locumClaimTF2 = false;
if (reportType == 0)
{
locumClaimTF1 = false;
locumClaimTF2 = false;
}
else if (reportType == 1)
{
locumClaimTF1 = true;
locumClaimTF2 = true;
}
else // 2
{
locumClaimTF1 = true;
locumClaimTF2 = false;
}
if (OrgID != 0) // Get by OrgID
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.Where(x => x.Shift.organisationID == OrgID)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g=>g.dataCurrencyAmount);
}
else // Ignore OrgID - Get ALL Orgs
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g => g.dataCurrencyAmount);
}
I'm using EF with unit of work pattern to get data frm
A few things come to mind, from top to bottom:
For handling the Booleans
bool locumClaimTF1 = (reportType == 1 || reportType == 2);
bool locumClaimTF2 = (reportType == 1);
From what I read in the query though, if the report type is 1 or 2, you want the Shift's LocumClaimTF flag to have to be True. If that is the case, then you can forget the Boolean flags and just use the reportType in your condition.
Next, for composing the query, you can conditionally compose where clauses. This is a nice thing about the fluent Linq syntax. However, let's start temporarily with a regular DbContext rather than the UoW because that will introduce some complexities and questions that you'll need to look over. (I will cover that below)
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO
{
dataLabel = tring.Concat(g.FirstOrDefault().User.FullName),
dataCount = g.Count(),
dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value)
})
.Sum(g=>g.dataCurrencyAmount);
}
Now this here didn't make any sense. Why are you going through the trouble of grouping, counting, and summing data, just to sum the resulting sums? I suspect you've copied an existing query that was selecting a DTO for the grouped results. If you don't need the grouped results, you just want the total. So in that case, do away with the grouping and just take the sum of all applicable records:
var total = query.Sum(x => x.shiftDateTotal.Value);
So the whole thing would look something like:
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;
}
Back to the Unit of Work: The main consideration when using this pattern is ensuring that this Get call absolutely must return back an IQueryable<TEntity>. If it returns anything else, such as IEnumerable<TEntity> then you are going to be facing significant performance problems as it will be returning materialized lists of entities loaded to memory rather than something that you can extend to build efficient queries to the database. If the Get method does not return IQueryable, or contains methods such as ToList anywhere within it followed by AsQueryable(), then have a talk with the rest of the dev team because you're literally standing on the code/EF equivalent of a land mine. If it does return IQueryable<TEntity> (IQueryable<ShiftDate> in this case) then you can substitute it back into the above query:
var query = _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;