April 30, 2008
@ 01:41 PM

One of the best things about being a programmer is new technologies,  taking a look at exciting new approaches to everyday tasks.  The .NET world is continually growing, in fact it was only 6-months ago that the 3.5 Framework was released giving you new tools such as LINQ, WPF, and Silverlight.  Now comes ASP.NET 3.5 Extensions Preview and you can add to the list Dynamic Data Controls. Dynamic Data Controls will enable you to build data driven website's that will work against a LINQ to SQL and LINQ to Entities object model.  With a fully functional website built on top of the MVC framework you can optionally override or customize any of the view templates giving you full control over our website.

Dynamic data works with the standard data controls enabling them to automatically support foreign key relationships,  giving you a friendly name display of foreign key values along with built in UI validation support based upon the constraints set in your data-model.

Once you download the new Dynamic Data controls (you can down load them from here ) unzip them to your favorite directory.  Inside the directory you will find a series of DLL's, Zip files, CMD files and a readme.  Inside the readme you will find instructions on setting up your environment and your first project of which both of these we will go over in this blog.  So now its time to get to the fun.

image

For this example you will need

Visual Studio 2008
SQL Express
ASP.NET 3.5 Extensions Preview
Northwind database

The examples here will be in VB.NET, however you can use C# if you so desire.

Installation

First you need to install the Dynamic Data controls.  To do this you can either open up a command window and navigate to the directory in which you unzipped the files, or you can simply double click on the appropriate one to install depending upon your system.  You will notice that there is support for both 32-bit and 64-bit operating systems.

Your first Project

Launch Visual Studio 2008, and click on File > New > Website and choose Dynamic Data Website (Preview).

imageA

Directory Structure:
You will notice that you have a pre-built website complete with a Dynamic Data directory structure.  The key area's I want to point out here are the CustomPages, FieldTemplates, and PageTemplates directories. 

imageB

FieldTemplates:These are the templates which are used to render each of the field types with in your datamodel.  You will notice the standard data types such as Boolean, DateTime, Integer, and Text.  You should also notice templates to handle the rendering of ForeignKey and Children relationships.  What you should also notice is that the templates for Image and XML data types are missing.  As of this release they are not installed by default; however you can create your own templates to control the rendering of these datatypes.  You can also override/customize the existing templates depending upon your needs.

PageTemplates:These are the default page templates to display / edit the data. 

CustomPages:This is where you will put any custom pages or override any pages in the default PageTemplates directory.

Data Model:

Add your database to your project.

Add a LINQ to SQL Class

imageC

Add the desired tables to your datacontext.

imageD

Now its time for the magic.  Open your global.asax and uncomment the line:

'    model.RegisterContext(GetType(YourDatacontext), New ContextConfiguration() With {.ScaffoldAllTables = False})

Then replace "YourDatacontext" with the name of the datacontext we just created and set ScaffoldAllTables = True.  Setting this value will build the dynamic pages based upon the data model.

   1: <%@ Application Language="VB" %>
   2: <%@ Import Namespace="System.Web.Routing" %>
   3: <%@ Import Namespace="System.Web.DynamicData" %>
   4:  
   5: <script RunAt="server">
   6: Public Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
   7:     Dim model As New MetaModel
   8:  
   9:     ' Uncomment this line to register your data context with the Dynamic Data engine.
  10:     ' Only set ScaffoldAllTables = true if you are sure that you want all your tables
  11:     ' to support a scaffold (i.e. templated) view.
  12:         model.RegisterContext(GetType(NorthwindDataContext), New ContextConfiguration() With {.ScaffoldAllTables = True})
  13:  
  14:     routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _
  15:         .Action = PageAction.List, _
  16:         .ViewName = "ListDetails", _
  17:         .Model = model})
  18:  
  19:     routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _
  20:         .Action = PageAction.Details, _
  21:         .ViewName = "ListDetails", _
  22:         .Model = model})
  23:  
  24:     'routes.Add(New DynamicDataRoute("{table}/{action}.aspx") With { _
  25:     '    .Constraints = New RouteValueDictionary(New With {.Action = "List|Details|Edit|Insert"}),
  26:     '    .Model = model})
  27: End Sub

Now run the site.

imageE

Drilling down to the Product details you will see that the friendly names for the categories are in both the drop down list, and the grid view.  You will also notice the ability to edit and delete.

So now go off and have some fun with this.  Next time we will look at adding support for the Image data type based upon Scott Hunter's sample and building our own custom pages.

So until next time, have fun and I'll see ya on the flip side.

David Yancey


 
Categories: .NET | .NET 3.5 | DynamicData

Visual Studio 2008 and .NET 3.5 are finally out and with it we have some new "toys" to look at.  Today we are going to look at XLINQ and the new ListView control while we build a new RSS Reader.  Before we get started I want to take a brief at LINQ.

LINQ (Language Integrated Query) defines a set of operators used to query, filter, and project data.  While the data must be encapsulated as an object, its source can be an array, enumerable class, XML, relational database, or any third party data source.  Some of the operators we have will be familiar to SQL programmers such as Select, Where, Join, Concat, OrderBy, Disctinct, and Union.  We will look at the syntax in a bit.  For further reading on LINQ be sure to check out the LINQ Project at Microsoft.

To get started we are going to need:
.NET 3.5 Framework
Visual Studio 2008

So lets get started by opening our development environment VS2008 and create a new Web project. 
In this project we are going to add the following:
Rss.vb (Class file)
GetRss.vb (Class file)
rss-reader.ascx (User Control)

 

Rss.vb

Looking at the code below I want to talk mainly about the LINQ portion.  But before we get going on it lets take a brief look at XDocument. In LINQ to XML (XLINQ) we use XDocument or XElement to load the XML into an object for us to query.  XDocument uses XMLReader as its underlying object to read the XML document and thus requires an XMLReader, TextReader, or URI to be passed into XDocument.  Once we have it loaded we will access it through the Descendants Method and Element Method. 

As you look closer at the query you can see that we are loading the node "channel" into an Anonymous type . 

myFeed In feedSource.Descendants("channel")

By using an Anonymous type we don't have to define a class declaration of the type.  We can instead use this type inline and access the elements of the node(s) we loaded into it and set them to properties of our collection with out having to define the properties.

feedDescription = myFeed.Element("description").Value

We can also create a sub collection with in our query and return it as part of the object to later be processed.

feedItems = myFeed.Descendants("item")

We finally we are returning our query as IEnumerable object to later be bound to a Listview in our class control.

 

Public Class RSS
    Public Function Read(ByVal URLPath As String) As IEnumerable
        Dim dcNameSpace As XNamespace
        dcNameSpace = XNamespace.Get("http://purl.org/dc/elements/1.1/")
        Dim feedSource As XDocument
        feedSource = XDocument.Load(URLPath)

        Dim query = From myFeed In feedSource.Descendants("channel") _
                    Select feedTitle = myFeed.Element("title").Value, _
                            feedDescription = myFeed.Element("description").Value, _
                            feedLink = myFeed.Element("link").Value, _
                            feedAuthor = myFeed.Element(dcNameSpace + "creator").Value, _
                            feedItems = myFeed.Descendants("item")
        Return query
    End Function
End Class


GetRss.vb

This is a simple class.  We are passing in URLPath (URL to our Feed) to be processed.  As you can see we call the read method of our RSS object (our DAL) and return it to our class control.  This class file is our BLL.  It is in this layer we would apply any business rules needed.

Public Class GetRSS
    Private m_urlPath As String
    Public Function GetRSS() As IEnumerable
        Dim myRSS As New RSS


        Return myRSS.Read(m_urlPath)
    End Function

    Public Sub New()

    End Sub

    Public Sub New(ByVal UrlPath As String)
        m_urlPath = UrlPath
    End Sub
End Class

 

Rss-Reader.ascx

Now to the fun part of putting it all together.  First the code behind. 

Simply we set a property for the class to hold URLpath, and bind the object to the our list view in our page_load.  However we have another method in our codebhind that I'm calling XEval.  The problem here is the sub collection inside our IEnumerable object.  When we get the collection at this point we are finding 2 main property's exposed.  One is value, and the other is XML.  We will see how we use both of these when we look at the markup.  Briefly though Value gives us the value of the property, and XML gives us the XML markup of that same property.  Neither of which are queriable (that I was able to find).  So what we are doing in XEval is passing the XML property to this method, along with the Xpath we want to return the InnerText of that node.

Imports System.Xml
Partial Public Class rss_reader
    Inherits System.Web.UI.UserControl

    Private m_urlPath As String
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim myRSS As New GetRSS(m_urlPath)
        listview1.DataSource = myRSS.GetRSS
        listview1.DataBind()

    End Sub

    Public Function xEval(ByVal x As String, ByVal xPath As String) As String
        Dim xDocument As New XmlDocument
        xDocument.LoadXml(x)

        x = xDocument.SelectSingleNode(xPath).InnerText
        Return x
    End Function

    Public Property URLPath() As String
        Get
            Return Me.m_urlPath
        End Get
        Set(ByVal value As String)
            Me.m_urlPath = value
        End Set
    End Property

End Class

Now the markup.

Here I want to look at the ListView.  ListView's can be bound to an object that Inherits IEnumerable, SQLDatasource, LINQDatasource, XMLDatasource, and ObjectDatasource.  In our ListView we are using the LayoutTemplate which has a placeholder inside of a div and the ItemTemplate.  There are other templates available to us such as AlternateItemTemplate, HeaderTemplate, FooterTemplate, and EmptyItemTemplate.  Each of these templates will be rendered into the placeholder in the layout template.  So as you can see this gives us flexability in defining the look and feel of our control.  Scott Guthrie has a great blog entry giving more information on ListView's.  For mine here I'm a CollapsiblePanelExender control from Ajax to provide a bit of control and styling.

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="rss-reader.ascx.vb" Inherits="Sandbox.rss_reader" %>
<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>

<asp:ListView ID="listview1" runat="server">
            <LayoutTemplate>
                <div style="width: 1000px;">
                    <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
                </div>
            </LayoutTemplate>            
            <ItemTemplate>
                <div>
                    <asp:Hyperlink ID="hlin2" runat="server" style="font-weight: bold; font-size: 12pt; color: Black;" Text='<%# Eval("feedTitle") %>' NavigateUrl='<%# Eval("feedLink") %>' />
                </div>
                <div>    
                    <asp:Label ID="label1" runat="server" style="font-weight: bold; font-size: 12pt;" Text='<%# Eval("feedDescription") %>' />
                </div>
                <div><hr /></div>           
                <div>
                    <asp:ListView ID="listview2" runat="server"  DataSource='<%# Eval("feedItems") %>' >
                        <LayoutTemplate>
                            <div>
                                <asp:placeHolder ID="itemPlaceHolder" runat="server" />
                            </div>
                        </LayoutTemplate>
                        <ItemTemplate>
                            <div>
                                <div id="descriptControl" runat="server" style="height: 30px;">
                                <div style="float: left;">
                                    <asp:HyperLink ID="hlink1" runat="server" Text='<%# xEval(Eval("XML"), "/item/title") %>' NavigateUrl='<%# xEval(Eval("XML"), "/item/link") %>' />
                                </div>
                                <div style="float:right;">
                                    <asp:ImageButton ImageUrl="~/expand.jpg" ID="imgbtn" runat="server" />
                                </div>
                                </div>                                
                               <asp:Panel ID="panel1" runat="server" BorderStyle="Inset" BorderColor="#333333" BorderWidth="1">
                                    <div>
                                        <asp:Label ID="label2"  style="font-size: 9pt; font-style:italic; color: Gray;" runat="server" Text='<%# xEval(Eval("XML"), "/item/pubDate") %>' />
                                    </div>
                                    <div id="descriptDiv" runat="server" style="padding: 3px;">
                                        <asp:Label ID="label3"  style="font-size: 9pt;" runat="server" Text='<%# xEval(Eval("XML"), "/item/description") %>' />
                                    </div>
                                </asp:Panel>
                                <cc1:CollapsiblePanelExtender ID="CollapsiblePanelExtender1" runat="server" Collapsed="true" 
                                    TargetControlID="panel1" ExpandControlID="imgbtn" CollapseControlID="imgbtn"
                                     CollapsedImage="expand.jpg" ExpandedImage="collapse.jpg" SuppressPostBack="true">
                                </cc1:CollapsiblePanelExtender>
                            </div>         
                        </ItemTemplate>
                    </asp:ListView>
                </div>        
            </ItemTemplate>
        </asp:ListView>
And the final output:
 

Download Sourcecode

 

That's all for now.

 

Next time we will look at building an XML Document using XDocument and XElement.

 

Dave


 
Categories: .NET | .NET 3.5 | LINQ | XLINQ