Using nested LINQ queries and collections of objects - c#

I am using a two LINQ queries(nested)
Here's what I want my query to achieve:
In my inner query
Retrieve a collection of UserLocation objects using two conditions
In my outer query
Retrieve a filtered collection of User objects where User.UID matches the property UserLocation.UID of each UserLocation object from the collection in the inner query.
I'm almost there code-wise, I'm just missing the final step -- I don't know how to get the outer query to enumerate through the UserLocation collection and match the UID.
In my code I have two queries, the top one is a working example of getting the FullName property of a User object using the inner query and the conditions I need(as well as matching UID).
The second query is the one I am having trouble with. What am I missing?
ownerLiteral.Text =
Users.First(u => u.UID.Equals(
UserLocations.First(s => s.IsOwner == true && s.LID.Equals(building.LID)).UID)).FullName;
var propertyteam =
Users.Where(c => c.UID.Equals(
UserLocations.Where(x => x.IsPropertyTeam == true && x.LID.Equals(building.LID))));
Edit: Fixed the problem
I had forgotten that UserLocations was a member of Users -- I shortened down my query and used .Any to select the UserLocations members that fit my conditions, then just return the User.
In the first query I return the FullName for the User object.
In the second query I now return a collection of User objects that fit the conditions.
For those that are interested I bind the second query to a DataList and then evaluate for their FullName in the user control.
ownerLiteral.Text =
Users.First(
u => u.UserLocations.Any(
ul => ul.IsOwner == true && ul.LID.Equals(building.LID))).FullName;
var propertyteam =
Users.Where(
u => u.UserLocations.Any(
ul => ul.IsPropertyTeam && ul.LID.Equals(building.LID)));

Your class relationships are confusing me, but I think your problem is that you're trying to treat a collection of UserLocation objects (Where() returns an IEnumerable) as a single UserLocation
I think this might do it:
var propertyteam = LoggedInUser.ExtranetUser.Company.Users
.Where(c => c.UID.IsPropertyTeam == true && c.UID.LID.Equals(building.LID));
Edit, based on further information:
So maybe this is what you're looking for?
var uidsWhoArePartOfThePropertyTeamForThisBuilding
= UserLocations.Where(x => x.IsPropertyTeam && x.LID == building.LID)
.Select(x => x.UID);
Assuming the UID member of a UserLocation is a whole User object, and not just some int ID for a User.

To write a T-SQL type IN clause, in LINQ you would use Contains. This would be where I would start:
var userlocations = LoggedInUser.ExtranetUser.UserLocations.Select(ul => ul.UID);
var propertyteam = LoggedInUser.ExtranetUser.Company.Users.Where(userlocations.Contains(u => u.UID));

Related

Filtering objects by nested list property value

I have a data grid with a column (Quantity) that is bound to a nested list int type property as follows:
data.PackageData.Contents.FirstOrDefault().orderedQuantity
When user apply a filter object (FilterValue) to that column I have to filter data using IQueryable query. What I have tried was something like this.
query = query.Where(e => e.PackageData.Contents.FirstOrDefault().orderedQuantity.Equals((Int16)FilterValue))
but when I trying to fetch data, I get an error.
data = query.ToList();
The LINQ expression 'DbSet().Where(p => (int)EF.Property<List>(EF.Property(p, "PackageData"), "Contents").AsQueryable().Select(o => (int?)o.orderedQuantity).FirstOrDefault().Equals((int)__p_0))' could not be translated.
How can I achieve this using LINQ?
Update
I tried with following LINQ. but still I get an error.
var filteredQty = (Int16)filter.FilterValue;
query = query.Where(e => e.PackageData.Contents.Any(i => i.orderedQuantity.Equals(filteredQty)));
Try to create external variable, as:
var filterValueInt = (short)FilterValue;
var query = query.Where(e => e.PackageData.Contents
.FirstOrDefault().orderedQuantity == filterValueInt);
EF try to translate to SQL. In the simplest cases, as example:
LINQ:
var myUsers = Users
.Where(user => user.Id >= 5)
.Select(user => user.Name)
.ToList();
SQL:
SELECT Name FROM Users WHERE Id >= 5;
(The result in SQL is more complex than that in reality but that's the idea)
When EF won't be able to translate to SQL, I think there's no choice left but to go through IEnumerable (to run LINQ in memory)
var list = query.ToList()
var filterValueInt = (short)FilterValue;
list = list.Where(e => e.PackageData.Contents
.FirstOrDefault().orderedQuantity == filterValueInt)`
.ToList()

LINQ: Is there a way to combine these queries into one?

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.

Linq to NHibernate: how to get a list of child objects without having the lists nested

I have a Linq to NHibernate query as follows:
var profile =
from UserProfile up in _Session.Query<UserProfile>()
.Fetch(x=>x.Messages)
where up.UserName == userName
select up.Messages;
this returns an IQueryable<IList<UserMessage>> which I then have to run a SelectMany() on. I'd prefer if I could just return an IQueryable<UserMessage> object instead, especially as the query will never return more than one user profile. Can this be done, or am I stuck with the extra step?
If you map the other side of the navigation e.g have a UserProfile property on the UserMessage class, your can start from UserMessage:
var messages =
from UserMessage um in _Session.Query<UserMessage>()
where um.UserProfile.UserName == userName
select um;
Otherwise you need to use SelectMany() to get a flattened out list.
Could you query the messages table directly and use the reverse association?
IQueryable<Message> messages = ...;
var filtered = from m in messages
where m.UserProfile.UserName == userName
select m;
Also, if you're willing to forgo query syntax you could make this shorter with:
var profile = _Session.Query<UserProfile>()
.Where(up => up.UserName == userName)
.SelectMany(up => up.Messages);

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");

LINQ: Doing an order by!

i have some Linq to Entity code like so:
var tablearows = Context.TableB.Include("TableA").Where(c => c.TableBID == 1).Select(c => c.TableA).ToList();
So i'm returning the results of TableA with TableB.TableBID = 1
That's all good
Now how can I sort TableA by one of its column? There is a many to many relation ship between the two tables
I tried various ways with no look, for example
var tablearows = Context.TableB.Include("TableA").Where(c => c.TableBID == 1).Select(c => c.TableA).OrderBy(p => p.ColumnToSort).ToList();
In the above case when i type "p." i don't have access to the columns from TableA, presumably because it's a collection of TableA objects, not a single row
How about using SelectMany instead of Select :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.SelectMany(c => c.TableA)
.OrderBy(p => p.ColumnToSort)
.ToList();
EDIT :
The expression below returns collection of TableAs -every element of the collection is an instance of TableA collection not TableA instance- (that's why you can't get the properties of the TableA) :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.Select(c => c.TableA);
If we turn the Select to SelectMany, we get the result as one concatenated collection that includes elements :
var tablearows = Context.TableB.Include("TableB")
.Where(c => c.TableBID == 1)
.SelectMany(c => c.TableA);
Okay, so now I've taken on board that there's a many to many relationship, I think Canavar is right - you want a SelectMany.
Again, that's easier to see in a query expression:
var tableARows = from rowB in Context.TableB.Include("TableA")
where rowB.TableBID == 1
from rowA in rowB.TableA
orderby rowA.ColumnToSort
select rowA;
The reason it didn't work is that you've got a different result type. Previously, you were getting a type like:
List<EntitySet<TableA>>
(I don't know the exact type as I'm not a LINQ to Entities guy, but it would be something like that.)
Now we've flattened all those TableA rows into a single list:
List<TableA>
Now you can't order a sequence of sets by a single column within a row - but you can order a sequence of rows by a column. So basically your intuition in the question was right when you said "presumably because it's a collection of TableA objects, not a single row" - but it wasn't quite clear what you mean by "it".
Now, is that flattening actually appropriate for you? It means you no longer know which B contributed any particular A. Is there only actually one B involved here, so it doesn't matter? If so, there's another option which may even perform better (I really don't know, but you might like to look at the SQL generated in each case and profile it):
var tableARows = Context.TableB.Include("TableA")
.Where(b => b.TableBID == 1)
.Single()
.TableA.OrderBy(a => a.ColumnToSort)
.ToList();
Note that this will fail (or at least would in LINQ to Objects; I don't know exactly what will happen in entities) if there isn't a row in table B with an ID of 1. Basically it selects the single row, then selects all As associated with that row, and orders them.

Categories

Resources