15

I have a query pulling from a database:

List<myClass> items = new List<myClass>(from i in context
                      select new myClass
                      {
                          A = i.A,
                          B = "",    // i doesn't know this, this comes from elsewhere
                          C = i.C
                      }

I also have another query doing a similar thing:

List<myClass2> otherItems = new List<myClass2>(from j in context
                            select new myClass2
                            {
                                A = j.A,   // A is the intersection, there will only be 1 A here but many A's in items
                                B = j.B
                            }

In reality these classes are much larger and query data that is separated not only by database but by server as well. Is it possible to use a LINQ query to populate the property B for all items where items.A intersect? All of the built in LINQ predicates appear only to do aggregates, selections or bool expressions.

In my brain I had something like this, but this is all off:

items.Where(x => x.B = (otherItems.Where(z => z.A == x.A).Single().B));

Or am I being ridiculous with trying to make this work in LINQ and should just abandon it in favor of a for loop where the actual setting becomes trivial? Because of deadlines I will be resorting to the for loop (and it's probably going to end up being a lot more readable in the long run anyway), but is it possible to do this? Would an extension method be necessary to add a special predicate to allow this?

Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • I would say your for loop is a good implementation. – JonH Oct 21 '11 at 15:47
  • Could you show a simple data set that has the principal structure that you are looking for? What is that last query trying to achieve? – Tormod Oct 21 '11 at 23:11
  • @Tormod: The second query contains the `B` variable. I'm looking for a predicate or extension method that will perform the population of the `B` variable in the first collection. – Joel Etherton Oct 21 '11 at 23:46

2 Answers2

28

LINQ is designed for querying. If you're trying to set things, you should definitely use a loop (probably foreach). That doesn't mean you won't be able to use LINQ as part of that loop, but you shouldn't be trying to apply a side-effect within LINQ itself.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • A contradiction might be the usage of inserts, updates and deletes. This suggests that it might be designed for more than just querying. I can agree that the default predicates that come packaged within the framework are specific to querying only, but it makes me wonder if the application of a thorough extension method if it might be stretched beyond simple queries. I fully recognize the difference between "can" and "should". At this point, I'm exploring the possibility (and trying to fill gaps in my own LINQ knowledge). – Joel Etherton Oct 21 '11 at 15:49
  • @JoelEtherton: They're part of the additions for specific LINQ providers - they're not part of LINQ itself. Each LINQ provider is "the query part following the LINQ pattern" plus other parts which are provider-specific. – Jon Skeet Oct 21 '11 at 15:54
  • I would think something like this would fit within LINQ to Objects, but I can understand why it isn't a default implementation. So in your opinion, if I had a need for something like this that could justify the effort cost, it would be necessary (prudent?) to implement my own custom LINQ to MyNamespace provider? – Joel Etherton Oct 21 '11 at 15:58
  • 1
    @JoelEtherton: It doesn't fit within LINQ to Objects because LINQ is designed to be functional, without side-effects. It goes against the whole philosophy of LINQ. By all means write reusable methods for side-effects which may work *with* LINQ, but I wouldn't try to make them look like *part of* LINQ. – Jon Skeet Oct 21 '11 at 16:02
0

Query the OtherItems first. Do a ToDictionary() on the result. Then, when querying the database, do this:

var items = from i in context
   select new myClass 
   { A = i.A, 
     B = otherItems[i.A], 
     C = i.C
   } 
Tormod
  • 4,551
  • 2
  • 28
  • 50
  • LINQ does not allow the combination of data queries and in-memory queries. It'll compile but it will generate a runtime exception because local sequences can't be used in data contexts. To get the data query portion to work, it'd have to execute that and bring it to a local sequence and then perform this other select which would create a redundant query (something I would be using LINQ to avoid). – Joel Etherton Oct 23 '11 at 21:48
  • Right. The local closure wont translate through the expression tree into the LINQ Query provider. Well, then it depends on how you want the query to execute. If you want the query to be lazy evaluated, you risk hitting it many times when iterating. But I suppose you could do it with doing src.Select(remdata=>new{}).AsEnumerable().Select(localdata=>new{A=ld.a,B=otherItems[ld.a],C=ld.c}) – Tormod Oct 24 '11 at 06:54