Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

2012-09-29

LINQ to Objects: Querying Nested Collections

I love LINQ.  LINQ is just fun to use.  I love the fact that I can query the same way across databases, XML  documents and objects (alright XML is slightly different, but...)

Today I had the task of querying down a complex set of objects that each own a collection data member.  I needed to get to some data 4-layers deep and thought the LINQ query would get pretty nasty.  That is until I learned that I can use the "from" keyword multiple times in the same LINQ query.

My example will be very contrived to save having to look at a lot of extra code for different collections, so I'll go ahead and just nest the same collection several layers down, just so we get the concept.

I have a Person class:
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }


    public int IQ { get; set; }

    private List<Person> _children;
    public List<Person> Children
    {
        get{ return _children ?? (_children = new List<Person>()); } 
        set{ _children = value; }
    }
}
If I set up a new List<Person> like so:
  List<Person> grandpas = new List<Person>
  {
    new Person{
      FirstName = "John", LastName = "Doe", IQ = 91,
      Children = new List<Person>{ // children
        new Person{ FirstName = "Meh", LastName = "Doe", IQ = 103 },
        new Person{
          FirstName = "Tae Kwon", LastName = "Doe", IQ = 125,
          Children = new List<Person>{ // grandchildren
            new Person{ FirstName = "Karate", LastName = "Kid", IQ = 68, },
            new Person{ FirstName = "Karate", LastName = "Kid2", IQ = 117 },
          }
        },
      }
    },
 
    new Person{
      FirstName = "John", LastName = "Doh", IQ = 135,
      Children = new List<Person>{ // children
        new Person{ FirstName = "Meh", LastName = "Doh", IQ = 42 },
        new Person{
          FirstName = "Tae Kwon", LastName = "Doh", IQ = 140,
          Children = new List<Person>{ // grandchildren
            new Person{ FirstName = "Doh", LastName = "Boy", IQ = 111 },
            new Person{ FirstName = "Doh", LastName = "Nut", IQ = 98 },
          }
        },
      }
    },
  };
Check out how easy it is to get to data that is below the top layer:
IEnumerable<Person> grandChildrenWithHighIQ = from grandpa in grandpas from parent in grandpa.Children from grandkid in parent.Children where grandkid.IQ > 100 select grandkid;
EDIT: I was asked to provide a version with the fluent API as well:

IEnumerable grandChildrenWithHighIQ = grandpas .SelectMany(grandpa => grandpa.Children.SelectMany(parent => parent.Children)) .Where(grandchild => grandchild.IQ > 100);

Now we know longer need be afraid of querying complex objects and getting the data we want.  Write your queries in the bLINQ of an eye, and get your data back even faster!

And I'll put a shameless plug out there for LINQPad since I took a screenshot from its results above.  It is a fantastic tool for crafting LINQ queries and is a wonderful sandbox for small .NET proof-of-concept projects because you can write small programs without creating a Visual Studio project or solution.

Enjoy!

2008-11-14

Why We Must Use Classes Instead of Structs for Read/Write DataBinding in C#

Structs are Value Types.
Classes are Reference Types.

When we DataBind in C#, if we're using Value Types... we get copies of those objects instead of pointers to them, for that's how they are passed to the bound control. Therefore, you can't edit the bound control and expect the original objects to be modified. The bound control is only editing the copies and thus we encounter side effects when DataBinding a list or array of value types (like structs.)

I ran into this on a project I was working on where I needed to show a DataGridView for editing attributes. My attribute representation was a struct with a name property and value property... and when I bound a BindingList to the DataGridView, the list would appear... but when I'd edit the values would return to the old values... and when I'd add rows, the values would disappear.

The explanation above explains why. It didn't dawn on me until I started searching for "BindingList structs" and found Bill Wagner's "Of DataBinding and Value Types" (archive.org since the site is no longer available). The title tipped me off because I wasn't thinking about the fact that structs are value types.

Bill's article explains in more eloquent detail than I care to go into here, as I don't feel the need to re-write a well written essay on the topic. I only hope to provide a quick answer for those searching for it, that don't find Bill's article first (and leave myself a quick reference when I forget the next time I try to use a struct...)

He also mentions in his article when it is appropriate to use structs. Thanks for a well-done article, Bill.

So, reminder: use classes instead of structs when you need to do read/write binding. If you're merely databinding for a viewable list... use structs as you like.