Linqbuilder Query with an OrderBy - c#

I have a 1 : M relationship.
I built a dynamic query based on input from users to return the listing of parents entities along with their children (using predicate builder:
(done successfully new TDataContext().Ps.Where(predicate) )...
but need to order the results by a field found only on the child entities.
I'm at a loss: new TDataContext().Ps.Where(predicate).OrderBy(p => p.Cs. ??)
where Ps = parents collection relationship with Cs = child entities
any help appreciated.

One way would be to select childs first:
new TDataContext().Ps.Where(predicate).SelectMany(p=>p.Cs).OrderBy(q => q.Name);

Try something like this:
new TDataContext().Ps.Where(predicate).OrderBy((<datatype of p> p) => p.Cs.Name)
You will have to replace "<datatype of p>" with whatever that is. Also, you will have to replace "Name" with whatever field you want to sort by.

Related

Apply where condition on the child table in Linq to SQL

I have a table TableA and child table TableB. I want to fetch all the parent table records,
but select child records which satisfy a condition. I am using include to get the child records.
Is there any direct way other than using select new?
LINQ to SQL has a LoadOptions that you can set on the context to do some powerful things. Most people point to the .LoadWith which eagerly loads child records. There's also an AssociateWith which specifies the filtering to apply on lazy child fetches. Both of them can take a lambda expression for sub child filtering. Here's an example:
var lo = new DataLoadOptions();
lo.AssociateWith<Customers>
(c => c.Orders.Where(o => !o.ShippedDate.HasValue));
this.LoadOptions=lo;
var query = from c in Customers
select c.Orders;
Note, this only works with LINQ to SQL. EF does not support this behavior at this time.
using (var context = new DbEntities()) {
foreach (var p in context.Parents) {
var childQuery = from c in p.Children
where c.whatever == something
select c;
// Do something with the items in childQuery, like add them to a List<Child>,
// or maybe a Dictionary<Parent,List<Child>>
}
}

Use LINQ-to-SQL to return an object that has child objects filtered

I have a MembershipGroups table that is associated with a child Members table. The Members table has a Status column which can be set to Active or Inactive.
I want to select all MembershipGroups and only their active Members
As an example,
MembershipGroups
ID----Title
1-----Group #1
2-----Group #2
Members
MembershipGroupID-Name--Status
1-------------------------John----Active
1-------------------------Sally----Inactive
1-------------------------David---Inactive
I'm trying to create a query that looks something like the following (which doesn't currently work):
var query = from mg in db.MembershipGroups
where mg.Members.Status = "Active"
select mg
The result for this example should return a MembershipGroup of ID#1 with only one child Member entity
How can use LINQ-to-SQL to select a parent object that filters on child objects? If I were using straight T-SQL then this would be a simple join with a where clause but it seems to be much more difficult to do using LINQ-to-SQL.
Edit - Updated answer to return the MemberShipGroup object
var query = (from mg in db.MembershipGroups
join m in db.Members.Where(mem => mem.Status == "Active")
on mg.ID equals m.MembershipGroups into members
select new
{
MembershipGroup = mg,
Members = members
}).AsEnumerable()
.Select(m => new MembershipGroup
{
ID = m.MembershipGroup.ID,
Title = m.MembershipGroup.Title,
Members = m.Members
});
In LINQ to SQL, you can use the AssociateWith method on the DataLoadOptions to set your child filter at the context level.
DataLoadOptions opt = new DataLoadOptions();
opt.AssociateWith<Member>(m => m.Status == "Active");
db.LoadOptions = opt;
With this in place, you can simply return your member groups (or filter them for the active ones using where mg.Any(group => group.Members.Status == "Active"). Then when you try to drill into the Members of that group, only the Active ones will be returned due to the LoadOptions.
See also http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.associatewith.aspx .
One word of warning, once you set the LoadOptions on a context instance, you can not change it. You may want to use a customized context to use this option.
As an alternative, you could use LINQ to SQL's inheritance model to create an ActiveMember type using the Status column as your discriminator and then create an association between the MemberGroups and ActiveMembers types. This would be the approach you would need to use to model this with the Entity Framework if you though about going that route as well as EF doesn't support the concept of the LoadOptions.
Make sure you are including the child objects you are trying to filter on, inside the query.
E.g.
var query = db.MembershipGroups
.Include("Members")
.Where(m => m.Members.Status == "Active");

EF4: Joining Entity Query on IEnumerable list of objects

I have two IEnumerables:
IEnumerable<ThisEmployee> thisEmployees;
IEnumerable<ThatEmployee> thatEmployees;
They are populated from 2 separate contexts. ThisEmployee and ThatEmployee are not matching types. They don't share anything similar apart from an EmployeeNumber property.
I want to get all ThatEmployee.Notes for any employee in thatEmployees that has a matching EmployeeNumber in thisEmployees.
I can't for the life of me work out how.
Your collections come from different contexts so get ids of employees first in linq-to-objects:
var ids = from e1 in thatEmployees
join e2 in thisEmployees on e1.EmployeeNumber equals e2.EmployeeNumber
select e1.Id;
Now use ids to get Notes from the database in single query
var notes = from n in context.Notes
where ids.Contains(n.Employee.Id)
select n;
Since its in two different contexts try using ToList to get all objects. Then using Linq to Objects u can use Where(r => thisEmployees.Any(s => s.EmployeeNumber == r.EmployeeNumber)). Not sure if i understood u correctly :)
How about something like:
var notes = thatEmployees
.Join(thisEmployees,
ta => ta.EmployeeNumber,
ti => ti.EmployeeNumber,
(ta, ti) => ta.Notes)

LINQ to SQL where collection contains collection

I have a problem :( I have a many-many table between two tables(1&2), via a mapping table(3):
(1)Trees / (2)Insects
TreeID <- (3)TreeInsects -> InsectID
And then a one to many relationship:
Trees.ID -> Leaves.TreeID
And I would like to perform a query which will give me all the Leaves for a collection of insects (via trees-insect mapping table).
E.g. I have List<Insects> and I want all Leaves that have an association with any of the Insects in the List via the Tree-Insects mapping table.
This seems like a simple task, but for some reason I'm having trouble doing this!!
The best I have: but the Single() makes it incorrect:
from l in Leaves
where (from i in Insects
select i.ID)
.Contains((from ti in l.Tree.TreeInsects
select ti.InsectID).Single())
select l;
(from i in insectsCollection
select from l in Leaves
let treeInsectIDs = l.Tree.TreeInsects.Select(ti => ti.InsectID)
where treeInsectIDs.Contains(i.ID)
select l)
.SelectMany(l => l)
.Distinct();
I'm bad with sql-like syntax so I'll write with extensions.
ctx.Leaves.Where(l => ctx.TreeInsects.Where( ti => list_with_insects.Select(lwi => lwi.InsectID).Contains( ti.InsectID ) ).Any( ti => ti.TreeID == l.TreeID ) );
Try investigating the SelectMany method - I think that may be the key you need.
I would get a list of Trees that are available to that Insect, then peg on a SelectMany to the end and pull out the collection of Leaves tied to that Tree.
List<int> insectIds = localInsects.Select(i => i.ID).ToList();
//note - each leaf is evaluated, so no duplicates.
IQueryable<Leaf> query =
from leaf in myDataContext.Leaves
where leaf.Tree.TreeInsects.Any(ti => insectIds.Contains(ti.InsectId))
select leaf;
//note, each matching insect is found, then turned into a collection of leaves.
// if two insects have the same leaf, that leaf will be duplicated.
IQueryable<Leaf> query2 =
from insect in myDataContext.Insects
where insectIds.Contains(insect.ID)
from ti in insect.TreeInsects
from leaf in ti.Tree.Leaves
select leaf;
Also note, Sql Server has a parameter limit of ~2100. LinqToSql will happily generate a query with more insect IDs, but you'll get a sql exception when you try to run it. To resolve this, run the query more than once, on smaller batches of IDs.
How do you get this list of insects? Is it a query too?
Anyway, if you don't mind performance (SelectMany can be slow if you have a big database), this should work:
List<Insect> insects = .... ; //(your query/method)
IEnumerable<Leave> leaves = db.TreeInsects
.Where(p=> insects.Contains(p.Insect))
.Select(p=>p.Tree)
.SelectMany(p=>p.Leaves);

Linking Multiple Tables in LINQ to SQL

I would like to get the list of albums (Distinct) which was sung by the artistId=1
I am very new to LINQ to SQL and do not know how to join multiple tables. Please see the database diagram below:
alt text http://a.imageshack.us/img155/8572/13690801.jpg
SingBy is the middle table between Track and Artist.
How could I achieve this?
var albums = from singer in artist
from sb in singby
from t in track
from a in album
where singer.artistId == 1 &&
sb.artistId == 1 &&
sb.trackId == t.trackId &&
a.albumId == track.albumId
select a;
I'm sure there must be a better way. You should look into creating Navigation Properties on your entities. Navigation Properties are like foreign keys.
Edit - corrected to get albums, not artists.
Now, I wrote the codes like the following and it works.
var albums = (from a in db.artists
where a.artistId == 1
join sb in db.singbies on a equals sb.artist
join t in db.tracks on sb.track equals t
join al in db.albums on t.album equals al
select al).Distinct();
return albums.ToList() as List<album>;
I tested the Chad's version and it works too. I would like to know which way is better and good for query optimization? Thanks all.
If you have all the foreign key relationship defined, you should be able to issue call like below:
dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).ToList();
This is relying on Linq to perform lazy load for Track and Singby automatically when required. Obviously this is not optimal to use when you have a large set of data in the db and performance is critical. You can chain the query with GroupBy or Distinct operation to return only the distinct set such as
dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).Distinct().ToList();
I would like to get the list of albums
(Distinct) which was sung by the
artistId=1
DBDataContext = new DBDataContext();
album[] = db.artists.Where(a => a.artistId == 1) /* Your artist */
.SelectMany(a => a.singbies) /* Check if `singby` converted to `singbies` */
.Select(sb => sb.track) /* The tracks */
.Select(t => t.album) /* The albums */
.GroupBy(al => al.albumId) /* Group by id */ /* "Distinct" for objects */
.Select(alG => alG.First()) /* Select first of each group */
.ToArray();
IEnumerable<Album> query =
from album in myDC.Albums
let artists =
from track in album.Tracks
from singBy in track.SingBys
select singBy.Artist
where artists.Any(artist => artist.ArtistId == 1)
select album;
List<int> Ids = dc.Albums.Where(a => a.Track.Singby.ArtistId == 1).Select(a=> a.albumId).Distinct().ToList();
List<Album> distinctAlbums = dc.Albums.Where(a => distinctAlbumIds.Contains(a.albumId)).ToList();
Hey TTCG, above is the simplest way to do it. This is because doing a Distinct on a List of objects won't do it based on the albumId.
Either you do it in two steps as above, or, you write your own Album Comparer which specifies uniqueness based on AlbumId and pass it to the Distinct call on a List.
NOTE:
The above will only work if you've defined the constraints in your DBML, but better still in your DB.
For best practices, always define your relationships IN THE DATABASE when using Linq to SQL, as Linq to SQL is not like EF, or NHibernate, in that is does not "abstract" your db, it simply reflects it. It's a tool for Data Driven Design, not Domain Driven, so define the relationships in the db.

Categories

Resources