Problem with LINQ query - c#

The following works fine:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
db.Enquiries.Where(f =>
f.WhereId == e.Id &&
f.WhatId == w.Id &&
f.EnquiryPublished != null &&
f.StatusId != EnquiryMethods.STATUS_INACTIVE &&
f.StatusId != EnquiryMethods.STATUS_REMOVED &&
f.StatusId != EnquiryMethods.STATUS_REJECTED &&
f.StatusId != EnquiryMethods.STATUS_ATTEND
).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
But:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
EnquiryMethods.BlockOnSite(db.Enquiries.Where(f => f.WhereId == e.Id && f.WhatId == w.Id)).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
+
public static IQueryable<Enquiry> BlockOnSite(IQueryable<Enquiry> linq)
{
return linq.Where(e =>
e.EnquiryPublished != null &&
e.StatusId != STATUS_INACTIVE &&
e.StatusId != STATUS_REMOVED &&
e.StatusId != STATUS_REJECTED &&
e.StatusId != STATUS_ATTEND
);
}
I get the following error:
base {System.SystemException}: {"Method 'System.Linq.IQueryable1[X.Enquiry]
BlockOnSite(System.Linq.IQueryable1[X.Enquiry])' has no supported translation to SQL."}

Linq to Sql only translates certain method calls to SQL, and yours (BlockOnSite) is not one of them. Hence the error. The fact that your method takes an IQueryable<T> and returns an IQueryable<T> doesn't make it special.

Ok I solved it using:
IQueryable<Enquiry> visibleOnSite = EnquiryMethods.VisibleOnSite(db.Enquiries);
var combinations = (from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
visibleOnSite.Where(f => f.WhereId == e.Id && f.WhatId == w.Id).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});

Related

How to avoid checking particular 'linq where clause' if that value is "any"?

I have below LINQ query:
SailingMain_Details = SailingMain_Details.Where(f => f.Duration == durationCr_Filter
&& f.DeparturePortID == depPortCr_Filter
&& f.CruiseLine == cruLineCr_Filter
&& f.ShipName == cruShipCr_Filter
&& f.DestinationCr == destinationCr_Filter).ToList();
in above query sometime i get some parameters values like value=="any". in that situation i want to avoid checking only that parameter. Can anyone please guide me how to do that. Thanks.
The below might work if you want to skip the condition if the filter is "any"
SailingMain_Details =
SailingMain_Details.Where(f => (durationCr_Filter != "any" ? f.Duration == durationCr_Filter : true)
&& (depPortCr_Filter != "any" ? f.DeparturePortID == depPortCr_Filter : true)
&& (cruShipCr_Filter != "any" ? f.ShipName == cruShipCr_Filter : true)
&& (cruLineCr_Filter != "any" ? f.CruiseLine == cruLineCr_Filter : true)
&& (destinationCr_Filter != "any" ? f.DestinationCr == destinationCr_Filter : true)).ToList();
To achieve what you want let me show you by example with DeparturePortID. Let's say "any" valu eis -1:
SailingMain_Details = SailingMain_Details
.Where(f =>
f.Duration == durationCr_Filter
&& (depPortCr_Filter == -1 || f.DeparturePortID == depPortCr_Filter)
&& f.CruiseLine == cruLineCr_Filter
&& f.ShipName == cruShipCr_Filter
&& f.DestinationCr == destinationCr_Filter)
.ToList();
Here, if depPortCr_Filter is -1, then (depPortCr_Filter == -1 || f.DeparturePortID == depPortCr_Filter) evaluates to true, independently of f.DeparturePortID == depPortCr_Filter condition.
Apply every filter on separate. example
if(durationCr_Filter.toUpper() != "ANY")
SailingMain_Details = SailingMain_Details.Where(f => f.Duration == durationCr_Filter);
if(depPortCr_Filter.toUpper() != "ANY")
SailingMain_Details = SailingMain_Details.Where(f => f.DeparturePortID == depPortCr_Filter);
if(cruLineCr_Filter.toUpper() != "ANY")
SailingMain_Details = SailingMain_Details.Where(f => f.CruiseLine == cruLineCr_Filter);
if(cruShipCr_Filter.toUpper() != "ANY")
SailingMain_Details = SailingMain_Details.Where(f => f.ShipName == cruShipCr_Filter);
if(destinationCr_Filter.toUpper() != "ANY")
SailingMain_Details = SailingMain_Details.Where(f => f.DestinationCr == destinationCr_Filter);
I assume here some parameter means it can be for any of those 5 params so you can try something like
SailingMain_Details = SailingMain_Details.Where(f => (f.Duration == durationCr_Filter || durationCr_Filter == "any")
&& (f.DeparturePortID == depPortCr_Filter || depPortCr_Filter == "any") &&
(f.CruiseLine == cruLineCr_Filter || cruLineCr_Filter == "any") && (f.ShipName == cruShipCr_Filter || cruShipCr_Filter == "any") &&
(f.DestinationCr == destinationCr_Filter || destinationCr_Filter == "any") ).ToList();
You can do it like
if (SailingMain_Details.Any(x => x.value == "any"))
{
// avoid the check here
SailingMain_Details = SailingMain_Details.Where(f => f.Duration == durationCr_Filter && f.DeparturePortID == depPortCr_Filter && f.CruiseLine == cruLineCr_Filter && f.ShipName == cruShipCr_Filter && f.DestinationCr == destinationCr_Filter).ToList();
}
else
{
SailingMain_Details = SailingMain_Details.Where(f => f.Duration == durationCr_Filter && f.DeparturePortID == depPortCr_Filter && f.CruiseLine == cruLineCr_Filter && f.ShipName == cruShipCr_Filter && f.DestinationCr == destinationCr_Filter).ToList();
}
e.g avoid checking means basically it shall return true by default. for one item see below. other can follow the same
&& (depPortCr_Filter == 'any' || f.DeparturePortID == depPortCr_Filter).

Slow LINQ Query on Join

I'm using EF 6 with .NET Core. I have written the following query to select some data into a custom object. At the moment the main query returns over 25000 records. Then the filters are applied. This query is running very slow. I'm just trying to find out if I'm doing any mistakes here. Why it could be so slow and is the filtering happening on the memory rather than on the database side?
public IQueryable<TicketViewModel> GetTickets(int companyId, string locale, TicketFilterViewModel filters, Guid customerRef, int? take, int currentUser)
{
IQueryable<TicketViewModel> tickets = (from to in _context.Ticket
join co in _context.Company on to.CompanyId equals co.CompanyId
join con in _context.Contact on to.ContactId equals con.ContactId
join site in _context.Site on to.SiteId equals site.SiteId
join country in _context.Country on site.CountryId equals country.CountryId
join cus in _context.Customer on con.CustomerId equals cus.CustomerId
join tic_type in _context.TicketType on to.TicketTypeId equals tic_type.TicketTypeId
join ts in _context.TicketStatus on to.TicketStatusId equals ts.TicketStatusId
join sb in _context.ServiceBoard on to.ServiceBoardId equals sb.ServiceBoardId into ob1
from a in ob1.DefaultIfEmpty()
join u in _context.User on to.TechnitianId equals u.Id into ob2
from b in ob2.DefaultIfEmpty()
join pr in _context.Priority on to.PriorityId equals pr.PriorityId into ob3
from c in ob3.DefaultIfEmpty()
where to.CompanyId == companyId && (customerRef == Guid.Empty || cus.RefNo == customerRef)
&& to.MergedIntoTicketId == null
select new TicketViewModel
{
CreatedOn = Helpers.Custom.UtcToStandardTime(locale, to.AddedOnUtc).ToString("dd/MM/yyyy hh:mm tt"),
CustomerName = cus.CustomerName,
TicketNumber = to.TicketNumber,
TicketTitle = to.TicketTitle,
RefNo = to.RefNo,
CompanyName = co.CompanyName,
ContactName = string.Concat(con.FirstName, " ", con.LastName),
SiteAddress = String.Concat(site.AddressLine1, ", ", site.AddressLine2, ", ", site.Suburb, ", ", site.State, ", ", site.PostCode, ", ", country.Name),
ServiceBoardName = a.BoardName,
TechnicianName = string.Concat(b.FirstName, " ", b.LastName),
PriorityName = c.PriorityName,
TicketStatus = ts.StatusName, //d.StatusName,
ServiceBoardId = to.ServiceBoardId,
TicketStatusId = to.TicketStatusId,
CustomerId = cus.CustomerId,
PriorityId = to.PriorityId,
ContractId = site.ContractId,
TechnitianId = to.TechnitianId,
TicketId = to.TicketId,
StatusCategoryId = ts.CategoryId,//,//d.CategoryId,
DueOnUtc = to.DueOnUtc,
DefaultStatusId = ts.DefaultId,//d.DefaultId,
ClosedOnUtc = to.ClosedOnUtc,
ResolvedOnUtc = to.ResolvedOnUtc,
InitialResponseMade = to.InitialResponseMade,
TicketTypeId = to.TicketTypeId,
TicketTypeName = tic_type.TicketTypeName
}).OrderByDescending(o => o.TicketNumber);
bool isFiltersHit = false;
if (filters != null)
{
if (tickets != null && tickets.Count() > 0 && filters.serviceboard_selectedItems != null && filters.serviceboard_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.ServiceBoardId != null && filters.serviceboard_selectedItems.Select(o => o.serviceBoardId).Contains(x.ServiceBoardId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.status_selectedItems != null && filters.status_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.TicketStatusId != null && filters.status_selectedItems.Select(o => o.ticketStatusId).Contains(x.TicketStatusId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.type_selectedItems != null && filters.type_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => filters.type_selectedItems.Select(o => o.ticketTypeId).Contains(x.TicketTypeId));
}
if (tickets != null && tickets.Count() > 0 && filters.technician_selectedItems != null && filters.technician_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.TechnitianId != null && filters.technician_selectedItems.Select(o => o.id).Contains(x.TechnitianId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.customer_selectedItems != null && filters.customer_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.CustomerId != 0 && filters.customer_selectedItems.Select(o => o.customerId).Contains(x.CustomerId));
}
if (tickets != null && tickets.Count() > 0 && filters.priority_selectedItems != null && filters.priority_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.PriorityId != null && filters.priority_selectedItems.Select(o => o.priorityId).Contains(x.PriorityId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.contract_selectedItems != null && filters.contract_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.ContractId != null && filters.contract_selectedItems.Select(o => o.contractId).Contains(x.ContractId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.source_selectedItems != null && filters.source_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.SourceId != 0 && filters.source_selectedItems.Select(o => o.item_id).Contains(x.SourceId));
}
}
if (take.HasValue)
return tickets.Take(take.Value);
return tickets;
}
If you compose the query based on your conditions from the filters, you will see a big improvement. Meaning, create a base query ( base and minimum conditions ) and then add the conditions one by one, depending on what your filters are. Execute the query after all filters are checked.
P.S: you don't really need to call tickets.Count() on every filter. Do it once, in the beginning.

Filtering an Iqueryable object by a group of rules

I've been filtetring an Iqueryable object by some rules, using successive Where clauses to do && filtering, how can i do the same for || filtering?
Anyone has a suggestion?
Using another words, i want to apply for all rules || filtering.
I'm going to show a portion of the code to exemplify what im asking.
Where i get the pages object:
public IQueryable<gbd_Pages> getAllPagesByOrderAndDir(int template_id, string name = "", int sortOrder = 3, string sortDirection = "asc")
{
IQueryable<gbd_Pages> Listpages;
if (sortDirection == "asc")
{
Listpages = from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false && c.Template_Id == template_id
&& !string.IsNullOrEmpty(name) ? c.Content.ToLower().Contains(name.ToLower()) : true
&& c.gbd_Template_Fields.SortOrder == sortOrder
select c).Take(1)
orderby pc.Content ascending
where p.IsActive == true && p.IsDeleted == false
select p;
}
else
{
Listpages = from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false && c.Template_Id == template_id
&& !string.IsNullOrEmpty(name) ? c.Content.ToLower().Contains(name.ToLower()) : true
&& c.gbd_Template_Fields.SortOrder == sortOrder
select c).Take(1)
orderby pc.Content descending
where p.IsActive == true && p.IsDeleted == false
select p;
}
return Listpages;
}
The function where i do the filtering. Actually it's only doing && filtering for all rules:
public IEnumerable<gbd_Pages> PagesFiltered(List<Rule> newRules, string search, int sortColumnId, string sortColumnDir, out int count)
{
GBD.FrontOffice.Controllers.SegmentsController segmentCtrl = new GBD.FrontOffice.Controllers.SegmentsController();
var Pages = controller.getAllPagesByOrderAndDir(1, search, sortColumnId, sortColumnDir);
if (newRules.Count > 0)//FILTRA A LISTAGEM DE ACORDO COM AS REGRAS
{
foreach (var rule in newRules)
{
if (rule.Value!=null)
{
gbd_Template_Fields templateField = controller.getTemplateFieldById(1, rule.Template_Field_Id);
gbd_Segment_Operators segOperator = segmentCtrl.getOperatorById(rule.Operator_Id);
if (segOperator.Value == "==")//IGUALDADE
if (rule.Value.Trim() != "")
{
if (templateField != null && templateField.Field_Id == 3) //tipo data
{
try
{
DateTime test_datetime = DateTime.Parse(rule.Value); // testa se a data introduzida é valida
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && c.Content.Trim() == rule.Value.Trim())));
}
catch (Exception ex)
{
}
}
else if (templateField != null && templateField.Field_Id == 12 && !string.IsNullOrEmpty(rule.Value))//Lista múltipla de valores
{
rule.Value = rule.Value.Replace(',', '|');
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && c.Content == rule.Value)));
}
else
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim() == rule.Value.ToLower().Trim())));
}
else
{
Pages = Pages.Where(p => !p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id) || p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content == null || c.Content.ToLower().Trim() == rule.Value.ToLower().Trim())));
}
else if (segOperator.Value == ">")//MAIOR QUE
{
if (templateField != null && templateField.Field_Id == 3) //tipo data
{
try
{
DateTime test_datetime = DateTime.Parse(rule.Value); // testa se a data introduzida é valida
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && (c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) > 0 || c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) == 0 && c.Content.Substring(3, 2).CompareTo(rule.Value.Trim().Substring(3, 2)) > 0 || c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) == 0 && c.Content.Substring(3, 2).CompareTo(rule.Value.Trim().Substring(3, 2)) == 0 && c.Content.Substring(0, 2).CompareTo(rule.Value.Trim().Substring(0, 2)) > 0))));
}
catch (Exception ex)
{
}
}
else if (templateField != null && templateField.Field_Id == 5)//tipo número
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content != "" && (c.Content.Trim().CompareTo(rule.Value.Trim()) > 0 || (rule.Value.Trim().StartsWith("-") && c.Content.Trim().CompareTo(rule.Value.Trim()) > 0 || (c.Content.Trim().CompareTo("0") > 0))))));//procurar outra solução https://stackoverflow.com/questions/7740693/big-issue-in-converting-string-to-datetime-using-linq-to-entities
else if (templateField != null && templateField.Field_Id == 6) //tipo telefone //ESTA CONDIÇÃO NÃO FOI ADICIONADA AINDA A BD
{
try
{
Pages = Pages.AsEnumerable().Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content != "" && (Convert.ToInt32(c.Content.Split(' ')[1]) > Convert.ToInt32(rule.Value))))).AsQueryable();//TODO: procurar outra solução
}
catch (Exception ex) { }
}
}
many more rules...
else if (segOperator.Value == "StartsWith")//COMEÇA COM
{
if (rule.Value.Trim() != "")
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim().StartsWith(rule.Value.ToLower().Trim()))));
}
else if (segOperator.Value == "EndsWith")//ACABA COM
{
if (rule.Value.Trim() != "")
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim().EndsWith(rule.Value.ToLower().Trim()))));
}
}
}
}
count = Pages.Select(d => d.Id).Count();
return Pages;
}
The Where statement is implicitly using AND in all cases. To get an OR statement equivalent, you'll need to use the double pipe operator (||) inside of a Where statement with something like:
.Where(p => p.A == 'b' || p.C == 'd')
Unfortunately there is no simple way to add successive optional statements the .NET framework. It would be great to have something like .Where(p => p.A == 'b').Or(p => p.C == 'd') but generating a query from this seems impractical - if there was an another Where statement, then how would Linq know how to group it relative to the other two? (A || B && C) versus ((A || B) && C).
That said, there is an external library (LINQKit) that can help fill the gap. See Dynamic where clause (OR) in Linq to Entities for reference.
You can work around the limitations of linq by just adding all the results into a List<>
List<gbd_Pages> allPages = new List<gdb_Pages>();
foreach (var rule in newRules)
{
IEnumerable<gbd_Pages> rulePages;
// current logic (except you need to not overwrite the Pages variable)
// instead of the pattern "Pages = Pages.Where( ..." use "rulePages = Pages.Where( ..."
allPages.AddRange(rulePages);
}
return allPages;

Issue with LINQ IN clause when filter value is empty but works with filter values

I have a filter called serviceEntryFilter with a property System which could have values for instance EP1, EP2 OR EP1 and sometimes this filter would be null. If there are multiple values or a single value then the query (IN) clause runs fine . If the filter value is null then I get the following error:
Unable to create a constant value of type 'System.String[]'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
[HttpPost]
public ActionResult List(string ServiceEntryStatus, string ServiceEntryReconciled, string ServiceEntryReliabilityRecord, string ActiveServiceEntry,
int PageNo, ServiceEntryFilter serviceEntryFilter = null)
{
string[] systems = null;
var list = (from se in db.ServiceEntry
join r in db.RunLogEntry on se.RunLogEntryID equals r.ID into joinRunLogEntry
from r2 in joinRunLogEntry.DefaultIfEmpty()
join u in db.User on se.TechnicianID equals u.ID
join s in db.System1 on se.SystemID equals s.ID
where (
((se.RunLogEntryID == 0 || se.RunLogEntryID != null))
&& ((serviceEntryFilter.ID.HasValue == false) || (se.ID == serviceEntryFilter.ID.Value && serviceEntryFilter.ID.HasValue == true))
&& ((serviceEntryFilter.ServiceDateTime.HasValue == false) || (EntityFunctions.TruncateTime(se.ServiceDateTime) == EntityFunctions.TruncateTime(serviceEntryFilter.ServiceDateTime) && serviceEntryFilter.ServiceDateTime.HasValue == true))
&& ((serviceEntryFilter.RunDate.HasValue == false) || (EntityFunctions.TruncateTime(r2.RunDate) == EntityFunctions.TruncateTime(serviceEntryFilter.RunDate) && serviceEntryFilter.RunDate.HasValue == true))
&& ((serviceEntryFilter.Technician == null) || (u.FullName.Contains(serviceEntryFilter.Technician.Trim()) && serviceEntryFilter.Technician != null))
&& (
((ServiceEntryStatus == "O" && se.ServiceRequestClosed == false) ||
(ServiceEntryStatus == "C" && se.ServiceRequestClosed == true) ||
(ServiceEntryStatus == "A")
)
)
&& (
((ServiceEntryReliabilityRecord == null) ||
(ServiceEntryReliabilityRecord == "N" && se.ReliabilityRecord == false) ||
(ServiceEntryReliabilityRecord == "Y" && se.ReliabilityRecord == true) ||
(ServiceEntryReliabilityRecord == "A")
)
)
&& (
((ServiceEntryReconciled == null) ||
(ServiceEntryReconciled == "N" && se.Reconciled == false) ||
(ServiceEntryReconciled == "Y" && se.Reconciled == true) ||
(ServiceEntryReconciled == "A")
)
)
&& (
((ActiveServiceEntry == null) ||
(ActiveServiceEntry == "N" && se.Active == false) ||
(ActiveServiceEntry == "Y" && se.Active == true) ||
(ActiveServiceEntry == "A")
)
)
&& (
(s.PlatformID == platformID) || (platformID == 0)
)
&& ((serviceEntryFilter.System == null) || ((serviceEntryFilter.System != null) && systems.Contains(s.SystemFullName)))
)
orderby se.ID descending
select new ServiceSearchEntry()
{
ID = se.ID,
ServiceDateTime = se.ServiceDateTime,
Technician = u.FullName,
System = s.SystemFullName,
ReasonForFailure = se.ReasonForFailure,
RunDate = (r2 == null ? (DateTime?)null : r2.RunDate)
});
var listData = list.Skip((page - 1) * PageSize).Take(PageSize);
ServiceEntriesListViewModel viewModel = new ServiceEntriesListViewModel()
{
ServiceSearchEntry = listData,
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = list.Count()
}
};
}
The Issue:
The following clause is throwing an error when SystemFilter.System is NULL. It is null at times when users do not select values for it. Sample values are as follows:
EP1, EP2
EP1
TP2, TP3, TP4
&& ((serviceEntryFilter.System == null) || ((serviceEntryFilter.System != null) && systems.Contains(s.SystemFullName)))
If it has a value, then I put it in an array and its works like a charm, its just when its null.
The issue is that everything inside a LINQ statement will get translated to SQL. There isn't really a conditional statement that I see here that says "don't try to add this array filter if it's actually null".
I would initialize the systems array to a zero length array, overwrite it if the filter.Systems is not null, and then make my linq statement as follows:
systems.Contains(s.SystemFullName)
Don't include that null checking inside the LINQ statement as it's not doing what you are expecting.
To build conditional LINQ statements, you might want to look at PredicateBuilder: http://www.albahari.com/nutshell/predicatebuilder.aspx
This is a known limitation with Linq to Entities - see section on Referencing Non-Scalar Variables Not Supported.
In otherwords, this line:
systems.Contains(s.SystemFullName)
can't be used as part of your EF query.

streamline linq query

I have tried using Left outer join using linq. It gives me the same result anytime i change my report parameters.
var _result = (from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
from ml
in SessionHandler.CurrentContext.MailingListEntries
.Where(mle => mle.SurveyCode == ls.SurveyCode).DefaultIfEmpty()
from lists
in SessionHandler.CurrentContext.MailingLists
.Where(m => m.MailingListId == ml.MailingListId).DefaultIfEmpty()
from channel
in SessionHandler.CurrentContext.Channels
.Where(ch => ch.ChannelId == lists.ChannelId).DefaultIfEmpty()
from tmChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(tcg => tcg.ChannelGroupId == channel.ChannelGroupId).DefaultIfEmpty()
from dmChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(dcg => dcg.ChannelGroupId == tmChannelGroup.ParentChannelGroupId).DefaultIfEmpty()
from amChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(acg => acg.ChannelGroupId == dmChannelGroup.ParentChannelGroupId).DefaultIfEmpty()
where (model.ChannelId != 0 && channel.ChannelId == model.ChannelId ||
model.TMId != 0 && channel.ChannelGroupId == model.TMId ||
model.DistrictId != 0 && dmChannelGroup.ChannelGroupId == model.DistrictId ||
model.AreaId != 0 && amChannelGroup.ChannelGroupId == model.AreaId ||
model.AreaId == 0 && amChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.DistrictId == 0 && dmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.TMId == 0 && tmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.ChannelId == 0 && tmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId)
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ls).ToList();
I have this LINQ query which is repeated (sort of) based on the model value. How would I be able to shorten this query..if I could just use one var object instead of using a bunch for different parameters..as you see this code is repeated.
if (model.ChannelId != 0)
{
var _result =
(from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
join ml in SessionHandler.CurrentContext.MailingListEntries on ls.SurveyCode equals ml.SurveyCode
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelId == model.ChannelId
&& ml.EmailBounce == null || ml.EmailBounce.Equals(false)
select ls).ToList();
var _SentSurveys =
(from ml in SessionHandler.CurrentContext.MailingListEntries
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelId == model.ChannelId
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ml).ToList();
model.SentSurveys = _SentSurveys.Count() > 0 ? _SentSurveys.Count() : 0;
model.CompletedSurveys = _result.Count() > 0 ? _result.Count() : 0;
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
//model.Referring = _result.Average(m => Convert.ToInt32(m.Question1Answer));
model.Referring = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer)).Average());
model.ServicePerformance = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer)).Average());
model.InstallPerformance = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer)).Average());
model.ReferringLennox = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer)).Average());
}
else if (model.TMId != 0)
{
var _result =
(from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
join ml in SessionHandler.CurrentContext.MailingListEntries on ls.SurveyCode equals ml.SurveyCode
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelGroupId == model.TMId
select ls).ToList();
var _SentSurveys =
(from ml in SessionHandler.CurrentContext.MailingListEntries
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelGroupId == model.TMId
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ml).ToList();
model.SentSurveys = _SentSurveys.Count() > 0 ? _SentSurveys.Count() : 0;
model.CompletedSurveys = _result.Count() > 0 ? _result.Count() : 0;
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
model.Referring = _result.Select(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer)).Average();
model.ServicePerformance = _result.Select(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer)).Average();
model.InstallPerformance = _result.Select(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer)).Average();
model.ReferringLennox = _result.Select(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer)).Average();
}
and there are 5 additional model parameters and for each parameters, a new var _result and _SentSurveys are created..i just want to streamline this code.
I think refactoring this first can make writing these queries easier would be beneficial. If this isn't an option, you can use some CompiledQueries to cut down on the repetition of the big queries. But doing so doesn't make it "streamlined" in terms of efficiency, just makes your code slightly cleaner. Additionally, your post-processing in both cases look nearly identical with a lot of unnecessary checks. No need to repeat the common stuff. With some heavy refactoring, you could probably do something like this:
// need to set up the compiled queries first
static readonly Func<MyDataContextType,
MyModelType,
Func<MyDataContextType, MailingListEntry, MailingList, Channel, MyModelType, bool>,
IQueryable<LennoxSurveyResponse>>
GetResult = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
Func<MyDataContextType, MailingListEntry, MailingList, Channel, MyModelType, bool> pred) =>
from lsr in ctx.LennoxSurveyResponses
join mle in ctx.MailingListEntries on lsr.SurveyCode equals mle.SurveyCode
join ml in ctx.MailingLists on mle.MailingListId equals ml.MailingListId
join ch in ctx.Channels on ml.ChannelId equals ch.ChannelId
where pred(ctx, mod, mle, ml, ch)
select lsr);
static readonly Func<MyDataContextType, MyModelType, IQueryable<MailingListEntry>>
GetSentSurveys = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod) =>
from mle in ctx.MailingListEntries
join ml in ctx.MailingLists on mle.MailingListId equals ml.MailingListId
join ch in ctx.Channels on ml.ChannelId equals ch.ChannelId
where ch.ChannelId == mod.ChannelId
&& (mle.EmailDate != null || mle.LetterDate != null || mle.EmailBounce == null)
select mle);
static readonly Func<MyDataContextType, MyModelType, MailingListEntry, MailingList, Channel, bool>
ChannelPredicate = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
MailingListEntry mle, MailingList ml, Channel ch) =>
ch.ChannelId == mod.ChannelId && ml.EmailBounce == null || !ml.EmailBounce.Value);
static readonly Func<MyDataContextType, MyModelType, MailingListEntry, MailingList, Channel, bool>
TMPredicate = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
MailingListEntry mle, MailingList ml, Channel ch) =>
ch.ChannelGroupId == mod.TMId);
static void UpdateModel(MyModelType model)
{
if (model.ChannelId == 0 && model.TMId == 0) return;
var currentContext = SessionHandler.CurrentContext;
var predicate = (model.ChannelId != 0) ? ChannelPredicate : TMPredicate;
var results = GetResults(currentContext, model, predicate).ToList();
var sentSurveys = GetSentSurveys(currentContext, model).ToList();
model.SentSurveys = sentSurveys.Count();
model.CompletedSurveys = results.Count();
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
model.Referring = _result.Average(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer));
model.ServicePerformance = _result.Average(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer));
model.InstallPerformance = _result.Average(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer));
model.ReferringLennox = _result.Average(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer));
if (model.ChannelId != 0)
{
// should be rounded
model.Referring = Math.Round(model.Referring);
model.ServicePerformance = Math.Round(model.ServicePerformance);
model.InstallPerformance = Math.Round(model.InstallPerformance);
model.ReferringLennox = Math.Round(model.ReferringLennox);
}
}

Categories