LinqToSql Filter EntitySet - c#

I am working on a WP7 application using Linq To Sql. I have used Linq, but this is the first I have used Linq to Sql. I am having an issue with filtering data in an EntitySet. I maybe doing it wrong I have no clue. What I have right now works, but I need to get one of the EntitySets filtered.
I have 4 tables. The Parent, Child, Grandchild, and a ParentChild linking table. When I query ParentChild, I get back the ParentChild entity and I can iterate through the Parent, Child and Grandchild entities just fine. What I want to be able to do is filter the Grandchild entity.
Lets say I have a father and mother in the Parent table. Then I have a son and daughter in the Child table. Then a grandson and granddaughter in the Grandchild table. Of course there are normal associations, etc.
I want to return the father, which also gets me all the associated tables just fine. The problem that I have is with filtering on the Grandchild. Let's say I want just the grandson and have a field for sex. How can I do this? I just can't seem to figure it out.
Here is the code I am using which works fine, but it pulls all the grandchildren.
IQueryable<ParentChild> parentChild = from ParentChild c in DataContext.ParentChild
where c.ParentId == this.parentId
select c;
foreach (Grandchild grandchild in parentChild.SelectMany(parent => parent.Child.Grandchild))
{
Console.WriteLine(grandchild.Name);
}
So if I do this:
IQueryable<ParentChild> parentChild = from ParentChild c in DataContext.ParentChild
where c.ParentId == this.parentId && c.Child.Grandchild.Any(a => a.Sex == "F")
select c;
foreach (Grandchild grandchild in parentChild.SelectMany(parent => parent.Child.Grandchild))
{
Console.WriteLine(grandchild.Name);
}
I get the parent, but I only get the children that have female grandchildren. I want the parent, all the children (even if they don't have female grandchildren or don't have any grandchildren) and only the female grandchildren.

After much trial and error and searching, I found the answer. I have to use the AssociateWith option.
DataLoadOptions dataLoadOptions = new DataLoadOptions();
dataLoadOptions.AssociateWith<Child>(c => c.Grandchild.Where(p => p.Sex == "F"));
this.DataContext.LoadOptions = dataLoadOptions;

As long as you've got your foreign keys set up correctly in SQL; LINQ to SQL will be able to give you association properties that match your foreign key relationships.
If your foreign keys are set up you'll be able to do the following...
var query = from p in DataContext.Parent
//make sure they have at least 1 female grandchild
where p.GrandChilds.Any(gc => gc.IsFemale)
select p;
I've made some assumptions about the names in your datamodel, but you get the idea. :-)

Related

Runtime joins to query within Entity Framework

I have a database that is mapped using the Entity Framework. Entity Framework is generating the C# code of the database objects in the similar manner as shown below. For simplicity I have created Parent, Child, GrandChild hierarchy but the actual db contains much longer hierarchies and many other fields.
class Parent
{
string name;
int id;
datetime DateOfBirth;
}
class Child
{
string name;
int id;
int ParentId; ( FK reference to Parent Child )
}
class GrandChild
{
string name;
int id;
int ChildId; (FK reference to Child )
}
Now, I am building an api where the filters will be provided at runtime. I mean, some of the queries could be
Give all GrandChild rows for ParentId =1
Give All Grandchild rows for ChildName = "x"
Give all GrandChild rows for Parent with DateOfbirth = "x/y/z"
So, how can I build in C# code, using LINQ or Expression Trees to create predicates and join filters dynamically/runtime.
Following URL:
https://msdn.microsoft.com/library/bb882637.aspx
shows how to create dynamic query, but not how to INNER JOIN multiple such queries. Does anyone know how to do that?
This Stackoverflow answer:
The parameter '***' was not bound in the specified LINQ to Entities query expression
also highlights how to create filters dynamically but not how to join them.
Does anyone know how to create dynamic queries to filter rows and join the queries? Let me know if you need more information. Thanks
Well, I'm going to suppose that is not your real model because you should have properties instead fields and your entities must be public.
If you have represented your db relationships using navigation properties, you could create a extension method like this:
static IQueryable<TEntity> Select<TEntity>(this IQueryable<TEntity> query, List<Expression<Func<TEntity, bool>>> filters = null,
List<Expression<Func<TEntity, object>>> includes = null)
{
if (includes != null)
{
query = includes.Aggregate(query, (current, include) => current.Include(include));
}
if (filters != null)
{
query = filters.Aggregate(query, (current, filter) => current.Where(filter)); //at the end this is going to be translated to condition1 && condition2 ...
}
return query;
}
In the first list you pass all the conditions you want to apply to your query and the second helps you to load the related entities that you need in your query:
var conditions = new List<Expression<Func<GrandChild, bool>>>() { (t) => t.Child.Parent.ParentId==1 };
var includes = new List<Expression<Func<GrandChild, object>>>() { (t) => t.Child.Parent };
var query= yourContext.GrandChilds.Select(filters,includes);
1 . Give all GrandChild rows for ParentId =1
var grandChildData=from grand in GrandChild
join ch in Child on grand.ChildId equals ch.Id
where ch.ParentId==1
select grand;
Give All Grandchild rows for ChildName = "x"
var grandChildData=from grand in GrandChild
join ch in Child on grand.ChildId equals ch.Id
where ch.name = "x"
select grand;
Give all GrandChild rows for Parent with DateOfbirth = "x/y/z"
var grandChildData=from grand in GrandChild
join ch in Child on grand.ChildId equals ch.Id
join p in Parent on ch.ParentId equals p.id
where p.DateOfbirth==Convert.ToDateTime("2016/01/01")
select grand;

EF filtered include - many child entities

Having some issues with filtering child entities.
I know that EF doesn't support filtered includes but I can't get any other alternative to work either.
var q = from sWithA in
(from s in db.Svs
where s.Env.UID.Equals(env)
select new
{
Svs= s,
Cons= from c in s.Cons
where c.Apps.Any(a => a.AppT.Type.Equals(appT))
select c
}).AsEnumerable()
select sWithA.Svs;
List<Svs> svsList = q.ToList();
This actually generates a SQL query which returns the information I need, but the child entities aren't being attached to the parent.
svsList contains two Svs objects but the child collection (Cons) is empty.
Svs < many-to-many > Cons < many to one > Apps
Any ideas?
The relationship fixup trick that you apparently try to exploit doesn't work for many-to-many relationships, only for one-to-one and one-to-many relationships. For many-to-many relationships you have to fixup the navigation collections manually, for example like so:
List<Svs> svsList = (from s in db.Svs
where s.Env.UID.Equals(env)
select new
{
Svs = s,
Cons = from c in s.Cons
where c.Apps.Any(a => a.AppT.Type.Equals(appT))
select c
})
.AsEnumerable()
.Select(sWithA =>
{
sWithA.Svs.Cons = sWithA.Cons.ToList();
return sWithA.Svs;
})
.ToList();
Yes, it's ugly and looking at this code one really misses the filtered Include support.

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>>
}
}

Filtering a query in Entity Framework based upon a child value

I've got a model set up in the Entity Framework (EF) where there are two tables, parent and child, in a one to many relationship. Where I am having trouble is writing a query with linq where I am trying to retrieve a single instance of the parent while filtering upon a field in the parent and another field in the child. It would look something like what is listed below:
var query = from c in context.ParentTable
where c.IsActive == true && c.ChildTable.Name = "abc"
select c;
Unfortunately when I try this it fails because no field named "Name" appears available via Intellisense when I type c.ChildTable.
Any guidance would be appreciated.
That is correct because c.ChildTable is not of your Child type but EntityCollection<Child>. In order to make your query work, you need to modify it like this:
var query = from p in context.ParentTable
from c in p.ChildTable
where p.IsActive == true
&& c.Name == "abc"
select p;

Linqbuilder Query with an OrderBy

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.

Categories

Resources