Three Ways to Bind a DropDownList with LINQ and C# ASP .NET

modified

Introduction

With the latest release of C# ASP .NET 3.5, Visual Studio 2008, and LINQ, there is a whole new way of working with the data layer in C# ASP .NET web applications. Prior to LINQ, many developers would either generate or custom-code a data layer based upon the usage of SqlConnection, SqlCommand, and DataReader objects to manipulate data. With LINQ and LINQ to SQL, we no longer need to rely on the details of the connection objects and can instead begin thinking more abstractly by using the pre-generated LINQ business objects and the data access layer. In this manner, LINQ has brought a conformity to the data layer in ASP .NET web applications. However, as with any new technology, new architectures often arise to support them. As we continue on, you’ll see how to implement your own tiered architecture, specifically for populating datasource controls.

In this article, we’ll visit three ways to populate a DropDownList control by binding to a data source. We’ll cover the simple, the direct, and the 3-tier enterprise method.

Simple as a Text File

The first method for populating an ASP .NET DropDownList with LINQ is by indicating directly in the ASPX file itself, where the datasource is and what fields to bind to. We can do this in a .NET 3.5 web application through the use of the LinqDataSource control.

Binding with the LinqDataSource tag

1
2
3
<asp:DropDownList ID="lstItems" runat="server" DataTextField="Name" DataValueField="ItemId" DataSourceID="srcItems" />

<asp:LinqDataSource ID="srcItems" runat="server" ContextTypeName="DropDownBusinessObjects.Data.DataContext" TableName="Items" />

What we have above is a basic DropDownList control placed on the web page. Notice that we pre-set the DataTextField and DataValueField properties to indicate the fields we wish to display. We also included an additional property, called DataSourceID, which specifies the location of the data source to actually pull data from. In our case, the data source will be a LinqDataSource control.

The second tag is the LinqDataSource control, specific to .NET 3.5. Your web application must target the 3.5 framework in order to have this tag available. To use the LinqDataSource tag, you simply specify the ContextTypeName, which is the fully qualified name to your LINQ to SQL data context. You then specify the TableName within the context to pull the data from. LINQ will automatically pull the appropriate fields that you specified in the DropDownList tag.

What’s Good About Simple?

The LinqDataSource tag is a very straight-forward method for binding to a DropDownList. It requires no code-behind work. It’s easy to use, easy to understand, and extremely easy to update. In fact, since the code exists in the ASPX file itself, which is really just a glorified HTML file, we can easily make changes to the data binding source, while the web application is runnings, without having to recompile source code or DLLs. This can be an advantage if you may be frequently changing data sources or need this kind of control over the data.

What’s Bad About Simple?

While simple is always a good thing, it’s not always right. Depending on your project’s architecture, binding data directly to LINQ in your ASPX page may violate the tiered layers and break boundaries in your design. Worse than that, the tag itself doesn’t provide you with any type of business object that could be used elsewhere in the web application, such as passing to a web service, using in composition, design patterns, etc. While it was easy to insert the tag and bind to data, code reuse in this situation is limited.

The LINQ data context map used for these examples.

Writing Some Code with Direct LINQ Binding

The second way for binding a DropDownList to a LINQ to SQL table is through directly binding to the LINQ table itself. This is almost exactly the same as the simple example above, with the exception of leaving out the DataSourceID field, since we’ll be directly populating that ourselves, in the code-behind.

Binding directly to a LINQ table

<asp:DropDownList ID=”lstWeapons” runat=”server” DataTextField=”Name” DataValueField=”WeaponId” />

Our tag is just a simple DropDownList control with the text and value field names specified. The names should match those in the LINQ table you plan to bind to.

The difference between directly binding to LINQ and the LinqDataSource tag, is that now we need to write a little in the code-behind to bind the data. Specifically, you’ll need to add the following code to your web application’s Page_Load function.

1
2
3
4
5
6
7
8
9
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Binding directly to a LINQ business object.
lstWeapons.DataSource = CommonManager.GetWeapons();
lstWeapons.DataBind();
}
}

Assuming you have a separate manager tier for performing business tasks (outside of the user interface), we’ll call this the CommonManager, you would define the following function for GetWeapons().

1
2
3
4
5
6
7
8
9
10
public class CommonManager
{
    public static List<Weapon> GetWeapons()
    {
        using (DataContext context = new DataContext())
        {
            return context.Weapons.ToList();
        }
    }
}

Notice in the GetWeapons() function, all we’re really doing is calling the ToList() function on the Weapons table in LINQ. The context object, DataContext, is the name of the LINQ generated context, which contains our tables. This was added by LINQ to SQL during the generation process. Since the ToList() function returns a bindable collection, we can bind directly to the DataSource of the control and have the data display properly.

What’s Good About Direct Binding to LINQ?

Unlike the simple method, we’re actually writing some code-behind to pull the data from LINQ and display it in the control. This allows us more control over how the data is displayed, processed, and handled. For instance, we could easily include some pre-processing in the GetWeapons() function to insert a blank option before the data (if you wanted the DropDownList to show a blank item initially), or other data manipulation.

We’re also preserving a data tier in our architecture, namely the Business Logic tier. Instead of having the LINQ access code directly in the ASPX file, we’ve moved the logic into the second tier, leaving UI functionality strictly to the ASPX code-behind, and the LINQ play hidden in the tier.

What’s Bad About Direct Binding to LINQ?

While we’re getting closer to an enterprise architecture, we still only have two tiers (not including LINQ’s own tier), and we’re not actually using a business object. Instead, we’re using LINQ’s generated business objects and binding directly to these. In the case where you need more code reuse, such as the ability to pass around the business object, binding directly to LINQ prevents you from gaining this. You could certainly pass the LINQ object throughout your layers, but it may come with a price. The LINQ generated objects tend to be quite heavy, carrying connections and data manipulation functions with them. We would also need to define a GetObjects() function for every table we need to return to populate a DropDownList control.

Yet Another Way for the Enterprise in All of Us

The third way for binding a DropDownList to a LINQ to SQL table is through the use of custom light-weight business objects and reflection. There is a specific distinction between a light-weight business object and LINQ’s generated business objects. The light-weight object is custom made by the developer and it models the database table. However, it only contains the properties and business methods required for the application. The connections, connectivity functions, and other generated properties are left in LINQ’s objects.

While this method requires a little more coding to achieve, you’ll find that the flexibility you gain with code reuse can be well worth the time.

Binding through a light-weight business object

1
<asp:DropDownList ID="lstMonsters" runat="server" AssemblyName="DropDownBusinessObjects.Types.MonsterType, DropDownBusinessObjects.Types" />

Notice our ASP .NET tag is very similar to the above examples. We’re again using a basic DropDownList control, but this time we’ve actually left out the DataTextField and DataValueField tags. This is because we will actually programmtically set these in our code, in an automated fashion. The UI developer can simply ignore the ID and Value tags and continue on creating hundreds of DropDownList controls with ease. The only additional property we add to the control is a custom property called AssemblyName. This property points to the fully qualified name of the light-weight business object, followed by the assembly name.

That is,

1
<asp:DropDownList ID="lstMonsters" runat="server" AssemblyName="Fully.Qualified.Namespace.YourBusinessObject, Assembly.Name" />

If your web application will contain many data source controls, you can see how this tag can greatly increase your ability to swap in and out controls, change bindings, and forget about memorizing ID and Value field names in the database. But, we’ll need some code to make this work.

The Magic Starts with an Interface

To put together our enterprise-style LINQ data binding with light-weight business objects, we’re going to need a basic interface that our business objects, which will bind to a data source control, will implement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface IDataBindable
{
    List<NameValueType> ToList();
}

[Serializable]
public class NameValueType
{
    public string Name { get; set; }
    public string Value { get; set; }

    public NameValueType()
    {
    }

    public NameValueType(string name, string value)
    {
        Name = name;
        Value = value;
    }
}

What we’ve done is defined the interface to contain a single function ToList() which returns a list of NameValueTypes. The NameValueType class itself, is just a basic holder for an ID and Text field to display in the DropDownList. By having our business objects implement the ToList() function, we know that no matter what kind of business object we may be dealing with (any table in LINQ), we can always call ToList() to retrieve its list of data. We’ll also know that the ID and Text fields will always be Name and Value, as defined in the NameValueType. With this infomation, we can move on to using Reflection to create a generic way of binding our business objects to DropDownList controls, or really, any ListControl (the base class for DropDownList) for that matter.

Mixing in Our Light-Weight Business Object

With our interface defined, we can now create a light-weight business object. This class will be similar to LINQ to SQL’s generated table class, but it will leave out the heavier connections and data manipulation functionality. We’ll gain an object that we can easily re-use throughout the web application and even share with other applications through web services.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[Serializable]
public class MonsterType : IDataBindable
{
    public int MonsterId;
    public string Name;
    public int HP;
    public string Description;

    public MonsterType()
    {
    }

    public MonsterType(string name, int hp, string description)
    {
        Name = name;
        HP = hp;
        Description = description;
    }

    public MonsterType(int monsterId, string name, int hp, string description)
        : this(name, hp, description)
    {
        MonsterId = monsterId;
    }

    #region IDataBindable Members

    public List<NameValueType> ToList()
    {
        List<NameValueType> resultList = new List<NameValueType>();

        using (DataContext context = new DataContext())
        {
            List<Monster> itemList = context.Monsters.ToList();
            foreach (Monster item in itemList)
            {
                resultList.Add(new NameValueType(item.MonsterId.ToString(), item.Name));
            }
        }

        return resultList;
    }

    #endregion
}

Notice we simply define the database field names as properties of the business object. We have a few constructors for convenience. The important part of this object is where it implements IDataBindable and provides a body for the ToList() method. Notice in this method, we implement LINQ’s DataContext and call the LINQ to SQL table’s ToList() method to return a list of data items from the database. The list returned is in LINQ’s generated business object format, so we convert the results into a NameValueType object. This is how we can convert LINQ’s own generated class into a simple ID/Value type that our DropDownList can bind to.

A Touch of Reflection Goes a Long Way

The next ingredient is a function in our business logic tier which will take the AssemblyName property from our DropDownList control and call the ToList() method of the light-weight business object defined in the control’s tag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CommonManager
{
    public static void PopulateListControl(ListControl lstControl)
    {
        string assemblyName = lstControl.Attributes["AssemblyName"];

        // Find the class
        Type obj = Type.GetType(assemblyName);

        // Get it's constructor
        ConstructorInfo constructor = obj.GetConstructor(new Type[] { });

        // Invoke it's constructor, which returns an instance.
        IDataBindable createdObject = (IDataBindable)constructor.Invoke(null);

        // Call the interface's ToList() method and bind to the control's data source.
        lstControl.DataValueField = "Name";
        lstControl.DataTextField = "Value";
        lstControl.DataSource = createdObject.ToList();
        lstControl.DataBind();
    }
}

The magic here lies in reflection. We obtain the assembly name from the custom attribute tag that we’ve added to the control in the ASPX file. With this string, we can create the Type and invoke its constructor to get a physical object. We already know that the object implements IDataBindable (because you coded your light-weight business object that way), so we can cast it and call it’s ToList() function. We don’t have to worry about the details of LINQ to SQL or how the data is actually fetched. All we care is that we receive a list of items with a Name property and a Value property (kindly provided by the NameValueType object). From there, it’s a simple matter to set the field names for the control and bind.

We can glue this all together in the Page_Load function by calling the PopulateListControl function on our control.

1
2
3
4
5
6
7
8
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        // Binding with a light-weight business object.
        CommonManager.PopulateListControl(lstMonsters);
    }
}

What’s Good About the Business Object Method?

By using custom light-weight business objects, we’ve created a basic architecture for promoting code re-use and sharing the objects. We can easily compose, enhance, and extend new objects based upon existing ones. We can also implement a variety of design patterns using the objects. We’ve also hidden away the details of LINQ to SQL in the data-types tier, so that our user interface and business logic layer never know the details of where the data comes from, nor do they need to reference LINQ. They simply rely on the interface to return the data in the expected format. This can make the creation of many DropDownList controls very easy in the user interface (provided a little elbow grease is used while creating the business objects). The user interface also can ignore the details of determining what ID and Value columns to read from in the database, since this is already handled by our own business objects.

What’s Bad About the Business Object Method?

The obvious point is that it required more code. In our simple method, we only needed a single line added to our ASPX file. Now we need to create business objects for each different DropDownList data table, which obviously takes more time. Another point to take into consideration is performace and your architecture. Since we’re using reflection, there is an increase in performance, although this is minimal (it can also be minimized even further by caching the list data).

Conclusion

We’ve just seen three different ways for binding data to a DropDownList control, ranging from simple, to direct, to advanced. The simple method was easy to use and implement, but lacked code re-use and extensibility. The direct method brought us a little closer to extensions, but lacked true composition; it also tended to violate our data tiers. The advanced method divided our tiers evenly, keeping the user interface completely separate from LINQ’s data, but required a little more code to get there.

While each method for binding LINQ to SQL tables to a data source control carries its own pros and cons, your web applications architecture will ultimately define which one is right for you. Take a look at your own design and see how you can best suit your .NET application’s needs.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share