Performing a LINQ Union on two anonymous type collections with C#

Written on

In my last post I found out that you can bind anonymous types to repeaters in C#, but there's a lot of other interesting things you can do with anonymous types, such as performing a LINQ Union operation on two anonymous type collections. I'll show how this is possible using a simple example.

Firstly, let's say that you have created a web application where people can use their existing facebook or twitter account to log on to your website. In a slightly naive example we will first get a list of users from facebook, and another list from twitter, and join them together into one user list and then work with them.

XDocument facebookXml = GetFacebookUsers();

var facebookQuery = from user in facebookXml.Descendants("FacebookUser")
select new
{
Username = (string) user.Element("Username"),
Firstname = (string) user.Element("Firstname"),
Lastname = (string) user.Element("Lastname"),
Birthday = (DateTime) user.Element("Birthday"),
Country = (string) user.Element("CountryName")
};

XDocument twitterXml = GetTwitterUsers();

var twitterQuery = from user in twitterXml.Descendants("TwitterUser")
select new
{
Username = (string) user.Element("Username"),
Firstname = (string) user.Element("Firstname"),
Lastname = (string) user.Element("Lastname"),
Birthday = (DateTime) user.Element("DOB"),
Country = (string) user.Element("Country")
};

On a side note, I'm casting the inner XElement objects to string, or DateTime; I prefer doing this to user.Element("Username").Value due to the fact that the end result is the same but we avoid the NullReferenceException that .Value may give us.

Although the two XML document's come from different sources and in fact have different structures and element names, the key thing here is that the anonymous type I have created in the facebookQuery has the same property names (and types) as the anonymous type I create in the second query (the twitterQuery). The .NET compiler is clever enough to realise that these two anonymous types are not two distinct anonymous types with the same properties, but are in fact the same anonymous type. The LINQ union set operator can obviously only be used on sets/collections of the same type, but that is exactly what we want to do next (the only difference being that we are allowing the compiler to create the type for us). Here is what we would want to do next:

var allUsers = facebookQuery.Union(twitterQuery);

Or most likely a more useful query like the one below:

var allUsers = facebookQuery.Union(twitterQuery)
.Distinct()
.Where(u => u.Country == "Norway");

One thing to note is if we add another property to the first anonymous type, for example a new string property called MobileNumber, the anonymous types will now be different (i.e. they will be two distinct anonymous types) and we will not be able to use the LINQ Union operation any longer:

var facebookQuery = from user in facebookXml.Descendants("FacebookUser")
select new
{
Username = (string) user.Element("Username"),
Firstname = (string) user.Element("Firstname"),
Lastname = (string) user.Element("Lastname"),
Birthday = (DateTime) user.Element("Birthday"),
Country = (string) user.Element("CountryName"),
MobileNumber = (string) user.Element("Mobile")
};

var twitterQuery = from user in twitterXml.Descendants("TwitterUser")
select new
{
Username = (string) user.Element("Username"),
Firstname = (string) user.Element("Firstname"),
Lastname = (string) user.Element("Lastname"),
Birthday = (DateTime) user.Element("DOB"),
Country = (string) user.Element("Country")
};

// This will result in a compile time error
var allUsers = facebookQuery.Union(twitterQuery);

Comments