query a sub-collection of a collection with linq - c#

I've got a collection of products, and each product object has it's own ProductImages collection. Each ProductImage object has a IsMainImage bool field. I'm having a hard time building a Linq query like this:
select products.productimages.imagename where products.productid == 1 and
product.productimages.ismainimage == true
Can anyone help me figure this out, point me to an online resource where I can learn about writing linq queries like this, or both?
Thank you for your help!

Try something like
from product in products
where product.productid == 1
from image in product.productimages
where image.ismainimage
select image.imagename
I also found this list of 101 linq queries which may contain good information for you.

You can also use .SelectMany() projection method also.
products.Where(product => product.productid == 1)
.SelectMany(product =>
product.productimages.Where(image => image.ismainimage)
.Select(image => image.imagename)
);

Another way to write the query is to select first image that is the main image for the product 1:
var q = from p in products
where p.ProductID == 1
select p.ProductImages.First(img => img.IsMainImage);
I would think this is more readable than nested from clauses (which are usually used for joins and similar constructs). Using First may be also more efficient, but that's just a guess (and it very likely doesn't matter)

Related

Is it possible to combine 2 LINQ queries, each filtering data, before fetching the results of the queries?

I need to retrieve data from 2 SQL tables, using LINQ. I was hoping to combine them using a Join. I've looked this problem up on Stack Overflow, but all the questions and answers I've seen involve retrieving the data using ToList(), but I need to use lazy loading. The reason for this is there's too much data to fetch it all. Therefore, I've got to apply a filter to both queries before performing a ToList().
One of these queries is easily specified:
var solutions = ctx.Solutions.Where(s => s.SolutionNumber.Substring(0, 2) == yearsToConsider.PreviousYear || s.SolutionNumber.Substring(0, 2) == yearsToConsider.CurrentYear);
It retrieves all the data from the Solution table, where the SolutionNumber starts with either the current or previous year. It returns an IQueryable.
The thing that's tough for me to figure out is how to retrieve a filtered list from another table named Proficiency. At this point all I've got is this:
var profs = ctx.Proficiencies;
The Proficiency table has a column named SolutionID, which is a foreign key to the ID column in the Solution table. If I were doing this in SQL, I'd do a subquery where SolutionID is in a collection of IDs from the Solution table, where those Solution records match the same Where clause I'm using to retrieve the IQueryable for Solutions above. Only when I've specified both IQueryables do I want to then perform a ToList().
But I don't know how to specify the second LINQ query for Proficiency. How do I go about doing what I'm trying to do?
As far as I understand, you are trying to fetch Proficiencies based on some Solutions. This might be achieved in two different ways. I'll try to provide solutions in Linq as it is more readable. However, you can change them in Lambda Expressions later.
Solution 1
var solutions = ctx.Solutions
.Where(s => s.SolutionNumber.Substring(0, 2) == yearsToConsider.PreviousYear || s.SolutionNumber.Substring(0, 2) == yearsToConsider.CurrentYear)
.Select(q => q.SolutionId);
var profs = (from prof in ctx.Proficiencies where (from sol in solutions select sol).Contains(prof.SolutionID) select prof).ToList();
or
Solution 2
var profs = (from prof in ctx.Proficiencies
join sol in ctx.Solutions on prof.SolutionId equals sol.Id
where sol.SolutionNumber.Substring(0, 2) == yearsToConsider.PreviousYear || sol.SolutionNumber.Substring(0, 2) == yearsToConsider.CurrentYear
select prof).Distinct().ToList();
You can trace both queries in SQL Profiler to investigate the generated queries. But I'd go for the first solution as it will generate a subquery that is faster and does not use Distinct function that is not recommended unless you have to.

Simple table join in nHibernate

I'm new to working with nHibernate and have inherited a project which has implemented it for accessing its database. So far I've been able to write additional single-table query methods using QueryOvery<>, but I'm finding the logic behind table joins perplexing.
I want to implement the following T-SQL query:
SELECT DISTINCT f.FILE_ID, f.COMPANY_ID, f.FILE_META_ID, (etc...)
FROM AUDIT.FILE_INSTANCE f
INNER JOIN AUDIT.FILE_INSTANCE_REPORTING_PERIOD p ON p.FILE_ID = f.FILE_ID
WHERE p.REPORTING_PERIOD_ID BETWEEN 20150101 AND 20151304
AND FILE_STATUS != 'CANCELLED';
In this example, the reporting period ids would be parameters. Please note that they are integers, not dates, and the DISTINCT clause is important. How should I proceed?
I do not know your classes but if i would have written it, it would look like this:
var results = session.QueryOver<FileInstance>()
.Where(fi => fi.Status == FileStatus.Canceled)
.JoinQueryOver(fi => fi.ReportingPeriods)
.WhereRestrictionOn(period => period.Id).Between(20150101, 20151304)
.Transform(Transformers.DistinctRootEntity)
.List();

How do you override lazy loading on a single LINQ query using Fluent NHibernate

By default, I have lazy loading enabled on all my models, and that's the way I want to keep things. However, sometimes I want to eagerly fetch all the data up front on an individual query. From everything I've read, I should be using FetchMany() for this purpose. However, if I do:
var dbRecipe = (from r in session.Query<Models.Recipes>().FetchMany(x => x.Ingredients)
where r.RecipeId == recipeId
select r).FirstOrDefault();
Then dbRecipe.Ingredients.Count() returns 1. In other words, it only returns the first ingredient of that recipe. However, if I do:
var dbRecipe = (from r in session.Query<Models.Recipes>()
where r.RecipeId == recipeId
select r).FirstOrDefault();
Then dbRecipe.Ingredients.Count() returns 12, which is correct, however it performs a second query to load the ingredients for that recipe.
How do I make FetchMany fetch all 12 records up front? I was assuming that was the difference between Fetch and FetchMany. I'm clearly doing something wrong.
You can work around this by not running FirstOrDefault as the last statement. This will cause nh to run a top(1) query which yields wrong results...
Instead use .ToList().FirstOrDefault().
Or you use QueryOver<> which works fine
session.QueryOver<Models.Recipes>()
.Fetch(prop => prop.Ingredients)
.Eager
.Where(p => p.RecipeId == recipeId)
.SingleOrDefault();

Using Distinct in LINQ to SQL on the primary key

I have a system that uses tags to categorize content similar to the way Stack Overflow does. I am trying to generate a list of the most recently used tags using LINQ to SQL.
(from x in cm.ContentTags
join t in cm.Tags on x.TagID equals t.TagID
orderby x.ContentItem.Date descending select t)
.Distinct(p => p.TagID) // <-- Ideally I'd be able to do this
The Tags table has a many to many relationship with the ContentItems table. ContentTags joins them, each tuple having a reference to the Tag and ContentItem.
I can't just use distinct because it compares on Tablename.* rather than Tablename.PrimaryKey, and I can't implement an IEqualityComparer since that doesn't translate to SQL, and I don't want to pull potential millions of records from the DB with .ToList(). So, what should I do?
You could write your own query provider, that supports such an overloaded distinct operator. It's not cheap, but it would probably be worthwhile, particularly if you could make it's query generation composable. That would enable a lot of customizations.
Otherwise you could create a stored proc or a view.
Use a subselect:
var result = from t in cm.Tags
where(
from x in cm.ContentTags
where x.TagID == t.TagID
).Contains(t.TagID)
select t;
This will mean only distinct records are returned form cm.Tags, only problem is you will need to find some way to order result
Use:
.GroupBy(x => x.TagID)
And then extract the data in:
Select(x => x.First().Example)

How does a LINQ expression know that Where() comes before Select()?

I'm trying to create a LINQ provider. I'm using the guide LINQ: Building an IQueryable provider series, and I have added the code up to LINQ: Building an IQueryable Provider - Part IV.
I am getting a feel of how it is working and the idea behind it. Now I'm stuck on a problem, which isn't a code problem but more about the understanding.
I'm firing off this statement:
QueryProvider provider = new DbQueryProvider();
Query<Customer> customers = new Query<Customer>(provider);
int i = 3;
var newLinqCustomer = customers.Select(c => new { c.Id, c.Name}).Where(p => p.Id == 2 | p.Id == i).ToList();
Somehow the code, or expression, knows that the Where comes before the Select. But how and where?
There is no way in the code that sorts the expression, in fact the ToString() in debug mode, shows that the Select comes before the Where.
I was trying to make the code fail. Normal I did the Where first and then the Select.
So how does the expression sort this? I have not done any change to the code in the guide.
The expressions are "interpreted", "translated" or "executed" in the order you write them - so the Where does not come before the Select
If you execute:
var newLinqCustomer = customers.Select(c => new { c.Id, c.Name})
.Where(p => p.Id == 2 | p.Id == i).ToList();
Then the Where is executed on the IEnumerable or IQueryable of the anonymous type.
If you execute:
var newLinqCustomer = customers.Where(p => p.Id == 2 | p.Id == i)
.Select(c => new { c.Id, c.Name}).ToList();
Then the Where is executed on the IEnumerable or IQueryable of the customer type.
The only thing I can think of is that maybe you're seeing some generated SQL where the SELECT and WHERE have been reordered? In which case I'd guess that there's an optimisation step somewhere in the (e.g.) LINQ to SQL provider that takes SELECT Id, Name FROM (SELECT Id, Name FROM Customer WHERE Id=2 || Id=#i) and converts it to SELECT Id, Name FROM Customer WHERE Id=2 || Id=#i - but this must be a provider specific optimisation.
No, in the general case (such as LINQ to Objects) the select will be executed before the where statement. Think of it is a pipeline, your first step is a transformation, the second a filter. Not the other way round, as it would be the case if you wrote Where...Select.
Now, a LINQ Provider has the freedom to walk the expression tree and optimize it as it sees fit. Be aware that you may not change the semantics of the expression though. This means that a smart LINQ to SQL provider would try to pull as many where clauses it can into the SQL query to reduce the amount of data travelling over the network. However, keep the example from Stuart in mind: Not all query providers are clever, partly because ruling out side effects from query reordering is not as easy as it seems.

Categories

Resources