Auto-generate a Web Admin Interface using LINQ to SQL

Like a lot of web programmers, I spent years using PHP to create web applications. One of the tools I frequently used in conjunction with PHP was phpMyAdmin . This helpful web-based tool allowed me to easily manage my MySQL databases via a web browser.

After reading Scott Guthrie’s “Binding UI using the ASP:LinqDataSource Control” article I became intrigued by the idea of building aphpMyAdmin-like inteface that could utlize any “LINQ to SQL Class”. In theory this seemed simple, in practice it involved a lot reflection .

I have a very early build of this running and have provided the code below. Comments are sprinkled throughout the code and explain what is happening. Here is how to create this:

Step 1: Create the ASPX page

Create the following ASPX file: ~/admin/LinqMyAdmin.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LinqMyAdmin.aspx.cs" Inherits="LinqMyAdmin" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""> 
<html xmlns="">  
<head runat="server">  
    <link href="./LinqMyAdmin.css" rel="Stylesheet" type="text/css" /> 
    <form id="form1" runat="server">  
        <div id="contentwrapper">  
        <div id="contentcolumn">  
            <asp:PlaceHolder ID="LinqPlaceHolder" EnableViewState="true" Visible="true" runat="server"></asp:PlaceHolder> 
            <h2 id="TableHeader" runat="server" /> 
                PageSize = "20" 
                AllowPaging = "true" 
                AllowSorting = "true" 
                EnableViewState = "true" 
                AutoGenerateColumns = "true" 
                AutoGenerateDeleteButton = "true" 
                AutoGenerateEditButton = "true" 
                <AlternatingRowStyle CssClass="odd" /> 
                <PagerStyle CssClass="pager" HorizontalAlign="Left" /> 
        <div id="leftcolumn">  
            <asp:Repeater ID="TablesRepeater" EnableViewState="false" runat="server">  
                    <li><a href="linqmyadmin.aspx?table=<%# Container.DataItem %>"><%# Container.DataItem %></a></li> 

Step 2: Create the Code-Behind

Create the following code-behind file: ~/admin/LinqMyAdmin.aspx.cs

using System;  
using System.Collections;  
using System.Data.Linq.Mapping;  
using System.Reflection;  
using System.Text.RegularExpressions;  
using System.Web;  
using System.Web.UI.WebControls;  
public partial class LinqMyAdmin : System.Web.UI.Page  
    // --------------------------------------------------------------------  
    // --------------------------------------------------------------------  
    string LinqSQLClass = "Intranet.Data.NorthwindDataContext, App_Code";  
    Type LinqContextType;  
    /// <summary>  
    /// Executed upon Page Load.  
    /// </summary>  
    protected void Page_Load(object sender, EventArgs e)  
        string TableName = Request.QueryString["table"];  
        // Display a list of available Tables  
        LinqContextType = System.Type.GetType(LinqSQLClass, true);  
        TablesRepeater.DataSource = GetAllTables(LinqContextType);  
        // If we have a "table" value in our querystring, then display the table.  
        if (TableName != null)  
    /// <summary>  
    /// Gets a list of all Tables found in a LinqContext  
    /// </summary>  
    /// <param name="_Type">The LinqContext Type</param>  
    /// <returns>list of table names.</returns>  
    private ArrayList GetAllTables(Type _Type)  
        ArrayList _Tables = new ArrayList();  
        PropertyInfo[] _AllProperties = _Type.GetProperties();  
        foreach (var _PropInfo in _AllProperties)  
            // LINQ Tables are Generics and will be declared as type System.Data.Linq.Table<TEntity>  
            // I worked a long time to create a generic Table Type comparison.  Nothing worked.  
            // So I ended up just converting the type to a string and searching for a pattern.  :(  
            if (Regex.IsMatch(_PropInfo.PropertyType.ToString(), @"^System.Data.Linq.Table") == true)  
        return _Tables;  
    /// <summary>  
    /// </summary>  
    /// <param name="TableName"></param>  
    private void DisplayTable(string TableName)  
        // We don't know the ContextTypeName or TableName in advance.  
        // Create a LinqDataSource control dynamically and add it to our  
        // PlaceHolder control.  
        LinqDataSource _linqSource = new LinqDataSource();  
        _linqSource.ID = "LinqSource";  
        _linqSource.ContextTypeName = LinqSQLClass;  
        _linqSource.TableName = TableName;  
        _linqSource.EnableInsert = true;  
        _linqSource.EnableDelete = true;  
        _linqSource.EnableUpdate = true;  
        _linqSource.AutoPage = true;  
        _linqSource.AutoSort = true;  
        _linqSource.EnableViewState = true;  
        // Fetch the Primary Keys associated with the current table.  
        string[] _ID = GetTableKeys(TableName);  
        // Attach our LinqDataSource to our DataGrid.  
        TableHeader.InnerHtml = "Table -&gt; " + TableName;  
        TableGrid.DataSourceID = "LinqSource";  
        TableGrid.DataKeyNames = _ID;  
    /// <summary>  
    /// Get all properties associated with our LinqContext Table.  
    /// </summary>  
    /// <param name="TableName">The TableName</param>  
    /// <returns>The properties associated with this table</returns>  
    private PropertyInfo[] GetTableProperties(string TableName)  
        // All we currently have is a table name; "Customers" (plural).  
        // What we want to fetch are the properties associated with the  
        // model that is associated with our table.  That model, example,  
        // would be called "Customer" (singular).    
        // We find the connection between table & model by using  
        // reflection on the table.  The table will have have a type  
        // of System.Data.Linq.Table<Customer>.  The "Customer Type" is   
        // retrievable by looking at the Generic Arguments.  
        //  Clear as mud?  Great, let's get started...  
        Type _TableType = LinqContextType.GetProperty(TableName).PropertyType;  
        Type _ModelType;  
        PropertyInfo[] _ReturnProperties = null;  
        // Make sure we have a Generic Type  
        if (_TableType.IsGenericType == true)  
            Type[] _GenericArgs = _TableType.GetGenericArguments();  
            // Ensure that we have some Generic Arguments  
            if (_GenericArgs.Length > 0)  
                // Our model type will be the first Arg.  
                _ModelType = _GenericArgs[0];  
                _ReturnProperties = _ModelType.GetProperties();  
        return _ReturnProperties;  
    /// <summary>  
    /// Gets the primary keys associated with a LinqContext Table  
    /// </summary>  
    /// <param name="TableName">The Table Name</param>  
    /// <returns>A list of primary keys associated with the table</returns>  
    private string[] GetTableKeys(string TableName)  
        ArrayList _Keys = new ArrayList();  
        bool _PrimaryKey = false;  
        // Loop through each property found in the Table Model.  
        foreach (var _PropInfo in GetTableProperties(TableName))  
            // The Primary Key flag is a Custom Attribute.  Get all Custom Attributes  
            // associated with the current property.  
            Object[] _Objects = _PropInfo.GetCustomAttributes(true);  
            // Reset our Primary Key flag.  
            _PrimaryKey = false;  
            // Loop through each Custom Attribute.  
            foreach (Object _obj in _Objects)  
                // The attribute we're looking for is "System.Data.Linq.Mapping.ColumnAttribute"  
                if (_obj.GetType() == typeof(System.Data.Linq.Mapping.ColumnAttribute))  
                    ColumnAttribute _Attribute = (ColumnAttribute)_obj;  
                    if (_Attribute.IsPrimaryKey == true)  
                        _PrimaryKey = true;  
            if (_PrimaryKey == true)  
        return _Keys.ToArray(typeof(string)) as string[];  

Step 3: Create the CSS

Create the following CSS file: ~/admin/LinqMyAdmin.css

body {  
    background-color: #FFFFFF;  
    font-size: 15px;  
#contentwrapper {  
    float: left;  
    width: 100%;  
#contentcolumn {  
    margin-left: 200px; /*Set left margin to LeftColumnWidth*/ 
#leftcolumn {  
    float: left;  
    width: 200px; /*Width of left column*/ 
    margin-left: -100%;  
#leftcolumn ul {  
    list-style: none;  
    margin-left: 5px;  
    padding-left: 0px;  
#leftcolumn li   
    padding-left: 0px;  
    margin-left: 0px;  
    margin-bottom: 5px;  
h2 {  
    font-family: Arial;  
a:link {  
a:visited {  
a:hover, a:focus {  
    text-decoration: underline;  
table {  
    border: solid 1px #e5eff8;  
th {  
    font-weight: normal;  
    text-align: center;  
    border: solid 1px #e5eff8;  
    padding:.3em 1em;  
thead th {  
    text-align: center;  
tr.odd td {  
    background: #f7fbff 
td {  
    color: #678197;  
    border: solid 1px #e5eff8;  
    padding:.3em 1em;  
    text-align: center;  
.pager td {  
    text-align: left;  

Access the page via the following URL:


I would not recommend putting this code on a public server or using production data. If you do happen to upload this code to a public server, be sure to secure access to the page.

Obviously this web application lacks a lot of functionality. What is amazing though is how much functionality I am getting “for free” because of ASP.NET’s LinqDataSource and GridView. However, I find that I’m reaching the limit of what I can do dynamically. For example, weaving automatic foreign-key mappings into this code is proving difficult. I have some thoughts on how to overcome this, but have not yet found the time to experiment.

I hope to provide more updates later. Feedback is welcome!