I have been using LINQ for quite some time and have even had a few posts on LINQ. I was first exposed to LINQ at a .NET user group and my initial reaction was WOW now that is cool. Having a common language for querying data from XML, SQL, Oracle, ADO, and in memory objects, now that's the way to do it. There are even LINQ providers for TerraServer, Amazon and Flickr, just too name a few.
Lately as I am coding I always try to think LINQ. It's not easy as most of us are set in our ways. But we must change with the times. So I keep asking myself is there a LINQ way of doing this?
I recently needed access to the server variables exposed in the Request object. The server variables are stored in a NameValueCollection. Did I hear collection! If it's a collection then there most be a LINQ way handling these server variables.
Unfortunately, the NameValueCollection does not expose the name/value pairs as a collection of a single object but rather it has two collections; one for the keys and the other for the values. This does not lend itself well for use with LINQ. How can we adapt this NameValueCollection to be used within a LINQ query? This is where extensions come to the rescue. I decided to write an extension method ToEnumerable that would take the name/value pairs and package them into a custom type. The extension code is shown in Listing 1.
Listing 1: NameValueExtension
Imports Microsoft.VisualBasic
Imports System.Runtime.CompilerServices
Public Module NameValueCollectionExtension
<Extension()> _
Public Function ToEnumerable(ByVal nvCol As NameValueCollection) _
As IEnumerable(Of NameValue)
Dim item As NameValue
Dim listNV As New List(Of NameValue)
Dim i As Integer
For i = 0 To nvCol.Count - 1
item = New NameValue(nvCol.Keys(i), nvCol.Item(nvCol.Keys(i)))
listNV.Add(item)
Next
Return listNV.AsEnumerable
End Function
End Module
Public Class NameValue
Private _key As String
Public Property Key() As String
Get
Return _key
End Get
Set(ByVal value As String)
_key = value
End Set
End Property
Private _Value As String
Public Property Value() As String
Get
Return _Value
End Get
Set(ByVal value As String)
_Value = value
End Set
End Property
Public Sub New(ByVal key As String, ByVal val As String)
Me.Key = key
Me.Value = val
End Sub
End Class
This listing has a module, NameValueCollectionExtension, and a class, NameValue. The NameValue class has two properties; Key and Value and a constructor used to conveniently instantiate the object. The extension method, ToEnumerable, takes the NameValueCollection and loops through, extracting the key and value. The key and value is used to create an instance of NameValue object and is added to a List(Of NameValue).
Listing 2 shows how this extension method can be used in the context of Request.ServerVariables.
Listing 2: Using LINQ on Request.ServerVariables
Imports System.Data
Imports NameValueCollectionExtension
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
GridView1.DataSource = _
From ServerVariables In Request.ServerVariables.ToEnumerable _
Where (ServerVariables.Key.Contains("HTTP"))
GridView1.DataBind()
End Sub
End Class
This listing does not contain the markup as it only has a GridView. With the addition of this extension method we can now use Request.ServerVariables in a LINQ query. It is relatively simple to filter, order or even join Request.ServerVariables. In the above code listing all server variables where the key has the string HTTP is included. The result is bound to the GridView and is shown in Figure 1.
Figure 1: Request.ServerVariables Bound to GridView
One last point this extension method is not confined to Request.ServerVariables but can be used on any object of type NameValueCollection.
Thanks to Keith J. Farmer's comment on making use of the build-in generic KeyValuePair<TKey,TValue>. The revised extension method is shown below.
Imports Microsoft.VisualBasic
Imports System.Runtime.CompilerServices
Public Module NameValueCollectionExtension
<Extension()> _
Public Function AsEnumerable(ByVal nvCol As NameValueCollection) _
As IEnumerable(Of KeyValuePair(Of String, String))
Dim lnv As New List(Of KeyValuePair(Of String, String))
Dim i As Integer
For i = 0 To nvCol.Count - 1
lnv.Add(New KeyValuePair(Of String, String) _
(nvCol.Keys(i), nvCol.Item(nvCol.Keys(i))))
Next
Return lnv.AsEnumerable
End Function
End Module
Guess the movie
That really was a Hattori Hanzo sword.