C# LINQ Join multiple tables into one object - c#

Main problem to solve:
How do I get "2 levels down" to get Owner.Pet[n].Toys and put it all in one object right away.
There are three entities in the system: Owner, Pet and Toy.
I am trying to write a query (using LINQ's SQL-like notation) that will bring me single object of class Owner with its inner properties filled = all pets assigned to that owner + each pet having all its toys.
This is what I have so far, it's not working as expected. SQL feelings tell me that I am missing GROUP JOIN somewhere...
var singleOwnerQuery =
from o in owners
join p in pets on o.FirstName = p.OwnerName
join t in toys on p.PetId = t.ToyUniqueId // each toy is unique - there are not "re-usable" toys
where o.Name == "..." && o.LastName == "..."
select new Owner
{
Pets = pets // this should already assign all the toys for each of the pets
};
Any help will be appreciated.
I skipped lot of other properties inside each of the classes to make it simpler

Try the following query. Without model, this is just direction how to do Eager Loading query as in your case.
var singleOwnerQuery =
from o in owners
where o.Name == "..." && o.LastName == "..."
select new Owner
{
Pets = pets.Where(p => o.FirstName == p.OwnerName)
.Select(p => new
{
p.PetId,
Toys = toys.Where(t => p.PetId == t.ToyUniqueId).ToList()
})
.tolist()
};

Related

Join tables in NHibernate without mapping

I have the following two objects:
User
class User {
public int role;
}
Role
class Role {
public int id;
public string name;
}
be note that role property inside User is int and not Role, that's our limitations.
I want to join between all the users and each of his role. In the mapping objects there is no reference as you can understand, just a simple type (int).
How do I do that join statement?
It's called a theta join:
var a = (from u in session.Query<User>()
from r in session.Query<Role>()
where u.role == r.id
select new { u.Username, Role = r.name }).ToList();
Assuming you have a Username property on the User class.
Yes, this "theta join" (as I just learned this term) is very handy and let's us not worry about putting in pointless mapping relationships.
WARNING HOWEVER IN USING THIS!!! This tripped me up a lot.
Adding to the above example...
var list = new List<int>( { 2, 3 } ); // pretend in-memory data from something.
var a =
(from u in session.Query<User>()
from x in list
from r in session.Query<Role>()
where u.role == r.id
where r.id == x.id // pretend list wants to limit to only certain roles.
select new { u.Username, Role = r.name }).ToList();
THIS WILL BOMB with some NotSupported exception.
The trick is that anything coming from NHibernate Session must come LAST. So this alteration WILL work:
var a =
(from x in list
from u in session.Query<User>()
from r in session.Query<Role>()
where u.role == r.id
where r.id == x.id // pretend list wants to limit to only certain roles.
select new { u.Username, Role = r.name }).ToList();
And and BTW, you can use join as well, however you have to make sure if you have any nullable data types, that you use the .Value if you are joining to something not-nullable.
var a =
(from x in list
from u in session.Query<User>()
join r in session.Query<Role>() on u.role equals r.id
where r.id == x.id // pretend list wants to limit to only certain roles.
select new { u.Username, Role = r.name }).ToList();
And while we're at it, let's say you have a method that has some dynamic condition. In this example the 'list' which could be a list of roles to filter by, but don't filter at all if the list is not there. Well, if you do the .ToList() then you are causing this query to execute immediately. But instead you can add a condition and then execute it later:
var a =
from u in session.Query<User>()
join r in session.Query<Role>() on u.role equals r.id
where r.id == x.id // pretend list wants to limit to only certain roles.
select new { u.Username, Role = r.name, RoleID = r.id }; // Adding the Role ID into this output.
if (list != null) // assume if the list given is null, that means no filter.
{
a = a.Where(x => list.Contains(x.RoleID));
// WARNING. Unfortunately using the "theta" format here will not work. Not sure why.
}
var b = a.ToList(); // actually execute it.
var c = a.Select(x => new { x.Username, x.Role }).ToList() // if you insist on removing that extra RoleID in the output.
One last thing.. Sometimes some simple logic will fail when executed in the select new { .. } part. I don't have an explanation. In our case the logic was just converting a DB value of a uint to an Enumerator of a model. But to get around that, I just avoided doing that conversion while reading the data but saved the value. Then in a later step, after the data was loaded, I just did the conversion in another LINQ statement.
DISCLAIMER: While I wrote many of these things all the past several weeks, I did not put this code into my compiler to verify 100%.

Linq to entities navigation properties

In entity framework i have two table (two entities): People and Role with one to many relationship.
In the People tables i have a navigation property to Role:
//People.cs
public virtual ICollection<Role> Role { get; set; }
Now i want retrieve all people that have role as 'barman'. How can i achieve this?
I want use linq to entities in the query expression method. I've tried:
var listPerson = (from p in SiContext.People
where p.Role.Name = 'barman'
select p).ToList();
The problem is that i cannot make p.Ruolo.Name because p.Ruolo is a ICollectionType that doesn't have the property "Name" (while the entity Role has that property)
Since role is a collection, you need to use Any
var listPerson = (from p in SiContext.People
where p.Role.Any(x => x.Name == "barman")
select p).ToList();
Just to complemente your code include ToLower():
var listPerson = (from p in SiContext.People
where p.Role.Any(x => x.Name.ToLower() == "barman")
select p).ToList();
You can try the order way around (assuming you have the reverse navigation property)
var listPerson = SiContext.Role.First(r => r.Name == "barman").People.ToList();

Get list of child records

I have a database that looks like this:
tbl_Seminar
ID
isActive
tbl_SeminarFees
ID
seminar_id -- foreign key
fee_text
I want to get all seminars that are active (isActive ==1) and a list of the fees associated with that seminar. Each Seminar may have n records in tbl_SeminarFees that are its fees. I am able to return a linq structure that returns me a list of objects that look like this {seminar, SeminarFee} but I wanted to create a nested structure that looks like this:
{seminar, List<SeminarFee>}
What should my linq query look like?
here is my linq currently:
var results = from s in context.Seminar
join p in context.SeminarFees on
s.ID equals p.SeminarID
where s.IsActive == 1
select new
{
Seminar = s,
Fees = p
};
How do I change this to get a list of these: {seminar, List<SeminarFee>}
Thanks
UPDATE
#lazyberezovsky gave me a good idea to use a group join and into another variable. But then how do I loop through the result set. Here is what I have now:
foreach (var seminarAndItsFeesObject in results)
{
//do something with the seminar object
//do something with the list of fees
}
This however gives me the following error:
Argument type 'SeminarFees' does not match the
corresponding member type
'System.Collections.Generic.IEnumerable`1[SeminarFees]'
What am I doing wrong?
Thanks
You can use group join which groups inner sequence items based on keys equality (a.k.a. join..into) to get all fees related to seminar:
var results = from s in context.Seminar
join f in context.SeminarFees on
s.ID equals f.SeminarID into fees // here
where s.IsActive == 1
select new
{
Seminar = s,
Fees = fees
};
You can't call ToList() on server side. But you can map results on client later.
BTW You can define navigation property Fees on Seminar object:
public virtual ICollection<SeminarFee> Fees { get; set; }
In this case you will be able load seminars with fees:
var results = context.Seminar.Include(s => s.Fees) // eager loading
.Where(s => s.IsActive == 1);
var results = from s in context.Seminar
join p in context.SeminarFees on s.ID equals p.SeminarID
where s.IsActive == 1
group p by s into grouped
select new {
Seminar = grouped.Key,
Fees = grouped.ToList()
};

Entity Framework Filtering On GrandChildren

Still pretty new to entity framework. So forgive me if this is a noob question. Hoping someone can shed some light on this.
I am trying to select data from 3 related tables.
Leagues -> Teams -> Rosters ->
The relationships are League.LeagueID => Team.LeagueID => Roster.TeamID
In the Roster table there is a PlayerID column
I need a query that can select all leagues where Roster has PlayerID = 1
I cannot seem to filter results on the grandchild record no matter what I try. Not finding too much on the internet either.
I have found a way to do this with anonymous types but those are read only so i can make changes to the data. I must be able to update the data after it returns.
db.Leagues.Where(l => l.Teams.Any(t => t.Roster.PlayerID == 1));
The SQL generated should get you what you want, even it looks unreadable ;)
If you want to specifically use inner joins to do this, you can do so with code like this:
from l in db.Leagues
join t in db.Teams on l.LeagueID equals t.LeagueID
join r in db.Rosters on t.TeamID equals r.TeamID
where r.PlayerID = 1
select l
UPDATE
To do with with eager loading the child associations use Include():
((from l in db.Leagues
join t in db.Teams on l.LeagueID equals t.LeagueID
join r in db.Rosters on t.TeamID equals r.TeamID
where r.PlayerID = 1
select l) as ObjectQuery<League>).Include(l => l.Teams.Select(t => t.Rosters))
db.Roasters.Where(r=>r.PlayerId ==1).Select(r=>r.Team).Select(t=>t.League).Distinct()
If Roaster has many teams and team has many leagues you can use .SelectMany instead of .Select
Example of .SelectMany from MSDN:
PetOwner[] petOwners =
{ new PetOwner { Name="Higa, Sidney",
Pets = new List<string>{ "Scruffy", "Sam" } },
new PetOwner { Name="Ashkenazi, Ronen",
Pets = new List<string>{ "Walker", "Sugar" } },
new PetOwner { Name="Price, Vernette",
Pets = new List<string>{ "Scratches", "Diesel" } } };
// Query using SelectMany().
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);

Linq To Entities

I have a small problem in my where clause in the linq expression below. If I put the number 3 instead of department.Id I get the desired result but when I use department.Id I get nothing in the resultset.
I also want to get a count for the number of filters for that filter name using the query again using distinct.
var dept = Page.RouteData.Values["department"];
var department = (from d in db.Departments
where d.Name.Replace(" ", "-") == dept
select new {d.Id, d.Name}).FirstOrDefault();
var query = from p in db.Products
join f in db.ProductFilters on p.Id equals f.ProductId into filters
from x in filters.Where(x => x.Product.DepartmentId == department.Id
/* if == 3 it works */)
select new { x.Name, x.Id };
Promoted to answer from comments:
Have you checked that the department instance is as you think it should be after the first linq statement - ie has an Id == 3?
Your first query is not finding any valid department and is therefore returning default which most probably means that departmend.Id == 0.

Categories