Tuesday, August 31, 2010

Linq.Union Problem

Linq has created a lot of excitement in the developer communities and is a great tool for mixing the UI, Business Logic and the Infrastructure into a seriously entangled mess of unmaintainable code, if of course used improperly. So I am working hard on learning the "proper" way to use it.

Meanwhile, only yesterday I spent quite sometimes googling/binging around to find a way to apply aggregate filters dynamically to a query to no avail. Maybe this is something that I should put down as "not knowing the proper way of using Linq"!

Anyways, here's the use case, judge for yourself:

I have a list of suppliers and their residence States. I'd like to offer the user the ability to pull the list of suppliers who are based in a bunch of states of her own choice. i.e today she like to have the list of suppliers in CO, TX and CA. Tomorrow she may like to have the list of suppliers based in WA and CO.

If I were to create a fix function to get the supplier for CO, WA, TX and CA, the I would write something like this:



This works but it is not "Dynamic". Remember the users list of states changes everday. So I came up with the following solutions:



This is "Dynamic" (iterates through a list of states) but doesn't work!!

Here's the output... the middle states are missing!!

DynamicUnion Result
===Bad Results========
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

The problem seems to be the following statement:



For whatever reasons, this only remembers the suppliers for the first and the last states in the list. The ones in the middle are lost!! The syntax surely looks decent and "c-sharpish". It compiles too (but don't ship it yet). I am at a loss to explain this strange behavior. I shall tentatively dub it as the Linq.Union.Bug for now.

The work around is rather simple, just persist every set of records in a list as they are pulled from the data source. This should work except that it does not do the Union operation (as in set theory) properly, i.e. it doesn't get rid of repetition and you need to do some cleaning up before you present the list to the user.





Anyways, the whole program follows with the run output.



Run Output

As you will note in the Dynamic Results, TX and CA (the states between the first and last entries in the list) are eaten up by the "Union Bug"!

StaticUnion Result
===Good Results======
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier5 is based in TX
Supplier6 is based in TX
Supplier7 is based in TX
Supplier9 is based in TX
Supplier10 is based in TX
Supplier11 is based in CA
Supplier12 is based in CA
Supplier13 is based in CA
Supplier14 is based in CA
Supplier15 is based in CA
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

DynamicUnion Result
===Bad Results========
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

Dynamic WorkAround
===Good Results======
Supplier0 is based in CO
Supplier1 is based in CO
Supplier2 is based in CO
Supplier3 is based in CO
Supplier4 is based in CO
Supplier5 is based in TX
Supplier6 is based in TX
Supplier7 is based in TX
Supplier9 is based in TX
Supplier10 is based in TX
Supplier11 is based in CA
Supplier12 is based in CA
Supplier13 is based in CA
Supplier14 is based in CA
Supplier15 is based in CA
Supplier16 is based in WA
Supplier17 is based in WA
Supplier18 is based in WA
Supplier19 is based in WA
Supplier20 is based in WA

1 comment:

NasserO said...

Just a note that this is not a bug. This due to the Delayed Execution feature in Linq. To fix this, you just need to change q = q.Union(p); to q = q.Union(p).ToList <Supplier>();