Sunday, May 21, 2006

DLinq, Visual Basic 9.0, and ASP.NET 2.0 Issues

Most LINQ sample applications that I've seen to date are C# 3.0 console projects that write results to the command window. In my opinion, neither C# code nor console applications are optimum choices for demonstrating LINQ features and capabilities. The advantages of Visual Basic 9.0 as a LINQ programming language are well known—static typing where possible and dynamic typing where necessary. But today's perception of LINQ and its DLinq/XLinq components is primarily as advanced extensions—anonymous types, query comprehensions, lambda expressions, expression trees, and extension methods—to the C# language. According to the LINQ May 2006 CTP's "Getting Stated with Visual Basic 9.0" white paper, the goal of LINQ is "increasing productivity in data-intensive programming." VB is regarded as a highly productive language because of its ease of use and enhanced readability compared to C, C++, C# and Java. Thus all the articles, columns, and blog posts I write use VB rather than C#. Note: Paul Vick expands on the preceding topic in his "BASIC Principals" post, which links to John Montgomery's "Express 'Orcas' Principles" post. The latter article debates whether 'Orcas Express' should include the LINQ extensions. Console projects minimize the effort required to demonstrate basic LINQ, DLinq, and XLinq features and their coding techniques. But console projects lack interactivity and aren't suited to displaying substantial amounts of data, such as result sets from DLinq queries against large tables. So I use Windows forms with databound controls for most DLinq examples or text boxes for manipulating XML documents with XLinq. Here's a typical example of a Windows form with DataGridViews bound to a DLinq Data Source from a recent "LINQ Takes Shape in May CTP" article that I wrote for FTP Online (click image for larger version).

Web Forms, GridViews, and ObjectDataSources

Scott Guthrie's "Using LINQ with ASP.NET (Part 1)" post demonstrates that Web forms with GridView controls bound to IEnumerable(Of T) instances greatly enhance the learning experience compared to console projects. I would write more Web site solutions or Web application projects (WAP) if ASP.NET 2.0 offered a better infrastructure for LINQ demo projects. Unfortunately, ASP.NET 2.0 doesn't let you take advantange of new DLinq features, such as the DLinq Data Source designer, nor do GridView controls support bidirectional databinding or paging directly, which would require a LINQDataSource to replace the ObjectDataSource control.

Note: Scott Guthrie says "DLinq designer support for web projects will be coming in the future" in his comment to this post. You can, however, create a LINQ Class Library with a DLinq designer, and add the class library as a reference to a file-system Web site (as Scott suggested).

The following code in the Page_Load event handler populates a GridView control in a WAP with rows from a join of the Northwind Suppliers and Products entities:

Dim objProds = _
From s In dcNwind.Suppliers, _
  p In dcNwind.Products _
Where s.Country = "USA" AndAlso s.SupplierID = _
  CType(p.SupplierId, Integer) _
Select s.SupplierID, s.CompanyName, p.ProductID, _
  p.ProductName, p.QuantityPerUnit, p.UnitPrice _
Order By It.CompanyName, It.ProductName
gvProducts.DataSource = objProds

The preceding code in the Default.aspx page of a WAP generates the following simple report:

Note: The "LINQ + WAP == Coolness" post on Harry Pierson's DevHawk Weblog shows you how to edit a C# WebApplication.csproj file to specify the C# 3.0 compiler for a WAP. ("Change the Target Import element to import '$(ProgramFiles)\LINQ Preview\Misc\Linq.targets' instead of '$(MSBuildBinPath)\Microsoft.CSharp.targets').

Hector Cruz provides step-by-step instructions to set up WAP and specify the Visual Basic 9.0 compiler in a March 13, 2006 message from the LINQ Project General newsgroup's "Using VB LINQ in Web Applications" thread. Hector adds instructions for creating a user template for LINQ Web Applications in the next (March 14) message.

An issue with using the LINQ ASP.NET Web Site template for the file-system (Web site) model is setting the compiler version for dynamic Web page compilation. In this case, the Web site model uses the C# 3.0 compiler for pages in the project folder but apparently not for classes added to the ...\App_Code folder. Workarounds are pasting the DataContext class code into the Default.aspx page or adding a reference to a LINQ Class Library created from code generated by SQLMetal.exe or the DLinq Data Source Designer.

Any of these three workarounds causes this exception when invoking the gvProducts.DataBind() method of the preceding LINQ query code: "Member access 'System.Nullable`1[System.Int32] SupplierID' of 'DLinqDesignerLibrary.Product' not legal on type 'System.Data.DLinq.Table`1[DLinqDesignerLibrary.Product]." This exception doesn't occur in the WAP version of the project, as the preceding figure proves.

An alternative execution path executes the following query to verify that the DataContext object isn't the source of the problem:

Dim objCusts = From c In dcNwind.Customers _
  Where c.Country = "USA" _
  Select c.CustomerID, c.CompanyName, _
  c.ContactName, c.City, c.Region, _
  c.PostalCode, c.Phone
gvCusts.DataSource = objCusts

Here's the GridView populated by the simple alternative query:

The difference in behavior of identical DataContexts in WAPs and file-system Web Sites is a major issue; the source of the problem remains a mystery.

Problems with VB External (XmlMappingSource) XML Mapping Files

The May 2006 CTP modifies SQLMetal.exe to enable creating a class file without mapping attribute decorations and an XML document that supplies the mapping attributes. Here's the command-line syntax to generate an XML mapping file (NwindMap.xml) and a corresponding DataContext class (XmlMap.Northwind):

"\program files\linq preview\bin\sqlmetal"
/server:.\SQLEXPRESS /database:Northwind
/map:NwindMap.xml /namespace:XmlMap
/language:vb /code:Northwind.vb /pluralize
The C# version of the SampleQueries.sln project has an \101+ DLinq Queries\External Mapping node with a Load and Use an External Mapping sample, which executes as expected. The VB version doesn't have an External Mapping example. Here's the code that should instantiate the XmlMap.Northwind DataContext:
Private dcNwind As XmlMap.Northwind
Private Sub XMLMapping_Load(ByVal sender _
  As Object, ByVal e As System.EventArgs) _
  Handles Me.Load
  Dim strMapSource As String = _
    Application.StartupPath + "\NwindMap.xml"
  Dim xmsNwind As XmlMappingSource = _
    XmlMappingSource.FromXml(File. _
  dcNwind = New XmlMap.Northwind(My.Settings. _
    NorthwindConnection, xmsNwind)
End Sub

Executing the last instruction throws "An unhandled exception of type 'System.InvalidOperationException' occurred in System.Data.DLinq.dll. Additional information: The type 'DLinqXMLMapping.XmlMap.Customer' is not an entity."

Could this be the reason that VB version of Sample Queries doesn't have an External Mapping example?

P.S.: Mike Champion, Microsoft's XLinq program manager, is seeking "feedback from potential LINQ customers about what they like, what they don't like, and what more they need in a product before the design is frozen."

Technorati Tags: , , , , , , ,