Using LINQ To Filter By Object Type and Then Quantify
Using LINQ To Filter By Object Type and Then Quantify
Introduction
Every so often, I run across a case where LINQ comes in so handy that I just want to post a quick article sharing some particular usage of it. For this example, I was working on a web page that shows user roles and their permissions within my application. Below is a photo showing how the display looked before my added LINQ code.
My web application has other roles not listed in that image. This is why there are some permissions that none of the listed roles are approved for such as "Allow Access To Site/User/Role Maintenance". For this page, I wanted to omit any rows from the display where at least one of the roles wasn't approved for it. After all, these permissions were irrelevant for these roles. Displaying them would only confuse my users and invite support questions.
The data is being displayed in an ASP.NET GridView
that has been bound to a DataSet
. To omit the rows, I decided I would filter the DataSet
prior to assigning the GridView
's DataSource
property and calling its DataBind
method. Please notice that the first column of the GridView
will contain a string
for the description of the permission and each subsequent column will contain a bool
specifying if that role is approved for the permission. One thing not obvious from the image is that the number of roles is driven by the database and is not static
. If I add another role to my application, there will be another column added here and I wouldn't want to have to modify this code to accommodate the additional role.
Below is my code to call the method to filter the DataSet
. GetMyRoleData
is just a dummy method to acquire the needed data and I do not show it in this example. I then call the FilterRows
method that will delete all the rows from the DataSet
for which no role has approval for that row's permission. I then set the GridView
's DataSource
property and call the DataBind
method. Nothing of note so far.
DataSet ds = GetMyRoleData();
FilterRows(ds);
RoleComparisonView.DataSource = ds;
RoleComparisonView.DataBind();
Next comes my method to actually filter the unwanted rows. It would have been very easy to enumerate through each row, which I did, and then enumerate through each column of the row. If every column is set to false
, delete the row. Or, if any column is true
, don't delete the row. The latter would be more efficient because I could stop checking once the first column had a true
value.
But instead of enumerating through each column and checking its value, LINQ can make the code so much simpler. Why not just use the Any
operator? If any of the elements in the row's array of items is true
, then I don't want to delete the row. Alternatively, I could use the All
operator and make sure they are all set to false
, but again, this would be less efficient and require every item to be checked. Using the Any
operator instead of the All
operator allows the code to stop once an item is true
.
There is one last complication and that is the fact that the first column of each DataRow
is a string
for the permission description and not a bool
for a role. This can easily be overcome though with the OfType
operator. Just call the OfType
operator on the array of items, ItemArray
, and specify to only return elements of type bool
. It's just that simple. Here is the code.
private void FilterRows(DataSet ds)
{
bool someRoleHasPermission;
foreach (DataRow dr in ds.Tables[0].Rows)
{
someRoleHasPermission = dr.ItemArray.OfType<bool>().Any(b => b == true);
if (!someRoleHasPermission)
{
dr.Delete();
}
}
}
So, I enumerate through each DataRow
in the DataTable
's Rows
collection. I then call the OfType
operator on the row's ItemArray
to return only those items that are a bool
. Last, I call the Any
operator and provide a lambda expression returning true
if any of the items is set to true
. Very simple. I then call the DataRow
's Delete
method if I got a false
back from the LINQ query. Just like that, my permissions have been filtered. Here is one last image showing the GridView
after calling the FilterRows
method:
As you can see, LINQ can make so many chores just that much more simple.