I'm trying to work out how best to filter Posts by their Fields/Regions in Piranha without loading them all and then performing the filtering.
At the moment I have the following:
var posts = _db.Posts
.Where(p => p.Published >= DateTime.Now)
.Where(x => x.title == "somestring")
.Select(p => p.Id)
.ToList();
If I wanted to query a field in regions named FooArticleDetails how would I modify the above to achieve this?
I can retrieve the results by loading all the Posts and their related Regions but this is not preferred for obvious reasons.
loadedItems.Posts
.Where(post =>
suppliedDiff.Contains(
"hard",
post.Regions.FooArticleDetails.difficulty.Value)).ToLower()
))
.ToList();
I've figured it out. Post Regions are stored as PostFields. These can be accessed via the Fields property.
posts.Where(post =>
post.Fields
.Where(x =>
x.FieldId == "difficulty" &&
x.RegionId == "FooArticleDetails" &&
suppliedDiff.Contains(x.Value.ToLower())
)
.Any());
Related
I have a scenario where I need to filter on a child list<object> ResponseIssues that is being included with the parent Question which is also a list<object>. For this example, I have 10 questions I'm pulling back from a table that I will always need to pull back whether or not there are ResponseIssues.
There appears to be a couple of problems with my query. The first problem is that the number of Questions goes from 10 to 1 since I currently only have one question associated with ResponseIssues. I need all questions to come back.
The second problem is that when I look closer at the ResponseIssues child list<object>. While I'm seeing records that are associated with the question, it's not filtering out rows by SuveryPeriod and RespondentByQuarterId. I'm expecting one row and I'm getting three rows where two of the rows where from a previous period. The same issue happens for the Responses child list.
Here's my current code below. Any ideas on how to restructure the query where it factors in the above issues and returns a Questions object and not something anonymous?
var question = await _dbContext.Questions
.Include(x => x.Responses)
.Include(x => x.ResponseIssues)
.Include(x => x.SurveySection)
.Include(x => x.Survey)
.Where(x => x.SurveyId == surveyId &&
x.Responses.Any(r => r.SiteUserId == siteUserId &&
r.SurveyPeriodId == surveyPeriodId &&
r.RespondentByQuarterId == 2
) &&
x.ResponseIssues.Any(ri => ri.SurveyPeriodId == surveyPeriodId &&
ri.RespondentByQuarterId == 2
))
.OrderBy(x => x.Position)
.ToListAsync();
I was able to do the above by breaking it out into three separate queries rather than just one. I still would be curious to know if someone in the community has a way to do it as one query though.
Anyway, here's my code below. I'm able to update the Questions Parent with the correct number of rows for both Responses and ResponseIssues along with returning all of the questions.
var question = await _dbContext.Questions
.Include(x => x.SurveySection)
.Include(x => x.Survey)
.Where(x => x.SurveyId == surveyId)
.OrderBy(x => x.Position)
.ToListAsync();
var responses = await _dbContext.Responses
.Where(x => x.SiteUserId == siteUserId &&
x.SurveyPeriodId == surveyPeriodId)
.ToListAsync();
var responseIssues = await _dbContext.ResponseIssues
.Where(x => x.SurveyPeriodId == surveyPeriodId &&
x.SiteUserId == siteUserId)
.ToListAsync();
foreach (var item in question)
{
var foundResponse = responses.Where(x => x.QuestionId == item.Id).ToList();
var foundResponseIssue = responseIssues.Where(x => x.QuestionId == item.Id).ToList();
if (foundResponse != null)
{
item.Responses = foundResponse;
}
if (foundResponseIssue != null)
{
item.ResponseIssues = foundResponseIssue;
}
}
I know that in Linq I have to do the OrderBy after doing a Select - Distinct, but I'm trying to order by an Included entity property that get lost after the Select.
For example:
var accounts = _context.AccountUser
.Include(o => o.Account)
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.OrderByDescending(o => o.LastAccessed)
.Select(o => o.Account)
.Distinct();
As I'm doing the Where by an or of two different parameters, there is a good chance to obtain duplicated results. That's why I'm using the Distinct.
The problem here is that after I do the Select, I don't have the LastAccessed property anymore because it doesn't belong to the selected entity.
I thing the structure of the AccountUser and Account can be inferred from the query itself.
If you have the bi-directional navigation properties set up:
var accountsQuery = _context.AccountUser
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.Select(o => o.Account)
.Distinct()
.OrderByDescending(a => a.AccountUser.LastAccessed);
When Selecting the Account you do not need .Include() Keep in mind that any related entities that you access off the Account will be lazy-loaded. I recommend using a .Select() to extract either a flattened view model or a view model hierarchy so that the SQL loads all needed fields rather than either eager-loading everything or tripping lazy-load calls.
Since LINQ doesn't implement DistinctBy and LINQ to SQL doesn't implement Distinct that takes an IEqualityComparer, you must substiture GroupBy+Select instead:
var accounts = _context.AccountUser
.Include(o => o.Account)
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.GroupBy(o => o.Account).Select(og => og.First())
.OrderByDescending(o => o.LastAccessed)
.Select(o => o.Account);
I have class Goal, which contains collecion of objects ProgressItems. Progress items has property Date.
I want to get only goals which has progress items with date between specified range. Goals should have included ProgressItems, but only with passed Date condition.
How should I modify my LINQ?
var goalIds = _dbContext.GoalProgressItems
.Include(p => p.Goal)
.Where(p => p.Date >= range.From && p.Date <= range.To)
.Select(p => p.Id);
var goals = _dbContext.Goals
.Include(p => p.ProgressItems)
.Where(p => goalIds.Contains(p.Id))
.ToList();
You simply need to do .Join() on the two streams. Since goalIds is filtered already, it won't have any reference to Goals that doesn't have an item within the specified range, and, thus, .Join() will filter out items in goals that don't have matching entry(-ies) in goalIds.
var goalIds = _dbContext.GoalProgressItems
.Include(p => p.Goal)
.Where(p => p.Date >= range.From && p.Date <= range.To);
var goals = _dbContext.Goals
.Include(p => p.ProgressItems)
.Join(goalIds, g => g, gpi => gpi.GoalId, (g, gpi) => g);
and so now goals should contain only those goes that have progress items that are within desired range.
How about this query?
dbContext.Goals.Include(p => p.ProgressItems)
.Where(p => p.ProgressItems.Any(pr => pr.Date >= range.From && pr.Date <= range.To))
.ToList();
Note that the using the join as mentioned by LB2 would probably generate a more efficient SQL query (provided you can change the model the perform the join).
I have seen similar questions on here but none of the answers are working for my linq query.
I am trying to convert a string to integer on the .ThenBy()
dbResults = gaResultDetails.All
.Where(c => c.ContentLink.Id == contentId && c.RequestType.Id == requestTypeId)
.OrderBy(c => c.DateFrom)
.ThenBy(c => int.Parse(c.Data_2)).Take(Take).ToList();
Please note I am using nHibernate for data access and with the above expression get the following error:
[NotSupportedException: Int32 Parse(System.String)]
Help!
Some functions are not supported by the nhibernate linq expression builder, try this:
dbResults = gaResultDetails.All
.Where(c => c.ContentLink.Id == contentId && c.RequestType.Id == requestTypeId)
.AsEnumerable()
.OrderBy(c => c.DateFrom)
.ThenBy(c => int.Parse(c.Data_2))
.Take(Take)
.ToList();
Might not be ideal performance-wise, but should accomplish what you need.
This is just a shot in the dark. If the parse doesn't work in the ThenBy, it probably won't in the let but it's worth a shot. In LINQ syntax, cuz I like it better:
dbResults = (from c in gaResultDetails.All
where c.ContentLink.Id == contentId
&& c.RequestType.Id == requestTypeId
let nData2 = int.Parse(c.Data_2)
orderby c.DateFrom, nData2)
.Take(Take)
.ToList();
It seems like your ORM tries to perform casting on the SQL server side.
Try to evaluate data before casting, e.g. :
dbResults = gaResultDetails.All
.Where(c => c.ContentLink.Id == contentId && c.RequestType.Id == requestTypeId)
.OrderBy(c => c.DateFrom).ToList()
.ThenBy(c => int.Parse(c.Data_2)).Take(Take).ToList();
I need to order the articles stored in a database by descending publication date and then take the first 20 records after the article with Id == 100.
This is what I would like to do with Linq:
IQueryable<Article> articles =
db.Articles
.OrderByDescending(a => a.PublicationDate)
.SkipWhile(a => a.Id != 100)
.Take(20);
However, this generates a NotSupportedException because SkipWhile is not supported in Linq to Sql (see here).
A possible solution is to execute the query and then apply SkipWhile using Linq to Object:
IEnumerable<ArticleDescriptor> articles =
db.Articles
.OrderByDescending(a => a.PublicationDate)
.ToList()
.SkipWhile(a => a.Article.Id != 100)
.Take(20);
But this means I need to load the whole ordered list into memory first and then take 20 articles after the one with Id == 100.
Is there a way to avoid this huge memory consumption?
More in general, what is the best way to achieve this in SQL?
If, as I'm guessing from the column name, PublicationDate doesn't change, you can do this in two separate queries:
Establish the PublicationDate of the Article with Id == 100
Retrieve the 20 articles from that date onwards
Something like:
var thresholdDate = db.Articles.Single(a => a.Id == 100).PublicationDate;
var articles =
db.Articles
.Where(a => a.PublicationDate <= thresholdDate)
.OrderByDescending(a => a.PublicationDate)
.Take(20);
It might even be that LINQ to SQL can translate this:
var articles =
db.Articles
.Where(a => a.PublicationDate
<= db.Articles.Single(aa => aa.Id == 100).PublicationDate)
.OrderByDescending(a => a.PublicationDate)
.Take(20);
but that may be too complex for it. Try it and see.
You can try like this
var articles =
db.Articles
.Where(a => a.PublicationDate < db.Articles
.Where(aa => aa.Id==100)
.Select(aa => aa.PublicationDate)
.SingleOrDefault())
.OrderByDescending(a => a.PublicationDate)
.Take(20);
Isnt the solution to just add a where statement?
IQueryable<Article> articles = db.Articles.Where(a => a.id != 100).OrderByDescending(a => a.PublicationDate).Take(20);