Here's a quick overview of my relationships:
one Account has many AccountRoles
one AccountRole has many UserAccountRoles
many UserAccountRoles can have one User
What I need to get is a set of all Users for one Account.
How can I do this in LINQ?
Edit:
Here's what I've tried so far:
var dto = _context.Set<Model.Account>()
.Find(account.Id);
if (dto == null)
return null;
var userAccountRoleDTOs = dto.AccountRoles
.Select(ar => ar.UserAccountRoles);
var userDTOs = userAccountRoleDTOs
.Select(uar => uar.Select(uar2 => uar2.User));
return userDTOs;
userDTOs is a collection of collections at the return. It doesn't make much sense to me.
Since ultimately all you want is a flattened collection for the one user, you should be able to use SelectMany() to flatten the one-to-many relationship and then call Distinct() at the end:
var users = dto.AccountRoles
.SelectMany(ar => ar.UserAccountRoles.Select(uar => uar.User))
.Distinct();
Or, in query form
var users = (from ar in dto.AccountRoles
from uar in ar.UserAccountRoles
select uar.User)
.Distinct();
Or you can go the other way around:
from u in context.Users
where u.UserAccountRoles.Any(uar=>uar.AccountRole.Account.Id == someAccountId)
Related
I have a database that contains 3 tables:
Phones
PhoneListings
PhoneConditions
PhoneListings has a FK from the Phones table(PhoneID), and a FK from the Phone Conditions table(conditionID)
I am working on a function that adds a Phone Listing to the user's cart, and returns all of the necessary information for the user. The phone make and model are contained in the PHONES table, and the details about the Condition are contained in the PhoneConditions table.
Currently I am using 3 queries to obtain all the neccesary information. Is there a way to combine all of this into one query?
public ActionResult phoneAdd(int listingID, int qty)
{
ShoppingBasket myBasket = new ShoppingBasket();
string BasketID = myBasket.GetBasketID(this.HttpContext);
var PhoneListingQuery = (from x in myDB.phoneListings
where x.phonelistingID == listingID
select x).Single();
var PhoneCondition = myDB.phoneConditions
.Where(x => x.conditionID == PhoneListingQuery.phonelistingID).Single();
var PhoneDataQuery = (from ph in myDB.Phones
where ph.PhoneID == PhoneListingQuery.phonePageID
select ph).SingleOrDefault();
}
You could project the result into an anonymous class, or a Tuple, or even a custom shaped entity in a single line, however the overall database performance might not be any better:
var phoneObjects = myDB.phoneListings
.Where(pl => pl.phonelistingID == listingID)
.Select(pl => new
{
PhoneListingQuery = pl,
PhoneCondition = myDB.phoneConditions
.Single(pc => pc.conditionID == pl.phonelistingID),
PhoneDataQuery = myDB.Phones
.SingleOrDefault(ph => ph.PhoneID == pl.phonePageID)
})
.Single();
// Access phoneObjects.PhoneListingQuery / PhoneCondition / PhoneDataQuery as needed
There are also slightly more compact overloads of the LINQ Single and SingleOrDefault extensions which take a predicate as a parameter, which will help reduce the code slightly.
Edit
As an alternative to multiple retrievals from the ORM DbContext, or doing explicit manual Joins, if you set up navigation relationships between entities in your model via the navigable join keys (usually the Foreign Keys in the underlying tables), you can specify the depth of fetch with an eager load, using Include:
var phoneListingWithAssociations = myDB.phoneListings
.Include(pl => pl.PhoneConditions)
.Include(pl => pl.Phones)
.Single(pl => pl.phonelistingID == listingID);
Which will return the entity graph in phoneListingWithAssociations
(Assuming foreign keys PhoneListing.phonePageID => Phones.phoneId and
PhoneCondition.conditionID => PhoneListing.phonelistingID)
You should be able to pull it all in one query with join, I think.
But as pointed out you might not achieve alot of speed from this, as you are just picking the first match and then moving on, not really doing any inner comparisons.
If you know there exist atleast one data point in each table then you might aswell pull all at the same time. if not then waiting with the "sub queries" is nice as done by StuartLC.
var Phone = (from a in myDB.phoneListings
join b in myDB.phoneConditions on a.phonelistingID equals b.conditionID
join c in ph in myDB.Phones on a.phonePageID equals c.PhoneID
where
a.phonelistingID == listingID
select new {
Listing = a,
Condition = b,
Data = c
}).FirstOrDefault();
FirstOrDefault because single throws error if there exists more than one element.
I am trying to do something simple, but I can't figure it out. Using EF6, I have 2 tables which are associated by an intermediate associative table, giving a many-to-many relationship:
I want to query all users who belong to a list of families. So I get an array of families:
var db = new MyProjectEntities();
User user = GetUserById((int)HttpContext.Current.Session["CurrentUserId"]);
var families = db.Users.Where(u => u.UserId == user.UserId).First().Families.ToArray();
Then I want to query all users belonging to these families:
var users = db.Users.Where(u => families.Contains(u.Families));
But I get this error:
Instance argument: cannot convert from 'Database.Family[]' to 'System.Linq.IQueryable>'
Thanks in advance.
You can use Any method:
var users = db.Users.Where(u => u.Families.Any(fam => families.Contains(fam))).AsEnumerable();
I think this is what you're searching for.
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)
I currently a list of a Supplier class, within that supplier class is a list of orders.
Each order has a userID and an empty string variable for username.
I then have a list of users which contains userID and username.
The way I am doing this now is:
foreach(supplier s in SupplierList)
{
foreach (order o in s.childorders)
{
user u = _users.First(p => p.userid == o.userid);
o.username = u.username;
}
}
I feel this might be a little inefficient and I was wondering if it is possible to compact it down into one linq query?
The logic should be
set supplierslist.childorders.username to the value in _users where supplierslist.childorders.userid == _users.userid.
Im fairly new to Linq so any advice for this would be apreciated, or also if its a bad idea and to leave it as it is / reasons why would be good too.
Thanks
What you want to do here is iterate over a collection (many collections, really, but it doesn't make a difference) and mutate its members. LINQ is not really targeted at performing mutating operations but rather at querying. You can do it with LINQ, but it's against the spirit of the tool.
If you are constructing the SupplierList yourself, it might be possible to fetch the data appropriately with LINQ so that it comes pre-populated as you want it to be.
Otherwise, I 'd leave the foreach as it is. You can make a dictionary that maps ids to users to make the inner loop faster, but that's your call and it depends on your data size.
var orderUserPairs = SupplierList
.SelectMany(s => s.ChildOrders)
.Join(_users, o => o.UserId, u => u.userId, (Order, User) => new {Order, User});
foreach (var orderUserPair in orderUserPairs)
orderUserPair.Order.username = orderUserPair.User.username;
Though having both username and userId as part of order looks suspicious.
First a question...
It looks like you are operating on every order. Why do you need to cycle through the supplierlist first since you don't seem to be using it inside the loop? Unless there are orders that don't belong to any supplierlist, you might be able to skip that step.
If that isn't the case, then I think you can use a join. If you aren't familiar with the syntax for joins in linq, this is one (simplified) way to approach it:
var x = from S in SupplierList
join C in childorders on C.supplierlistID equals S.ID
where [whatever you need here if anything]
select new { field1, field2};
foreach var y in x
{
}
Note I assumed a foreign key in childorders to supplierlist. If that isn't the case you will have to modify accordingly.
Hope that helps.
You need to use SelectMany or join depending on weather you are using linq-to-sql or linq with local collections. If you are using local collections the better way is to use join, else use SelectMany.
Like this...join:
var selection = (from s in SupplierList
join o in s.childholders on s.userid equals o.userid
select new { username = o.username);
or, in case of linq-to-sql:
var selection = (from s in SupplierList
from o in s.childholders
select { username = o.username);
You can then use the anonymous type you projected the way you want.
I agree with Jon, but you could say:
var orders = (from s in supplier
from o in s.childorders
select new
{
Order = o,
User = _users.First(p => p.userid == o.userid)
}).ToList();
foreach(var order in orders) {
order.Order.username = order.User.username;
}
Untested of course :)
If users list contains many elements, it can be really slow so I'd use a temporary dictionary:
var userById = users.GroupBy(x => x.userid)
.ToDictionary(x => x.Key, x => x.First());
foreach(var order in supplier.SelectMany(x => x.childorders))
{
order.username = userById[order.userid].username;
}
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.