Context: The webapplication is some sort of Reddit with Tags.
I have the following include initialisation which returns a IQueryable:
IQueryable<Item> items = itemRepository
.Include( //.Actions
el => el.Votes,
el => el.Comments,
el => el.CreatedBy.Roles,
el => el.TagItems.Select(dl => dl.Tag.Actions))
.Where(el => el.SiteId == siteId && el.Status >= 0)
.OrderBy(orderBy).AsNoTracking();
The IQueryable is later used with ExpressionBuilder (LinqKit), because there are a lot of AND / OR queries.
When i use the personal mode on my application ( = show only results that a user upvoted/created or results from the tags that the user is following), the query becomes really slow:
if (filterBy.PersonalMode)
{
// || el.TagItems.Any(dl => dl.Tag.UserId == filterBy.UserId)
itemPredicate = itemPredicate.And(el =>
el.Votes.Select(dl => new { UserId = dl.UserId}).Any(dl =>
dl.UserId == filterBy.UserId)
|| el.CreatedById == filterBy.UserId
|| (filterBy.RoleId.HasValue && el.TagItems.Select(dl => new { UserRoleId = dl.Tag.UserRoleId, PersonalTags = dl.Tag.PersonalTags }).Any(dl => dl.UserRoleId.HasValue && filterBy.RoleId.HasValue && dl.UserRoleId.Value == filterBy.RoleId.Value))
|| el.TagItems.Any(dl => dl.Tag.PersonalTags.Select(cl => new { UserId = cl.UserId }).Any(cl => cl.UserId == filterBy.UserId)));
}
I'm trying to stay away from alternatives from EF, because it keeps my code pretty clean to read.
The query itselve, mentions a 61% execution time.
Execution plan XML:
https://www.brentozar.com/pastetheplan/?id=ry9najEMr
Slow part:
Output list
[handlr].[dbo].[Tags].Id; [handlr].[dbo].[Tags].Name; [handlr].[dbo].[Tags].SiteId; [handlr].[dbo].[Tags].Active; [handlr].[dbo].[Tags].CreatedOn; [handlr].[dbo].[Tags].FillsInParentTag; [handlr].[dbo].[Tags].ShowOnOverview; [handlr].[dbo].[Tags].ParentTagId; [handlr].[dbo].[Tags].LeftBower; [handlr].[dbo].[Tags].RightBower; [handlr].[dbo].[Tags].Depth; [handlr].[dbo].[Tags].Caption; [handlr].[dbo].[Tags].isRestricted; [handlr].[dbo].[Tags].Description; [handlr].[dbo].[Tags].ShowTag; [handlr].[dbo].[Tags].Count; [handlr].[dbo].[Tags].UserId; [handlr].[dbo].[Tags].UserRoleId; [handlr].[dbo].[Tags].SynonymOfTagId
Visual:
Any advice on how to improve this query? Adding non-clustured indexes don't seem to do much currently.
Related
I want to get all layergroups and include (eager loading) layers that have a specific url.
Here are my tables:
- LayerGroup (id, List<Layer>layers and more...)
- Layer(id, url, List<Attribute>attributes and more...)
- Attribute (id and more...)
This is what i got so far.
var groups = _context.LayerGroups
.Where(group => group.IsActive)
.Where(g => g.Layers.All(l => l.IsActive == true))
.Where(g => g.Layers.All(l => l.Url == "example1"))
.Include(g => g.Layers)
.ThenInclude(layer => layer.Attributes)
.Include(group => group.Layers).ThenInclude(layer => layer.SearchEngines)
.ToList();
But it seems to only get groups if exactly all layers is active and have the url example1. I want to get the layers with the right Url, even if the groups have layers with other url:s.
According to this you cannot filter Include and ThenInclude collection but you can filter parent with the condition based on Include. If you need so then you can write your query as follows:
var groups = _context.LayerGroups.Where(group => group.IsActive)
.Include(g => g.Layers)
.ThenInclude(layer => layer.Attributes)
.Include(group => group.Layers)
.ThenInclude(layer => layer.SearchEngines)
.Where(g => g.Layers.Any(l => l.IsActive == true && l => l.Url == "example1"))
.ToList();
I am facing this linq query for entity framework core (2.0).
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.Select
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new
{
singleObject = t2e.Table3NavPropObject.TargetObject,
enumerable1 = t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject),
enumerable2 = t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject),
enumerable3 = t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject))
}
)
)
.ToListAsync();
The goal is to query all the referenced instances of TargetObject which is referenced accros ~ 10 different tables.
Currently it returns an IEnumerable where the anonymous object contains the properties singleObject, enumerable1, enumerable2, enumerable3. All those properties are of type TargetObject or IEnumerable.
Can I, and how, rewrite the query to not return anonymous objects but just an IEnumerable containing all the values?
For some reason the compiler won't let me iterate over the anonymous collection and flatten it manually.
This should do the trick if you want one array per t1e row.
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.Select
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new[] {t2e.Table3NavPropObject.TargetObject}.Concat(
t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject)))
)
)
.ToListAsync();
If you want it completely flat, you'll want to switch that Select to a SelectMany().
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.SelectMany
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new[] {t2e.Table3NavPropObject.TargetObject}.Concat(
t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject)))
)
)
.ToListAsync();
Is Add and/or Concat a possible solution?
PoC:
var a = new [] { 1,2,3 };
var result = new {
firstA = a.First(),
otherAs = a,
backwards = a.Reverse()
};
var final = new List<int>();
final.Add(result.firstA);
final.Concat(result.otherAs.Concat(result.backwards))
.Dump();
_context.Homestays
.Include(x => x.CreatedUser)
.Include(x => x.UpdatedUser)
.Include(x => x.HomestayEvaluations)
.Include(x => x.HomestayContracts)
.Include(x => x.HomestayPoliceChecks)
.Include(x => x.HomestayHouseHolds)
.AsNoTracking()
.Select(x => new Homestay()
{
HomestayId = x.HomestayId,
HomestayFamily = ConstValue.GetHomestayFamilyName(x),
Address = x.Address,
Score = x.HomestayEvaluations.Any(x1 => x1.IsEvaluationActive) ? x.HomestayEvaluations.LastOrDefault(x1 => x1.IsEvaluationActive).GetScore() : 0,
Contract = x.HomestayContracts.Any(x1 => x1.IsContractActive) ? x.HomestayContracts.LastOrDefault(x1 => x1.IsContractActive).ContractDate : null,
Students = x.Students,
HouseHolders = x.HomestayHouseHolds.Count(x1 => x1.IsHouseHoldActive),
PoliceCheck = x.HomestayPoliceChecks.Any(x1 => x1.IsPoliceCheckActive) ? x.HomestayPoliceChecks.LastOrDefault(x1 => x1.IsPoliceCheckActive).PoliceCheckDate : null,
Language = x.Language,
Room = x.Room,
IsActive = x.IsActive,
CreatedDate = x.CreatedDate,
CreatedUserName = ConstValue.GetUserName(x.CreatedUser),
UpdatedDate = x.UpdatedDate,
UpdatedUserName = ConstValue.GetUserName(x.UpdatedUser)
})
.OrderByDescending(x => x.HomestayId);
Hello, Im wonder that how do I change my select query better ?
This code as below that it looks messy.
first excute any and if it is true, get last one. but is there any more short code ??
Contract = x.HomestayContracts.Any(x1 => x1.IsContractActive) ? x.HomestayContracts.LastOrDefault(x1 => x1.IsContractActive).ContractDate : null
I tried without any(), it occred error as null object if there is no data.
Please help me.
Thanks :)
Use this
Contract = x.HomestayContracts.LastOrDefault(x1 => x1.IsContractActive)?.ContractDate ?? null
Also you can disable lazy loading for this query in context's configuration
context.Configuration.LazyLoadingEnabled = false;
This way you can eliminate Include methods, cause shorter statement but not better performance. Use stored procedure for performance. Stored Procedure in Entity Framework
I need to get the last six payments from a schema that consists of just
Contribution Id
DUE Date
Amount
The payments spread over 7 years and I need to get the most current last six payments only I thought this would do it it as it follows my sql
SELECT TOP 6 [ID]
,[customerInfo]
,[IVACODE]
,[Contribution]
,[DUE_DATE]
,[isActive]
,[isDeleted]
FROM [portal].[dbo].[tblPortalContributionSchedule]
where customerInfo='01F6B68B-6FC2-4F9D-B586-6934B8D6C979'
and DUE_DATE <='2016/09/26'
ORDER BY DUE_DATE DESC
Linq Version
List<tblPortalContributionSchedule> _contributions
= portalEntities.tblPortalContributionSchedules
.Where(a => a.customerInfo == _customerId
&& a.isDeleted == false
&& a.DUE_DATE <=Convert.ToDateTime("2016/09/26"))
.Take(6)
.OrderByDescending(o => o.DUE_DATE)
.ToList();
foreach (var contribution in _contributions)
{
AllPayments.Add(new Payments(contribution.ID, Convert.ToDecimal(contribution.Contribution), Convert.ToDateTime(contribution.DUE_DATE), false));
}
But its not producing the same results as per below
Sorry people i require the answer in linq i need to no is that the correct way of doing it i getting less than exepected
Edit 1
This is the query I have so far but I still am not getting the same results as my sql script
List<tblPortalContributionSchedule> _contributions =
portalEntities.tblPortalContributionSchedules.Where(a => a.customerInfo ==
_customerId && a.isDeleted == false && a.DUE_DATE
<=Convert.ToDateTime("2016/09/26")).OrderByDescending(o =>
o.DUE_DATE).Take(6).ToList();
Edit 2
To show the code that I showed in the video
public List<Payments> getAllPayments(Guid _customerId)
{
List<Payments> AllPayments = new List<Payments>();
List<tblPortalPayment> _payments = portalEntities.tblPortalPayments.Where(a => a.CustomerId == _customerId && a.isDeleted == false).ToList();
foreach (var payment in _payments)
{
AllPayments.Add(new Payments(payment.id, Convert.ToDecimal(payment.paymentDue), Convert.ToDateTime(payment.paymentDate), Convert.ToBoolean(payment.isinArrears)));
}
List<tblPortalContributionSchedule> _contributions = portalEntities.tblPortalContributionSchedules.Where(a => a.customerInfo == _customerId && a.isDeleted == false && a.DUE_DATE<= Convert.ToDateTime("2016/09/26")).OrderByDescending(o => o.DUE_DATE).Take(6).ToList();
foreach (var contribution in _contributions)
{
AllPayments.Add(new Payments(contribution.ID, Convert.ToDecimal(contribution.Contribution), Convert.ToDateTime(contribution.DUE_DATE), false));
}
var result = AllPayments.OrderByDescending(o => o.paymentDate).ToList();
return AllPayments.OrderByDescending(o => o.paymentDate).ToList();
}
You can try it as shown below and let us know the result.
var dueDateFilter = new DateTime(2016, 09, 26);
List<tblPortalContributionSchedule> _contributions =
portalEntities.tblPortalContributionSchedules
.Where(a => a.customerInfo == _customerId
&& a.isDeleted == false
&& a.DUE_DATE <= dueDateFilter)
.OrderByDescending(o => o.DUE_DATE)
.Take(6)
.ToList();
Try using this query
SELECT [ID],
[customerInfo],
[IVACODE],
[Contribution],
[DUE_DATE],
[isActive],
[isDeleted]
FROM [portal].[dbo].[tblPortalContributionSchedule]
where customerInfo='01F6B68B-6FC2-4F9D-B586-6934B8D6C979'
and ROWNUM <= 6
ORDER BY DUE_DATE DESC
I have two Entities EntityTest and EntityRelated.
I want to query and get results from both Entites where Deleted is not true in both Entites.
I have tries below statement. It return EntityTest records by excluding records having Deleted = true but records of EntityRelated are not filtered.
db.EntityTest
.Include(i => i.EntityRelated)
.Where(x => x.Deleted != true && x.EntityRelated.Any(a => a.Deleted != true).ToList();
Solution 1: Use this implementation of an extension method Include2() Include2. as following:
db.EntityTest
.Include(i => i.EntityRelated.Where(e=>e.Deleted == false))
.Where(x => x.Deleted != true && x.EntityRelated.Any(a => a.Deleted != true)
.ToList();
Solution 1: