tags:

views:

60

answers:

5

I have two IQueryable<> lists. Say one is filled with objects of Class Bottle and the other is of type Cup.

Both classes have a Guid called DrinkID.

How would I take my Bottle list and find all the items in the Cup list that have a DrinkID that is NOT in the Bottle list?

I am looking for something like this:

var bottles = _context.AllBottles;
var cups = _context.AllCups.Where(cup => cup.DrinkID not in bottles.DrinkID);

I am using Linq to Entities.

+1  A: 
cups.Where(c => !bottles.Any(b => c.DrinkID == b.DrinkID) )
Winston Smith
This is the straightforward way, but it might be quite inefficient if the collections are large.
driis
My collections are not large. This seems the simplest way. (Though I upvoted you driis because I have not seen ToLookup before and will need to research it.)
Vaccano
+2  A: 
var bottles = //....
var cups = // ....

bottleLookup = bottles.ToLookup(b => b.DrinkId);
var cupsNotAvailable = cups.Where(c => !bottleLookup.ContainsKey(c.DrinkId);
driis
+1 for `ToLookup`, but the last line should be `cups.Where`
tzaman
@tzaman, thanks for pointing that out (fixed ina answer).
driis
This doesn't have a translation in Linq-to-SQL, so wouldn't this force enumeration of all bottles to the client ( .ToLookup ), which might in fact be more inefficient than performing it in the database.
Winston Smith
@Winston, the question says nothing about L2S.
Jay
@Winston, you are right, assuming the scenarios is Linq-to-Sql, then another approach might be more effective. But in Linq-to-Xml or Linq-to-Objects (and other), this will be more effective. We need to know about the context to be sure.
driis
I am using linq to Entities (ADO entity framework). Is this still a good approach for that "flavor" of linq?
Vaccano
@Vaccano, I would suggest you check the SQL generated by both options to see whether you are satisfied with the results. This option probably will require fetch all bottles from the server, then use a fast lookup for the comparison. Whether that is a bad thing depends on the number of possible records, and the efficiency of the SQL generated by the (!Any) apprach. (Btw, this is a classic problem with abstractions - you leave a little of the fine grained control behind, so when in doubt, test).
driis
Rule of thumb: If you have less than 50 bottles, I'd hazard a guess that it won't matter a lot, which solution you choose.
driis
+1  A: 

First, define an IHasDrinkID interface (choose a better name), and have both classes implement it.

The define a class which implements 'IEqualityComparer' (say DrinkComparer).

Then it's just

var bottleless =  Cups.Except(Bottles, new DrinkComparer());
James Curran
Good answer (+1), but I'd say this depends on how the types are used. If it makes sense for the application, go ahead and add an interface so the two types can be considered equal. If it is just for a single or a few queries, don't pollute the domain model with an unneeded interface.
driis
A: 
// pull out the ids from the bottles first to prevent re-evaluating for every comparison
var bottle_drink_ids = BottleList.Select(b => b.DrinkID).ToList();
var cups_not_in_bottles_list = CupList.Where(c => !bottle_drink_ids.Contains(c.DrinkID));
Jay
+1  A: 

I recommend revisiting the design, and ensuring that the Entity mapping matches the hierarchy you really want to have.

From the wording of your example, I'm infering that all Bottles are Cups, but you are looking for a simple way of retrieving non-Bottle Cups. The fact that you need a collection of base objects that are not a particular type of derived object raises a red flag for the design.

If possible, clean up the design a bit: make Cups an abstract base type, and define other concrete type(s) derived from Cup to fill the gap. Say, Flask.

At that point, it's easy to have the EF mirror the hierarcy (using table-per-concrete-type mapping). And instead of saying you've got Bottles and non-Bottles, you've got Bottles and Flasks (and other types as necessary), with the Cups abstraction still available as the base class.

Stephen Cleary