streamline linq query - c#

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);
}
}

Related

Prevent Entity Framework query from being evaluated locally

I have this query that is being partially evaluated locally and I am wondering how to prevent it.
It seems to be the conditional select that is causing the problem.
var fetchQuery = (
from udg in _entities.UberDirectGroups.AsNoTracking()
join uber in _entities.Ubers.AsNoTracking()
on udg.Id equals uber.Id
let memberCount = (
from t in _entities.UberDirects.AsNoTracking()
join u in _entities.Ubers.AsNoTracking()
on t.UberToId equals u.Id
where t.UberFromId == udg.Id && !u.Deleted
select u.UberTypeId == (byte)UberType.User ? 1 :
u.UberTypeId == (byte)UberType.Department ? u.Department.Users.Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.Office ? u.tblOffice.tblDepartment.SelectMany(d => d.Users).Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.ProjectGroup ? u.Group.GroupMembers.Select(pm => pm.User).Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.Role ? _entities.Roles.Where(r => r.RoleDataId == u.tblRoleData.Id).Select(r => r.tblUser).Where(user => user.Deleted == false).Count() : 0
).Sum()
where
udg != null &&
uber != null &&
uber.InstanceId == instanceId &&
(!isSearch || udg.Name.Contains(searchText))
select new TargetGroupProjection
{
id = udg.Id,
name = udg.Name,
created = uber.Date,
toCount = memberCount
}
);

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.

Linq to SQL : How to do WHERE [Column] = Element in LIST ?

First of all, i've seen many posts like mine but I didn't find the answer.
So, I try to find the equivalent of the "IN" clause for an linq to SQL request and i find something :
IList<int> listClientId = GetAllClientIdByRegionLPS(statutRegion);
if (!statutRegion.Equals(""))
q = q.Where(p => listClientId.Contains((int)p.ClientId));
But that makes me an error :
"'boolean contains(int32)' has no supported translation to sql"
What i want to do :
SELECT ...
FROM ...
WHERE [CONDITIONS]
AND (id == 1 OR id ==2 OR id==3 OR ...)
Let's see my code :
public IList<Appel> GetAllCourants(string Filter, int? StatutFacturation, int? statutDemande, int? statutPro, string statutRegion)
{
var q = from p in DbContext.Appels
where (p.Client.Nom.Contains(Filter) || p.AppelId.ToString().Contains(Filter) || (p.Materiel.Modele1.Nom + " #" + p.Materiel.NumeroSerie).Contains(Filter) || p.ContactClient.Nom.Contains(Filter) || p.ContactClient.Prenom.Contains(Filter) || p.Dossier.Contains(Filter) || p.Client.CodePostal.Contains(Filter) || p.Client.Ville.Contains(Filter) || p.NumDevis.Contains(Filter))
select p;
q = q.Where(p => p.StatutAppelId == 1 || p.StatutAppelId == 9 || p.StatutAppelId == 10 || p.StatutAppelId == 13 || p.StatutAppelId == 14 || p.StatutAppelId == 15 || p.StatutAppelId == 16 || p.StatutAppelId == 17 || p.StatutAppelId == 18);
if (StatutFacturation.HasValue)
q = q.Where(p => p.Facturable == StatutFacturation.Value);
if (statutDemande.HasValue)
q = q.Where(p => p.StatutDemandePiece == statutDemande.Value);
if (statutPro.HasValue)
q = q.Where(p => p.Depot == statutPro.Value);
//This makes me troubles
IList<int> listClientId = GetAllClientIdByRegionLPS(statutRegion);
if (!statutRegion.Equals(""))
q = q.Where(p => listClientId.Contains((int)p.ClientId)); /*Try to do p.ClientId = listClientId*/
var ret = q.Select(p => Deserialize<Generated.Appel>(p)).ToList();
return ret;
}
How can I fixe it ?
Thanks a lot in advance !
Try changing
IList<int> listClientId = GetAllClientIdByRegionLPS(statutRegion);
to
List<int> listClientId = GetAllClientIdByRegionLPS(statutRegion);

Linq2Sql strange behaviour

in my DB, I have e.g. 13 orders.
The code below returns all of them, if the OrderID = 0 and CustomerName = "lorem".
If I comment the line (OrderID == 0) ?.... it works fine. What's wrong ?
var result = (from x in db.Order
where
(OrderID == 0) ? x.OrderID > 0 : x.OrderID == OrderID
&&
(string.IsNullOrEmpty(CustomerName)) ?
!string.IsNullOrEmpty(CustomerName)
:
x.User.Name.Contains(CustomerName)
select x)
.ToList();
I think you can not define conditional condition inside LINQ query in this way, what you can do is, for example:
var result = (from x in db.Order where
((OrderID == 0 && x.OrderID > 0) ||
(OrderID != 0 && x.OrderID == OrderID))
&&
(string.IsNullOrEmpty(CustomerName)) ?
!string.IsNullOrEmpty(CustomerName)
:
x.User.Name.Contains(CustomerName)....

Problem with LINQ query

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
});

Categories