Linq filter collection with EF - linq

I'm trying to get Entity Framework to select an object and filter its collection at the same time. I have a JobSeries object which has a collection of jobs, what I need to do is select a jobseries by ID and filter all the jobs by SendDate but I can't believe how difficult this simple query is!
This is the basic query which works:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where c.Id == jobSeriesId
select c;
Any help would be appreciated, I've been trying to find something in google and what I want to do is here:http://blogs.msdn.com/bethmassi/archive/2009/07/16/filtering-entity-framework-collections-in-master-detail-forms.aspx
It's in VB.NET though and I couldn't convert it to C#.
EDIT: I've tried this now and it doesn't work!:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs")
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where (c.Id == jobSeriesId & c.Jobs.Any(J => J.ArtworkId == "13"))
select c;
Thanks
Dan

Include can introduce performance problems. Lazy loading is guaranteed to introduce performance problems. Projection is cheap and easy:
var q = from c in KnowledgeStoreEntities.JobSeries
where c.Id == jobSeriesId
select new
{
SeriesName = c.Name,
Jobs = from j in c.Jobs
where j.SendDate == sendDate
select new
{
Name = j.Name
}
CategoryName = c.Category.Name
};
Obviously, I'm guessing at the names. But note:
Filtering works.
SQL is much simpler.
No untyped strings anywhere.
You always get the data you need, without having to specify it in two places (Include and elsewhere).
No bandwith penalties for retrieving columns you don't need.
Free performance boost in EF 4.
The key is to think in LINQ, rather than in SQL or in materializing entire entities for no good reason as you would with older ORMs.

I've long given up on .Include() and implemented Lazy loading for Entity Framework

Related

Combining LINQ Queries to reduce database calls

I have 2 queries that work, I was hoping to combine them to reduce the database calls.
var locations = from l in db.Locations
where l.LocationID.Equals(TagID)
select l;
I do the above because I need l.Name, but is there a way to take the above results and put them into the query below?
articles = from a in db.Articles
where
(
from l in a.Locations
where l.LocationID.Equals(TagID)
select l
).Any()
select a;
Will I actually be reducing any database calls here?
This seems a bit complicated because Locations appears to be a multi-value property of Articles and you want to only load the correct one. According to this answer to a similar question you need to use a select to return them separately in one go so e.g.
var articles = from a in db.Articles
select new {
Article = a,
Location = a.Locations.Where(l => l.LocationId == TagId)
};
First failed attempt using join:
var articlesAndLocations = from a in db.Articles
join l in a.Locations
on l.LocationID equals TagID
select new { Article = a, Location = l };
(I usually use the other LINQ syntax though so apologies if I've done something stupid there.)
Could you not use the Include() method here to pull in the locations which are associated with each article, then select both the article and location object? or the properties you need from each.
The include method will ensure that you don't need to dip into the db twice, but will allow you to access properties on related entities.
You would need to use a contains method on an IEnumerable I believe, something like this:
var tagIdList = new List() { TagID };
var articles = from a in db.Articles.Include("Locations")
where tagIdList.Contains(from l in a.Locations select l.LocationID)
select new { a, a.Locations.Name };
(Untested)

Wait for DomainContext.Load<t> from an entityquery with joins to complete (returning new type via 'select new')

My app consolidates data from other DBs for reporting purposes. We can't link the databases, so all the data processing has to be done in code - this is fine as we want to allow manual validation during the imports.
Certain users will be able to start an update through the Silverlight 4 front end.
I have 3 tables in database x that are fed from one EF4 Model (ModelX). I want to join those tables together, select specific columns and return the result as a new entity that exists in a different EF4 Model (ModelY). I'm using this query:
var myQuery = from i in DBx.table1 from it in DBx.table2 from h in DBx.table3 where (i.id==it.id && h.otherid == i.otherid) select new ModelYServer {Name = i.name,Thing = it.thing, Stuff = h.stuff};
The bit i'm stuck on, is how to execute that query, and wait until the Asynchronous call has completed. Normally, i'd use:
DomainContext.Load<T>(myQuery).Completed += (sender,args) =>
{List<T> myList = ((LoadOperation<T>)sender.Entities.ToList();};
but I can't pass myQuery (an IEnumerable) into the DomainContext.Load() as that expects an EntityQuery. The dataset is very large, and is taking up to 30 seconds to return, so I definitely need to wait before continuing.
So can anyone tell me how I can wait for the IEnumerable query to complete, or suggest a better way of doing this (there very likely is one).
Thanks
Mick
One simple way is just to force it to evaluate by calling ToList:
var query = from i in DBx.table1
join it in DBx.table2 on i.id equals it.id
join h in DBx.table3 on i.otherid equals h.otherid
select new ModelYServer {
Name = i.name,
Thing = it.thing,
Stuff = h.stuff
};
// This will block until the results have been fetched
var results = query.ToList();
// Now use results...
(I've changed your where clause into joins on the earlier tables, as that's what you were effectively doing and this is more idiomatic, IMO.)

NHIbernate Linq group by count

I have the version 3.0.0.1001 nhibernate.
My objects are basically modeiling a lineup at an event. So I have a StageSet object which represents one slot in the schedule for a stage.
Each StageSet object has a Stage and an Act property.
It also has many Users - people who have favorited the set.
I'm trying to ascertain the most popular sets that have been favorited using the following linq:
var topStars = from s in Db.StageSets
group s by s.Act.Id into g
select new { SetKey = g.Key, Count = g.Count() };
However this just fails with a Could not execute query[SQL: SQL not available] error
Should I be able to do this?
w://
in case someone comes here. The following should work with NH 3.1
var topStars = from s in Db.StageSets
group s by s.Act.Id into g
select new { SetKey = g.First().Act.Id, Count = g.Count() }
You've specified the query correctly in linq. NHibernate is refusing to translate it.
I just copied your query with a slightly different domain and it worked. But that will count StageSets by Act, NOT favorites.

How do I retrieve only certain subobjects in LINQ?

I have an entity object (Company) which has 1 or more subobjects (CompanyRevision) represented as a non-null FK relationship in the database.
Using LINQ, I want to get all the Companies from the database, but I also only want the latest CompanyRevision for each company.
This is how I do it today, but I have a feeling this could be done using one query.
IEnumerable<Company> companyList = from p in ctx.Company.Include("CompanyRevisions")
select p;
foreach(Company c in companyList)
{
CompanyRevision cr = (from p in c.CompanyRevisions
orderby p.Timestamp descending
select p).First();
// Do something with c and cr...
}
As you can see, I would like to add this second LINQ query (the one that gets the latest CompanyRevision) into the first one, so that companyList[i].CompanyRevisions is basicly a list with just one entry (the latest one). I can't for the life of my figure out how to do this. Please help!
Thanks in advance
how about this: mixing the linq language and extension methods:
var results = from p in ctx.Company.Include("CompanyRevisions")
select new {Company = p,
Revision = p.CompanyRevisions.OrderByDescending(cr => cr.Timestamp).First()
}
Each result now has a Company and Revision member.
It's possible that you could also do this -
var results = from p in ctx.Company.Include("CompanyRevisions")
select new {Company = p,
Revision = (from pcr in p.CompanyRevisions
orderby pcr.Timestamp descending
select pcr).First()
}
To give the same results.
Although that's a guess - I haven't labbed that one out; but it's how I would try it first.

Does LINQ Support Composable "OR Queries"?

In another posting: Does Linq-To-Sql support composable queries there was discussion on how to compose/concat where clauses dynamically. This appears to be done with an "AND" (i.e. the first where clause and the second where clause are joined by an AND). What I am wondering is if there is a way to compose Linq queries with an OR.
Example:
var people = from p in Person
where p.age < 18
select p
var otherPeople = from p in people
where p.firstName equals "Daniel"
select p
This gives people with a first name of "Daniel" and that are under 18. I'm looking for the syntax to join these to find people who have a first name of "Daniel" or are under 18.
Note: I am using ADO.net Data Services so I do not have .Contains() available to me.
EDIT: The Union Suggestion (by Garry Shutler) is exactly what I am looking for functionality-wise. I did run into two possible issues with it:
It looks like it would make multiple database hits if I was to do a third condition (union seems to take an IEnumerable as its parameter) - I was hoping to build up multiple AND and OR statements in code and then execute one request.
Union is not supported by ADO.Net Data Services (very disappointing)
Is what you want as simple as:
var people = from p in Person
where p.age < 18 || p.firstName == "Daniel"
select p;
or have you just given a simple example?
In which case you can use:
var under18 = from p in Person
where p.age < 18
select p;
var daniels = from p in Person
where p.firstName == "Daniel"
select p;
var combined = under18.Union(daniels);
LinqToSql may be intelligent enough to convert that to an OR but I'm not so sure.
What about using PredicateBuilder by Joe Albahari?
var predicate = PredicateBuilder.False<Person>();
predicate = predicate.Or(p => p.age < 18);
predicate = predicate.Or(p => p.firstName == "Daniel");
var query = Person.Where(predicate);
The predicate option is the way to go. The Union option DOES NOT build good sql. Reference http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/925b245d-5529-4a64-8cd4-4bc83ee6fe7a/
I wrote about how to achieve queries which search for a key value within a set on my blog .
Here are the relevant links.
Contains Operations in ADO.NET Data Services Part I
Contains Operations in ADO.NET Data Services Part II
Using this , you can write queries which look like this
//The set in which we have to search for a match
List<string> citiesIWillVisit = new List<string>() {"London","Berlin","Prague"};
var customersAround = nwContext.Customers
.IsIn<Customers>(citiesIWillVisit, c=> c.City);
foreach (Customers localCustomer in customersAround) {
System.Console.WriteLine(localCustomer.ContactName);
}

Resources