Showing posts with label Orcas Beta 1. Show all posts
Showing posts with label Orcas Beta 1. Show all posts

Monday, July 02, 2007

Rico Mariani's DLinq (LINQ to SQL) Performance Tips (Round 3)

While waiting for Rico to drop his other shoe and provide some Beta 2 LINQ to SQL performance numbers, it occurred to me that increasing the size of the object property (field) values would have an deleterious effect on LINQ to SQL performance ratios.

Update 7/6/2007: Rico dropped the other shoe yesterday at 5:30 PM: DLinq (Linq to SQL) Performance (Part 4). Matt Warren comments at LINQ to SQL: Rico drops the other shoe. There's not much more to say until we get Beta 2.

Update 7/16/2007: Prior to moving to his new position as Chief Architect of Visual Studio 2008, Rico summarized his LINQ to SQL performance tests with Beta 2 bits in his July 16, 2007 DLinq (Linq to SQL) Performance Part 5 post. Rico achieved 93% of the performance of the underlying SqlClient/SqlDataReader data provider with compiled LINQ to SQL query expressions that exaggerate LINQ overhead. I'll see if I can duplicate his results when Beta 2 becomes generally available in two to three weeks.

Per Row Costs

As noted in my updated response to Rico's part 2, his tests used a short-form version of the Northwind Orders table: Just the OrderID, CustomerID, EmployeeID, and ShippedDate fields, which total 26 bytes of SQL Server datatypes. The objects have an additional ShipCountry property that SELECT AVG(LEN(ShipCountry)) FROM Orders says is 5 Unicode characters or 10 bytes. So you don't need to look it up, here's a copy of the performance metrics for the lightweight objects with the 36-byte (average) load:


Rows

Base, s.
LINQ (Param), s. Param / Base LINQ (Compiled), s. Compiled / Base Param / Compiled
0 0.293 1.941 6.63 0.589 2.01 3.30
6 0.317 2.051 6.47 0.664 2.09 3.08
22 0.340 2.299 6.76 0.803 2.36 2.86
77 0.429 3.164 7.38 1.273 2.96 2.49
122 0.507 3.894 7.68 1.684 3.32 2.31

A reminder: Base is the time to create and populate 500 instances of the objects with an SqlDataReader from an SQL Server 2005 Express instance on the same machine as the test harness. The code adds one instance per row to a List<Order> to emulate populating LINQ to SQL's IEnumerable<Order>. (The C# test harness now has 330 lines of code.)The LINQ (Param) and LINQ (Compiled) are times to execute 500 conventional-parameterized and compiled-parameterized LINQ queries, respectively.

Following are execution times and performance ratios for objects created from the entire Orders table with rows that average 172 bytes. The LINQ (Param) and LINQ (Compiled) times had more scatter than those for the smaller objects, but the performance patterns are similar (and to be expected).


Rows

Base, s.
LINQ (Param), s. Param / Base LINQ (Compiled), s. Compiled / Base Param / Compiled
0 0.304 1.948 6.41 0.579 1.90 3.36
6 0.350 2.504 7.15 0.739 2.11 3.38
22 0.426 2.866 6.73 1.084 2.54 2.64
77 0.735 4.059 5.55 2.243 3.05 1.81
122 0.973 5.033 5.17 3.126 3.21 1.61

The moral of this story is that increasing the average width of the row (weight of the object) by a factor of 4.78 increases execution time of a compiled query by a factor of 1.13 for 6 rows to 1.86 for 122 rows. If you need high performance when querying heavyweight objects, you might consider adding a second object widget with only the fields you need to the designer. But don't fall victim to an "entity explosion" by creating a custom entity for every table with lengthy varchar or nvarchar fields.

Entity/Identity Management

Rico recommends providing a read-only DataContext type to improve performance. The way I read the tea leaves is that setting the DataContext.EnableObjectTracking property value to false makes the DataContext read-only. However, my tests show no measurable performance difference with object tracking on or off.

Data Binding

Rico didn't provide the source code for the "necessary helper for getting and storing the fields so that there is effectively straight line code calling the underlying provider," so I'm not sure what he had in mind. Perhaps SqlDataReader code similar to that for the base data?

Wednesday, June 27, 2007

Rico Mariani's DLinq Performance Tips (Part 2) and Compiled Queries

Rico Mariani continues his analysis of LINQ to SQL performance issues with a new DLinq (Linq to SQL) Performance (Part 2) post dated June 25, 2007 by recommending compiled LINQ query expressions.

LINQ to SQL query expressions take advantage of lazy evaluation. Your code doesn't hit the SQL Server instance until the the IEnumerable<T> sequence's iterator (foreach or For Each structure) requests the first row. However, before the iterator starts it's pass, the compiler builds an expression tree, which in turn generates the T-SQL statement, and sends it to the database. As Rico observes:

In many cases all that will be different from one invocation to another is a single integer filtering parameter.

The expression tree incorporates logic to send a parameterized query when the query expression contains one or more parameters in the Where clause. For example:

SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[ShippedDate]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[ShipCountry] = @p0
ORDER BY [t0].[OrderID] DESC
-- @p0: Input NVarChar (Size = 3; Prec = 0; Scale = 0) NOT NULL [USA]

SQL Server caches the query plan and uses the cached version for different parameter values. Thus the only option remaining to the developer for improving performance is to compile the query, which eliminates building the expression tree and generating the T-SQL on each execution. 

Compiling Parameterized LINQ Query Expressions

Rico provides an example of a query delegate type that actually will compile. The only item missing is the delegate function:

private IEnumerable<Order> GetOrderById(int orderId)
    return q(Northwinds, orderId);

The LINQ to SQL: .NET Language-Integrated Query for Relational Data white paper by Dinesh Kulkarni, Luca Bolognese, Matt Warren, Anders Hejlsberg, and Kit George (March 2007) is one of the few (about 7) hits you'll get on a search for compiledquery.compile. The white paper has a very brief section that covers the the CompiledQuery class and its Compile method for compiling parameterized query expressions with a single example that will compile but won't work more than once during a session.

Note: The paucity of posts about compiled LINQ queries is surprising.

A bug—presumably in a System.Data.Linq class—causes a null reference exception if you create new DataContext objects for successive executions of the compiled query during a session. The sample code for the function that invokes the query delegate type does exactly that (and has a variable name error, to boot):

public IEnumerable<Customer> GetCustomersByCity(string city)
{
   Northwind db = new Northwind();
   return Queries.CustomersByCity(myDb, city);
}

Delete the Northwind db = new Northwind(); instruction, change the erroneous  myDB argument to your DataContext variable and the sample code should work as expected. However, this is another example of untested sample code. The authors might not have found the multiple invocations bug, but certainly wouldn't have been able to compile the function with the wrong argument name.

Note: Anders Borum confirmed the bug and pointed out the source of the problem in an answer to my Problem with Reuse of Compiled LINQ to SQL Query in Beta 1 post in the LINQ Project General forum. Matt Warren confirmed that the bug was detected and fixed in the Beta 1 timeframe but didn't make it into the Beta 1 bits. It's fixed for the forthcoming Beta 2 drop.

Where's the Meat?

Rico didn't offer any performance numbers in his Part 2 post, possibly because the missing function prevented him from comparing the execution time of his compiled query with baseline SqlDataReader data. VB 9.0 doesn't support lambda expressions in VS 2008 Beta 1, so I ported my original VB test harness to C# 3.0, added code to generate a combo box of Northwind country names with the number of orders per country, and wrote the code to add a parameterized query option for LINQ and SqlDataReader queries, and a compiled query option for LINQ. Here's a screen capture of the test harness's form:

The combo box contains the name and count of orders from 21 distinct ShipCountry values from the Northwind Orders table. The count varies from 6 (Norway) to 122 (USA and Germany.) The  DataContext's Orders object has only the fields required for the query expression (the three fields of the original test harness and Rico's first test object) plus the ShipCountry field for the Where clause constraint.

Following is the performance penalty data for executing 500 parameterized and compiled queries with differing numbers of rows:


Rows

Base, s.
LINQ (Param), s. Param / Base LINQ (Compiled), s. Compiled / Base Param / Compiled
0 0.293 1.941 6.63 0.589 2.01 3.30
6 0.317 2.051 6.47 0.664 2.09 3.08
22 0.340 2.299 6.76 0.803 2.36 2.86
77 0.429 3.164 7.38 1.273 2.96 2.49
122 0.507 3.894 7.68 1.684 3.32 2.31

Base is the parameterized SqlDataReader execution time in seconds. Param/Base is the ratio of parameterized LINQ query execution time to the Base time. Compiled/Base is the ratio of compiled, parameterized LINQ query execution time to the Base time. Param/Compiled is the performance ratio of compiled-parameterized to parameterized LINQ queries.

Data: Rows contain 36 bytes of date: 2 integers (8 bytes), 1 5-character CustomerID (10 bytes), one DateTime field (8 bytes), and an nvarchar ShipCountry field that averages 5 characters (10 bytes) for a total of 36 bytes. The rows of the Part 1 tests contained an average of 26 bytes (no ShipCountry field).

Hardware: 2005-era, single core 2.26-GHz Pentium 4 Dell 400SC server with 1 GB RAM and 80-GB 7,200 RPM UltraATA-100 drive (IBM IC35L090AVV207-0, 2MB buffer, 8.8 ms. seek time).

Conclusions: With Beta 1 bits, LINQ to SQL parameterized queries are about 7 times slower than the baseline SqlDataReader; LINQ to SQL compiled queries are about 2.5 times slower than the baseline SqlDataReader. The LINQ to SQL query performance penalty increases for both types of LINQ to SQL queries as the number of rows returned increases. The performance benefit of compiled over parameterized LINQ queries decreases as the number of rows increases.

Note: The the 330% performance factor of compiled queries in this scenario doesn't match the 500% performance factor reported by Anders Borum, probably because of the masking effect of database access.

What About Compiled Query Caching?

Frans Bouma, the developer of the LLBLGen Pro object/relational mapping (O/RM) tool for .NET, raises in a comment the issue of compiled query caching between DataContext refreshes. My assumption is that the compiled query is cached independently of the DataContext, just as the database connection is independent of the DataContext and is closed once the object(s) of interest have been hydrated.

Matt Warren says in a reply to the Problem with Reuse of Compiled LINQ to SQL Query in Beta 1 post:

We kind of knew we needed something like compiled queries before we even started seriously looking at perf.  We already had experience with ObjectSpaces to tell us that an ORM would have translation overhead.  Usually that overhead is not a big deal for a client-app, but we figured we could do better on server apps where the same query gets executed over and over again.

Due to the Beta 1 bug, I can't test inter-DataContext caching currently. I'll see what I can find out and update the post later.

Jomo Fisher, a member of the LINQ to SQL team, subsequently confirmed my assumption about DataContext-independent compiled queries in a June 28. 2007 comment:

Compiled queries are not bound to a particular [DataContext]. An intended usage is to compile a query once and use it across many DataContexts.

Where's Beta 2?

I'm seeing increasing numbers of "beta 2 fixes that," "beta 2 is faster," "wait for beta 2" and "it's better in beta 2" comments from the LINQ to ADO.NET folks. Rico's added his comment to the first post in his series:

Some things made it into Beta 1 but the bulk of what I'm going to post in the next few days didn't happen until after. You'll first see it in Beta 2. [Emphasis added.]

and Matt Warren seconds Rico's motion in this comment to his LINQ to SQL: Learning to Crawl post about Rico's item: 

Major improvements coming. :-)

 Let's hope bug fixes are coming, too.

Mini-Connectionless DataContext Is Gone from VS 2008

The "mini-connectionless DataContext" that Matt Warren described in his April 12, 2007 post to the Update existing disconnected objects (dlinq) thread in the LINQ Project General forum won't be in VS 2008's LINQ to SQL implementation. Matt says in June 18 and 27, 2007 posts to the LINQ: Next CTP thread:

No, this feature will not be in the first release of LINQ to SQL.

It is only missing because of lack of time.  When I mentioned it, I was talking about our forward thinking and a sample I'm trying to put together.

It will be interesting to see how Matt proposes to run LINQ to SQL v1.0 in the middle tier as a WCF service. I expected to see this feature in Beta 2, so I've updated my Changes Coming to LINQ for SQL post of May 15, 2007 with the bad news.

Update 6/28/2007: Fixed major formatting problems caused by <pre> elements, minor typos, and added sections on caching and the missing mini-connectionless DataContext.

Update 6/29/2007: Matt Warren said Rico suspects I have a faster disk drive, so I corrected and updated the drive specs on the earlier post and added a hardware section here. Clarified the row size (in bytes) and DataContext specs, also.

Friday, June 22, 2007

Will Visual Basic 9.0 Have Collection Initializers?

For reasons unknown, all mention of collection initializers for Visual Basic 9.0 has disappeared. The disappearance seems to have occurred without comment from the VB team or VB users.

The September 2005 "Overview of Visual Basic 9.0" .doc file covered the topic briefly, but the current HTML Overview of Visual Basic 9.0 version of February 2007 discusses only object initializers and array initializers. VS 2008 Beta 1 online help has a "connection initializers (C#)" topic but no corresponding VB topic.

The 2005 Overview's section 1.3 "Object and Collection Initializers" says this about collection initializers:

As we have seen, object initializers are also convenient for creating collections of complex objects. Any collection that supports an Add method can be initialized using a collection initializer expression. For instance, given the declaration for cities as the partial class,

Partial Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Float 
  Public Property Latitude As Float
End Class

we can create a List(Of City) of capital cities of our example countries as follows:

Dim Capitals = New List(Of City){ _
  { .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Lattitude = -18.6 }, _
  { .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  { .Name = "Monaco", _
    .Country = "Monaco", _
    .Longtitude = 7.2, _
    .Latitude = 43.7 }, _
  { .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}

This example also uses nested object initial[iz]ers, where the constructors of the nested initializers are inferred from the context. In this case, each nested initializer is precisely equivalent to the full form New City{…}.

Note: The preceding excerpt also appears in the session of the same name made to the XML 2005 Conference & Exhibition held in Atlanta November 14-18, 2005. I doubt if that code worked in any VB 9.0 preview.

Neither the class nor the sample collection initializer will compile. It's one thing to change the syntax for a new feature, but another to use a defective class declaration in sample code.

The authors of the original version, Erick Meijer, Amanda Silver, and Paul Vick, imported and modified the class code and modified the List(Of City) code to utilize the current New Type With syntax in "Object and Array Initializers" of the February 2007 version:

As we have seen, object initializers are also convenient for creating collections of complex objects. Arrays can be initialized and the element types inferred by using an array initializer expression. For instance, given the declaration for cities as the class,

Partial Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Long 
  Public Property Latitude As Long
End Class

we can create an array of capital cities for our example countries as follows:

Dim Capitals = { _
  New City With { _
    .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Lattitude = -18.6 }, _
  New City With { _
    .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  New City With { _
    .Name = "Monaco", _
    .Country = "Monaco", _
    .Longtitude = 7.2, _
    .Latitude = 43.7 }, _
  New City With { _
    .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}

After fixing the class declaration by removing Property and changing Long to Float, the array initializer expression won't compile because there's a missing New City() type declaration in the first line, which should read Dim Capitals = New City(){ _. It's clear that no one tested the code before publishing the update.

Note: It's a shame that VB didn't adopt the Dim corollary to C#'s var arrayName = New[]{ ... } syntax, as Ralf Ehlert mentions in his "Problems with New Features of VB9" post in the Visual Studio VB Express Orcas forum.

The authors made no mention of VB 9.0 collection initializers, which were also missing from Jean-Marie Pirelli's "C# 3.0 and VB 9.0" session at Microsoft TechDays 07 March 27-28, 2007 in Geneva. Uncharacteristically, Jean-Marie provided both C# and VB examples, but he dispensed with VB examples when he reached the Collection Initializers slide. (Anders Hejlsberg and Jay Schmelzer received credit for the slides.)

It's interesting that Jeff Bramwell's Collection Initializer post in the Visual Basic Orcas forum didn't receive a reply from a Microsoft representative. The suggestion he received from Klaus Even Enevoldsen wasn't apropos collection initializers.

Note: My March 26, 2007 Updated "Overview of Visual Basic 9.0" Stealth Post lists other problems with the Overview reported by Jim Wooley. These errors relate to unimplemented VB 9.0 features scheduled for Beta 2.

There also were errors in the LINQ to SQL documentation that I reported in my April 26, 2007 More Issues with VB 9.0 Samples in the Updated LINQ to SQL Documentation post.

Update 6/24/2007: Added comment about data type error in Cities class declaration and corrected minor typos.

Update 6/25/2007: Bill McCarthy takes issue with my "It's clear that no one tested the code before publishing the update" statement about the second sample that uses the New Type With syntax in his VB9 and collection initializers post.

Regardless of syntax issues with the array initializer code, the class declaration has both syntax (Property) and data type (Long) errors.

Regarding the failure of the initializer code to compile, Bill says:

[I]t is clear the document is forward looking, and talking about how things will/should be.

So, given that, if we stop and analyze the syntax for array initializers it should be basically as presented. Perhaps one could argue the syntax should be Dim Capitals() = {.    It should also support the syntax you can use today in VS 2005, e.g: Dim ints() As Int32 = {1,2,3} , or as per the code Roger suggests Dim ints() As Int32 = New Int32(){1,2,3}

Okay so given the minimum it needs to support (current syntax), in VB9 we add anonymous types and inferred typing.  Inferred typing means we remove the "As XXX" part.  So this means the syntax Dim Capitals As City() becomes Dim Capitals and the Dim Capitals() As City syntax becomes  Dim Capitals().  Anonymous types means we can't define the type name. So New City() { would become New() { _ , and given that the existing syntax doesn't even require the New Int32(), the New() becomes superfluous because the set brackets { } define the array data.

IOW: the syntax as shown is as it should be. (IMO)

Explicitly typing the array members by adding New City With { ... syntax precludes the array being of an anonymous type or use of inferred typing and the Dim Capitals = New City(){ ... statement is required. I didn't propose Dim Capitals() As City = {...} or Dim Capitals As City() = New City(){...}  as inferred by Bill's "Dim ints() As Int32 = {1,2,3} , or as per the code Roger suggests Dim ints() As Int32 = New Int32(){1,2,3}" sentence.

You can create anonymously-typed array elements by substituting New With for New City With, but Dim Capitals() = {...} returns an array of type Object with Option Strict Off and won't compile with Option Strict On. If VB 9.0 finally supports the New() construct for anonymously typed arrays, the the preceding code should compile with Dim Capitals = New(){ _, but I don't believe that New(){ _ would be superfluous. Here are two C# array initializer examples that use new[]:

/* Array initializer with object initializers (array is inferred type LineItem) */

var LineItems = new[]
{
    new LineItem {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = "24  500-g bottles", UnitPrice = 15.55M, Discount = 0.0F},
    new LineItem {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = "12 1-kg cartons", UnitPrice = 20.2M, Discount = 0.1F},
    new LineItem {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = "24 1-kg bags", UnitPrice = 25.45M, Discount = 0.15F}
};

/* Array initializer with object initializers (array and elements are anonymous types) */

var LineItems2 = new[]
{
    new {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = "24 500-g bottles", UnitPrice = 15.55M, Discount = 0.0F},
    new {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = "12 1-kg cartons", UnitPrice = 20.2M, Discount = 0.1F},
    new {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = "24 1-kg bags", UnitPrice = 25.45M, Discount = 0.15F}
};

However, I haven't heard anything about VB 9.0 implementing New(). On the other hand, I'm not sure why I would want to create an anonymously typed array or what its use would be.

Sunday, May 20, 2007

LINQ to DataSets Rehabilitates VB's Bang Operator

Visual Basic's bang (!) operator (a.k.a. pling and officially the dictionary lookup operator) seldom appears in VB.NET code. The purpose of the bang operator (alias the bang separator) is to specify members of string-keyed collections, such as fields of an ADO.COM Recordset, as in:

rsCusts!CustomerID = "BOGUS"
rsCusts!CompanyName = "Bogus Software, Inc."
rsCusts!ContactName = "Joe Bogus"

The preceding shaves a few keystrokes from "parens-and-quote" syntax, which relies on Fields being the default property of a Recordset:

rsCusts("CustomerID") = "BOGUS"
rsCusts("CompanyName") = "Bogus Software, Inc."
rsCusts("ContactName") = "Joe Bogus"

which, in turn, is shorthand for the fully-qualified syntax:

rsCusts.Fields("CustomerID") = "BOGUS"
rsCusts.Fields("CompanyName") = "Bogus Software, Inc."
rsCusts.Fields("ContactName") = "Joe Bogus"

One reason for bang's disappearance is that ! got a bum rap from VB.COM and VBA developers because amateur coders (especially Access rookies) used bang in place of dot (.) to specify properties of other objects, such as forms and controls. Doing this kills IntelliSense and compile-time checking. In fact, ! fell into such disrepute that many developers, such as this "VB Expert," believed that VB.NET didn't support the bang operator. Some folks claimed that performance suffered when ! was tokenized to the paren-quote syntax, although I never found anyone who reported testing the performance hit.

Paul Vick's July 15, 2003 Dictionary Lookup Operator post explains how the bang operator works and, in a comment, it's equivalent in C#—x!y : x["y"].

C# Joins and Associations in Typed and Untyped DataSets

LINQ to DataSets is most likely to be used in place of DataViews where joins between related tables are required. (DataViews don't support joins or projections (SELECT lists), so you need LINQ to DataSets or a custom DataView class, such as the JoinView from MSDN, to accommodate them.) I needed to verify the differences in the syntax for LINQ to DataSet queries over typed or untyped multi-table DataSets that use joins or navigate associations and include value types (specifically DateTime) with nulls. Erick Thompson wrote a Nulls - LINQ to DataSets Part 3 treatise in his LINQ to DataSet Documentation Series but didn't mention nullable datatypes in conjunction with the added DataRow.Field<T> collection.

Here's the C# test query for the typed DataSet with joins, which returns nine rows with values from each of four related Northwind tables, including one null ShippedDate value to test null handling:

var query1 = from c in dsNwindT.Customers
             join o in dsNwindT.Orders on c.CustomerID equals o.CustomerID
             join d in dsNwindT.Order_Details on o.OrderID equals d.OrderID
             join p in dsNwindT.Products on d.ProductID equals p.ProductID
             where p.ProductID == 2 && c.Country == "USA"
             orderby o.OrderID descending
             select new { c.CustomerID, c.CompanyName, o.OrderID,
                          ShippedDate = o.Field<DateTime?>("ShippedDate"),
                          p.ProductName, d.Quantity };

With the exception of the code to supply the ShippedDate value (emphasized), the preceding query statement is identical to the corresponding LINQ to SQL query (just replace the dsNwindT DataSet with a DataContext.

You lose strong typing of related DataRows when you navigate associations (ChildRows or ParentRows) between related DataTables instead of joining the DataTables. This requires FieldName = DataTable.Field<DataType>("FieldName") expressions for non-nullable and FieldName = DataTable.Field<NullableType>("FieldName") expressions for nullable fields for all but the topmost table of the hierarchy:

var query2 = from c in dsNwindT.Customers
             from o in c.GetChildRows("FK_Orders_Customers")
             from d in o.GetChildRows("FK_Order_Details_Orders")
             from p in d.GetParentRows("FK_Order_Details_Products")
             where p.Field<int>("ProductID") == 2 && c.Country == "USA"
             orderby o.Field<int>("OrderID") descending
             select new {
                 c.CustomerID, c.CompanyName,
                 OrderID = o.Field<int>("OrderID"),
                 ShippedDate = o.Field<DateTime?>("ShippedDate"),
                 ProductName = p.Field<string>("ProductName"),
                 Quantity = d.Field<short>("Quantity") };

Join syntax for untyped DataSets becomes quite cumbersome because of the need to apply the AsEnumerable() method to each DataTable and DataTable.Field<DataType>("FieldName") to the joined fields:

var query3 = from c in dsNwindU.Tables["Customers"].AsEnumerable()
             join o in dsNwindU.Tables["Orders"].AsEnumerable() on
                 c.Field<string>("CustomerID") equals
                 o.Field<string>("CustomerID")
             join d in dsNwindU.Tables["Order_Details"].AsEnumerable() on
                 o.Field<int>("OrderID") equals d.Field<int>("OrderID")
             join p in dsNwindU.Tables["Products"].AsEnumerable() on
                 d.Field<int>("ProductID") equals p.Field<int>("ProductID")
             where p.Field<int>("ProductID") == 2 &&
                 c.Field<string>("Country") == "USA"
             orderby o.Field<int>("OrderID") descending
             select new {
                 OrderID = o.Field<int>("OrderID"),
                 ShippedDate = o.Field<DateTime?>("ShippedDate"),
                 ProductName = p.Field<string>("ProductName"),
                 Quantity = d.Field<short>("Quantity") };

The added typing for untyped DataSets with associations instead of joins (emphasized) isn't that great:

var query4 = from c in dsNwindU.Tables["Customers"].AsEnumerable()
             from o in c.GetChildRows("FK_Orders_Customers")
             from d in o.GetChildRows("FK_Order_Details_Orders")
             from p in d.GetParentRows("FK_Order_Details_Products")
             where p.Field<int>("ProductID") == 2 &&
                c.Field<string>("Country") == "USA"
             orderby o.Field<int>("OrderID") descending
             select new {
                 CustomerID = c.Field<string>("CustomerID"),
                 CompanyName = c.Field<string>("CompanyName"),
                 OrderID = o.Field<int>("OrderID"),
                 ShippedDate = o.Field<DateTime?>("ShippedDate"),
                 ProductName = p.Field<string>("ProductName"),
                 Quantity = d.Field<short>("Quantity") };

But the added typing probably would trouble C# purists who substitute lambda functions for enhanced query syntax because doing so saves a few characters.

VB Associations in Typed and Untyped DataSets

When skimming the sample queries in the "LINQ over DataSet for VB Developers" whitepaper for the May 2006 LINQ preview, I noticed extensive use of the bang operator in DataTable!FieldName expressions taking the place of FieldName = DataTable.Field<DataType>("FieldName") and FieldName = DataTable.Field<NullableType>("FieldName") expressions. Strangely, there's no mention of "bang" or "dictionary" in the nine-page document.

Unfortunately, Orcas Beta 1's VB compiler doesn't implement the Join keyword and joins using SQL-89 equi-join syntax are limited to two tables. Thus, my tests with VB are limited to navigating associations.

Here's the VB version of C# query2, which returns objects rather than designated data types for fields other than CustomerID and CompanyName:

Dim Query2 = From c In dsNwindT.Customers, _
             o In c.GetChildRows("FK_Orders_Customers"), _
             d In o.GetChildRows("FK_Order_Details_Orders"), _
             p In d.GetParentRows("FK_Order_Details_Products") _
             Where CInt(p!ProductID) = 2 AndAlso c!Country.ToString = "USA" _
             Order By o!OrderID Descending _
             Select c.CustomerID, c.CompanyName, o!OrderID, _
                 o!ShippedDate, p!ProductName, d!Quantity

Notice that no special treatment of the ShippedDate field is required to handle null values. However, if you attempt to strongly type ShippedDate with a CType(o!ShippedDate, Nullable(Of DateTime)) expression, a runtime error occurs when iterating the row with the null value.

And here's the VB version of C# query4, which returns objects for all fields:

Dim Query4 = From c In dsNwindU.Tables("Customers"), _
             o In c.GetChildRows("FK_Orders_Customers"), _
             d In o.GetChildRows("FK_Order_Details_Orders"), _
             p In d.GetParentRows("FK_Order_Details_Products") _
             Where CInt(p!ProductID) = 2 AndAlso c!Country.ToString = "USA" _
             Order By o!OrderID Descending _
             Select c!CustomerID, c!CompanyName, o!OrderID, _
                 o!ShippedDate, p!ProductName, d!Quantity

Notice that the AsEnumerable() method is missing from the DataTable identifier in the first line. The C# query won't compile without it but the VB compiler doesn't care if it's present or not.

I believe it's a good bet that VB's Join syntax for untyped DataSets in Beta 2 will look like this, which is considerably more concise than that of the C# version:

Dim Query4 = From c In dsNwindT.Customers
             Join o In dsNwindT.Orders On c!CustomerID Equals o!CustomerID
             Join d In dsNwindT.Order_Details On o!OrderID Equals d!OrderID
             Join p in dsNwindT.Products On d!ProductID Equals p!ProductID
             Where CInt(p!ProductID) = 2 AndAlso c!Country.ToString = "USA"
             Order By o!OrderID descending
             Select c!CustomerID, c!CompanyName, o!OrderID, o!ShippedDate,
                 p!ProductName, d!Quantity

Update 5/18/2007: According to VB Team member Amanda Silver, VB's Join syntax uses the Equals operator (equals in C#) instead of the = operator to make explicit that the expression uses Object.Equals as the comparison operator.

Conclusion: C# purists castigate VB for its verbosity and some condemn VB code as prolix. VB's syntax for LINQ queries against joined or associated tables is equally or more concise than that of C#.

Update 5/24/2007: Paul Vick points out in his Javascript in VB ... post that "the Silverlight-based Javascript compiler that we released in Silverlight 1.1" as well as the VBx compiler for VB 10.0 is written in VB. Miguel de Caza mentions in his Compiler Lab: Second Day post:

Their new DLR-based Javascript compiler (that implements the current ECMA spec) was written by two developers in four months. Nandan Prabhu was at the conference and he explained that if they were to rewrite it today it would probably take three months for a programmer to implement as such a person would not have to keep up with the daily changes in the DLR [Dynamic Language Runtime].

It will be interesting to see how the final implementation of VB's Join keyword handles null value types in strongly typed projections.

Update 5/22/2007: You'll need to use the FieldName = DataTable.Field<DataType>("FieldName", DataRowVersion.Version) overload of the Field method in both VB and C# if you want to filter by a member of the DataRowVersion enumeration: Current, Default, Original or Proposed. (The "LINQ to DataSet for C# Developers" whitepaper describes the DataRowVersion overloads, but doesn't include a sample of ther use. "LINQ to DataSet for VB Developers" doesn't even mention these overloads.)

Tuesday, May 15, 2007

Changes Coming to LINQ for SQL

Beta 2 will bring a substantial number of changes to Orcas's LINQ-related features. Following are a few of the forthcoming changes mentioned in the LINQ Project General and ADO.NET vNext forums.

Simplifying Data Tracking in n-Tier Projects

Matt Warren replies to questions in the very long LINQ Project General forum's Update Existing Disconnected Objects thread (4/12/2007):

There is more work coming that makes 3-tier scenarios easier. The [current] re-attach and playback API's are included because they are the bare minimum required to make any reconnecting work at all, however cumbersome it may seem at this time.

  • There is an upcoming feature that automatically databinds LINQ to SQL objects directly to webforms that use these API's to communicate changes back into the DataContext.
  • There is also a technology in the works that automatically serializes LINQ to SQL objects to other tiers, change tracks and data binds the objects for you on that tier and serializes the objects back, solving the cyclic reference serialization problem and the indescribable schema problem for objects with change sets; which seems to match what many of you are describing.

The mechanism that does the change tracking on the client is similar to a mini-connectionless DataContext. It is a type that packages up all the objects, lists of objects and graphs that you want to send to the client. It serializes itself and everything you've given it automatically. (It implements IXmlSerializable.)

  1. On the client, the same class also manages change-tracking for all the objects that were serialized with it.
  2. When serialized again (on the trip back) it serializes both the objects and their change information that it logged.
  3. Back on the middle tier, you just make one call to re-attach the whole package to a new DataContext instance and then call SubmitChanges.

[Formatting added.]

I'm looking forward to test-driving the mini-connectionless DataContext when Orcas Beta 2 arrives (sometime between July and September, according to Microsoft sources).

Update 6/28/2007: The "mini-connectionless DataContext" that Matt Warren described in his April 12, 2007 post to the Update existing disconnected objects (dlinq) thread in the LINQ Project General forum won't be in VS 2008's LINQ to SQL implementation. See Matt's June 18 and 27, 2007 posts to the LINQ: Next CTP thread and my Rico Mariani's DLinq Performance Tips (Part 2) and Compiled Queries post of 6/28/2007.

The O/RM Designer's O/R Provider Property Will be Gone in Beta 2

According to Microsoft's Jay Hickerson in an answer to questions in the forum's LINQ to SQL designer "O/R Provider" property thread:

The O/R Provider was originally placed there so that we could generate code that would work correctly with databases other than MS SQL Server at runtime. However, the runtime support for this did not materialize in time to get it into the designer for Orcas. The property has been removed for Beta 2 and there will not be support for different providers from the designer. We are considering adding some level of support back in a future release, possibly a service pack.

Lack of support for databases other than SQL Server is the subject of my recent Future LINQ to SQL Support for Multiple Databases? post.

LINQ to SQL Integration with Windows Communication Foundation

Matt Warren says the following about WCF DataContract and DataMember attributes in answer to a question about DLINQ as WCF service in the LINQ Project General forum:

A future version of SqlMetal.exe will generate DataContract and DataMember attributes on your entities for you. Putting them on the DataContext won't work because the DataContext is not serializable.

[Thanks to Julie Lerman for the preceding link.]

Jim Wooley provides detailed instructions for adding WCF attributes as Custom Attributes properties with the LINQ to SQL O/RM tool's Attribute Editor dialog. Adding attributes in the designer prevents them from disappearing when you refresh the partial class code.

Microsoft should provide complete, non-trivial sample LINQ to SQL and Entity Framework applications that take full advantage of WCF in the Beta 2 timeframe.

Repairing the Union Operator

The following T-SQL code demonstrates with the Northwind Customersa and Suppliers tables how to write UNION queries with literals in SELECT lists based on SQL89-style equi-joins:

SELECT c.City AS City, c.CompanyName, 'Customer' AS [Type]
FROM Customers AS c, Suppliers AS s
WHERE c.City = s.City
UNION SELECT s.City AS City, s.CompanyName, 'Supplier'
FROM Customers AS c, Suppliers AS s
WHERE c.City = s.City
ORDER BY City

Executing the preceding statement generates the following output:

City      CompanyName                                    Type
--------- ---------------------------------------------- --------
Berlin    Alfreds Futterkiste                            Customer
Berlin    Heli Süßwaren GmbH & Co. KG                    Supplier
London    Around the Horn                                Customer
London    B's Beverages                                  Customer
London    Consolidated Holdings                          Customer
London    Eastern Connection                             Customer
London    Exotic Liquids                                 Supplier
London    North/South                                    Customer
London    Seven Seas Imports                             Customer
Montréal  Ma Maison                                      Supplier
Montréal  Mère Paillarde                                 Customer
Paris     Aux joyeux ecclésiastiques                     Supplier
Paris     Paris spécialités                              Customer
Paris     Spécialités du monde                           Customer
Sao Paulo Comércio Mineiro                               Customer
Sao Paulo Familia Arquibaldo                             Customer
Sao Paulo Queen Cozinha                                  Customer
Sao Paulo Refrescos Americanas LTDA                      Supplier
Sao Paulo Tradição Hipermercados                         Customer
(19 row(s) affected)

LINQ uses similar syntax and the Union/union operator to generate the preceding output. Here's the C# code:

StringBuilder sbQuery = new StringBuilder();

var query = (
from c in dcNwind.Customers
join s in dcNwind.Suppliers on c.City equals s.City
select new { c.City, c.CompanyName, Type = "Customer" })
.Union(
from c in dcNwind.Customers
join s in dcNwind.Suppliers on c.City equals s.City
select new { s.City, s.CompanyName, Type = "Supplier" });

foreach (var u in query)
{
    sbQuery.Append(u.City + "\t" + u.CompanyName + "\t" +
        u.Type + System.Environment.NewLine);
}
// Write to the text box
txtResult.Text = sbQuery.ToString();

However, the preceding code returns Customer to all rows, instead of the five Supplier instances in the T-SQL query resultset.

According to Matt Warren in a response to my forum post this bug will be fixed in Beta 2.

I would have posted a VB version of the preceding code, but VB won't support the Join keyword until Beta 2.

Missing VB LINQ Keywords

Amanda says in this Where is gone the Group By operator in VB? and and Let Keyword for Visual Basic posts that the following LINQ keywords will be added to VB 9.0 in Beta 2:

  • Join
  • Group By
  • Group Join
  • Skip [While]
  • Take [While]
  • Aggregates (Sum, Min, Max, etc.)
  • Let

as well as these language features:

  • Nullable shortcut syntax (?)
  • Lambda Expressions
  • Partial Methods
  • Ternary If operator*

* Update 6/15/2007: Jim Wooley reported in a comment that a ternary If operator (similar in function to IIf or inline If) will be added per Paul Vick's IIF becomes If, and a true ternary operator post of 5/8/2007.

LinqDataSource Control for ASP.NET

Beta 2 should deliver ASP.NET's LinqDataSource control that Anders Hejlsberg demonstrated at MIX07.

Problems with Stored Procedure Updates in the O/R Designer

This long LINQ to SQL: Problem with Use of Stored Procedures for Insert, Update, and Delete thread in the LINQ Project General forum lists four issues that affect replacing dynamic SQL with stored procedures for table updates in the O/R Designer:

  1. Code generation adds an underscore to reserved words, such as Order in VB which is missing in method calls.
  2. Delete funcitons have an extraneous current parameter.
  3. Update functions have current and original parameter values reversed (discovered by Jim Wooley and Mike Taulty).
  4. To date, I've been unable to match INSERT sprocs with the required signature for tables with identity PKs.

Presumably, these problems will be fixed in Beta 2. Matt Warren says early in the thread:

The problems with the designer code-gen have been resolved and should appear in the beta2 CTP or possibly sooner.

However, I haven't received any direct assurance that the specific problems in items 2 through 4 of the preceding list will be addressed. The party line is: "Databinding will be greatly improved in Beta 2."

I'll add more new features and improvements coming in Orcas Beta 2 to this list as I learn about and/or confirm them.

Sunday, April 29, 2007

Déjà Vu All Over Again: Entity Framework Cut from Orcas

Microsoft software architect Mike Pizzo announced in the ADO.NET Entity Framework Update post of 10:09 PM, Saturday, April 28, 2007:

[W]e have decided to ship the ADO.NET Entity Framework and Tools during the first half of 2008 as an update to the Orcas release of the .NET Framework and Visual Studio.

Mike does his best to spin the bad news by starting the announcement with:

Microsoft is deepening its investment in the ADO.NET Entity Framework as a critical piece of Microsoft’s Data Platform vision.

Many Microsoft observers will read that sentence as:

Microsoft is deep-sixing its investment in the ADO.NET Entity Framework as a critical piece of Microsoft’s Data Platform vision.

It's too soon to make that judgment, but there's no question that Mike's announcement delivers yet another blow to those developers (like me) who followed Microsoft's primrose path into its previous object/relational mapping (O/RM) tool void.

Microsoft MVP Scott Bellware avoided the most recent O/RM trap by abandoning a planned short-term implementation of the Entity Framework (EF), as described in his April 24, 2007 Falling Back to NHibernate - A Phone Call with the Entity Framework Folks:

We made a decision today to put an early end to our exploration of the Entity Framework for our current product work, and to fall back on NHibernate. ...

When the EDM designer started to drop out of the CTP's (post-August 2006), we had hoped that it would reappear around beta 1, and would be fairly solid around beta 2. We're scheduled to ship our current work product in coincidence with Orcas. We expect to start customer previews when a go-live license is offered. Without an editor for the metadata, we really can't justify the use of the EF to our customers.

The phone call [to the ADO.NET EF team] took place in two parts. The second part of the call got into some NDA-covered material that Kevin wasn't able to stick around for. Tim and Dan shared some info that gave me with a definitive and conclusive basis for making a sound call for our product. I'm very grateful to the EF team to have been enabled to make such a clear cut decision.

I have the feeling that Scott might have received advance notice (under NDA) that EF and its components were going back to the drawing board. Regardless, he said in an April 22, 2007 comment to my Persistence Ignorance Is Bliss, but Is It Missing from the Entity Framework? post:

I won't remain committed to the Entity Framework if the EDM Designer doesn't arrive by Orcas RTM.

I see the EDM designer as one of the most compelling features of EF in its current incarnation. It's the piece that translates well to how we expect our customers will expect to work with the entities in our product.

If the EDM designer is not in place by RTM, then I don't expect that we can ship a product to our customers if part of the product's customization story is based on the presence of the EDM.

We would need to fall back on NHibernate, and we have had this contingency plan in mind since we began the foray into EF.

Scott implemented his contingency plan two days later. (He insists he's not a member of the "NHibernate Mafia".)

Note: There still might be hope for EF with the Domain Driven Development (DDD) crowd. Interknowlegy's Tim McCarthy is writing .NET Domain-Driven Design with C#: Problem-Design-Solution to be published by Wiley. You can download the slides and code for Tim's April 19, 2007 "Domain-Driven Design using the ADO.NET Entity Framework" presentation to the International Association of Software Architects' Southern California chapter (IASA SoCal). The code is based on Paul Gielens original code for "Organizing Domain Logic" which compares Microsoft's Three-Layered Services Architecture (MS-TLSA) approach against DDD. (The three layers are the traditional data, business and presentation, not the EDM's storage, mapping, and coneptual layers.)

Does Microsoft Have a Data Access Strategy?

Mike posted Microsoft’s Data Access Strategy to the Data blog three minutes after the preceding bombshell. He answers the question with:

Yes, it turns out we do. Microsoft envisions an Entity Data Platform that enables customers to define a common Entity Data Model across data services and applications. The Entity Data Platform is a multi-release vision, with future versions of reporting tools, replication, data definition, security, etc. all being built around a common Entity Data Model.

Mike disclosed another part of the data access strategy in the first post:

Microsoft will be leveraging the Entity Data Model in future versions of Microsoft products such as SQL Server. This Data Platform vision enables customers to leverage their investment in a common conceptual entity model across product lines. [Emphasis added.]

Frans Bouma, the developer of LLBGen Pro, a commercial O/RM tool, has a different (and sinister) take on Microsoft's data access strategy:

The announcement speaks about Microsoft building applications on top of Entity Framework and also that the Entity Framework is moving towards SqlServer. Now, if you can add 1 and 1 together, you of course realize that if you have an application, say Sharepoint vNext, build on top of Entity Framework and you have one of your major cash cows SqlServer enabled with Entity Framework, you have a combination to earn more money: people who want the next version of your application also want your new database system.

The Entity Framework in ADO.NET vNext was designed to be database independent, and it might still be database independent when it eventually ships (I personally don't believe their H1 2008 mark). If you have one of your other major cash cows, say Sharepoint vNext, build on top of ADO.NET vNext, you then open up the road to host Sharepoint on say Oracle, DB2 or an open source database. This then would alienate one of your other cash cow products, SqlServer which will have the Entity Framework build-in.

Personally, I don't buy Frans' theory at present. Microsoft's "major cash cows" are Windows operating systems, the Office suite, and well-entrenched server-based applications, such as Exchange Server and SQL Server. By default, Windows SharePoint Services (WSS), which is a component of Windows Server 2003, uses freely distributable SQL Server 2005 Express (SSX) as its data store not as its host operating system.

Paul Wilson, the developer of the commercial WilsonORMapper (a simpler competitor to LLBGen Pro) says "Great News: Only One O/RM Shipping in Orcas." Wilson is a fan of LINQ to SQL because it's simpler but "good enough for the vast majority of cases."

Mike contrasts LINQ to SQL and LINQ to Entities:

LINQ to SQL supports rapid development of applications that query Microsoft SQL Server databases using objects that map directly to SQL Server schemas. LINQ to Entities supports more flexible mapping of objects to Microsoft SQL Server and other relational databases through extended ADO.NET Data Providers.

Despite the comments regarding support by LINQ to SQL for databases other than SQL Server by Dinesh Kulkarni, Scott, Guthrie, Matt Warren and Keith Farmer quoted in my Future LINQ to SQL Support for Multiple Databases? post of April 19, 2007, Mike has put that issue to bed. It's clear as part of Microsoft data access strategy that LINQ to SQL is LINQ to SQL Server (only).

Shades of WinFS, ObjectSpaces, Project Green, and the Microsoft Business Framework

The previous indication of trouble in the Entity Framework camp was the announcement at VSLive! San Francisco that the Entity Data Model (EDM) Designer had been cut from the Orcas release.

Earlier, a major controversy about EF's lack of "Persistence Ignorance" and its need to support Plain Old CLR Objects (POCOs), test-driven development (TDD), and agile programming techniques erupted at Microsoft's MVP Summit conference in Redmond on March 12 - 15, 2007.

But the real problem is that .NET developers might equate the result of EF's integration into the next version of SQL Server (code-named "Katmai") with the demise of ObjectSpaces, EF's ill-fated predecessor OR/M tool, when it was integrated into WinFS. My June 26, 2006 Microsoft Bites the Bullet: WinFS Bites the Dust post delivers the grisly details of the deaths of WinFS, ObjectSpaces, Project Green, and the MBF.

Here's Andrew Conrad's statement of the official status of ObjectSpaces in Visual Studio 2005 (then codenamed "Whidbey") as of April 6, 2004:

ObjectSpaces is not participating in Whidbey beta 1, however, it remains part of the Whidbey/Yukon wave and will be made available as a downloadable add-on pack for the .NET Framework Whidbey shortly after Whidbey ships. This additional development and stabilization period will be focused on improving the overall ObjectSpaces programming experience and providing tighter integration with WinFS, the next generation file system in Longhorn. The schedule for the ObjectSpaces mapping tool is also being adjusted accordingly.

His post concludes with:

Bottom line - this will mean some change in the release schedule, but should not adversely effect anyone's plans to deploy the ObjectSpaces framework. In fact, I believe the tighter integration with WinFS will significantly justify the delay.

Does that language sound familiar? Compare the comments with those to Mike's initial post.

Even more ominous is Barbara Darrow's April 26, 2007 speculation in CRN that "Katmai, Orcas Link Could Delay Longhorn Release." If true, which I doubt, Katmai's EF dependency could delay it and Longhorn Server until probably late 2008.

Mike replies with a Sunday-afternoon comment regarding spin, ObjectSpaces, and WinFS:

I apologize for the “PR-spin” feel people have associated with this post. Even a short delay of the Entity Framework is a difficult message, especially to convey through the imprecise nature of written communication. How do we convey that we believe this to be the right decision, without people losing confidence in the technology, particularly in light of Microsoft’s less-than-perfect record in shipping ORM solutions, and in the wake of WinFS? I struggled with the wording. With the approach. With the message. Yes, marketing was involved in reviewing the content (of course). I regret now accepting some of their input, but the message is the same. The Entity Framework will not ship in Orcas. It will ship shortly there-after. The slip isn’t about Microsoft’s lack of commitment to the technology. It does give us more time to address some key feedback we’ve received to date from internal and external customers, including providing at least CTP-quality tools for defining flexible mapping between the store and the objects. There’s no way I can convince you that this isn’t another ObjectSpaces, or another WinFS. You’ll have to look at how we continue to deliver over the next few months. You’ll have to look at the quality of the bits. You’ll have to look at the other investments Microsoft is making around the Entity Data Platform, including announcements and demos this week at Mix.

This year I celebrate my 20th year at Microsoft. In that time I’ve seen Microsoft invests heavily in a number of technologies that, for one reason or another, never come to market. This isn’t one of them.

I'm inclined to believe Mike's comment. NetDocs also comes to mind as an example of a well-funded Microsoft technology that never came to market.

"Those who cannot remember the past are condemned to repeat it."

George Santayana (1863 - 1952, The Life of Reason)

Mike says in his initial post, "Stay tuned for announcements starting next week at Mix around new products building on the ADO.NET Entity Framework." I plan to do this, but with trepidation.

Update 5/3/2007: Redmond Developer News senior editor Kate Richards quotes me in her April 30, 2007 ".NET Entity Framework Slips Beyond Orcas" article:

Roger Jennings, a developer and technology writer for OakLeaf Systems, doesn't think the later ship date is going to be a big deal. "They decided that they couldn't release the Entity Framework without the Entity Data Model Designer, which was my contention to start with," he said. "And they've also just announced a couple of incubation projects that depend on it that also might have an influence on the delivery date." ...

"Almost everybody is considering [Entity Framework] to be ObjectSpaces revisited, but I don't think that's the case," says Jennings.

The "incubation projects" are the ADO.NET Team's "Project Jasper" (a.k.a., Dynamic ADO.NET) and "Project Astoria" (a.k.a., "RESTful Data in the Cloud.)

Note: This item was linked from the Discussions section of the Techmeme entry for the original ADO.NET post. There's also a Discussions link to a Channel9 thread.

Thursday, April 26, 2007

More Issues with VB 9.0 Samples in the Updated LINQ to SQL Documentation

It's evident that no one tech-edited or double-checked the VB 9.0 code examples in the "LINQ to SQL: .NET Language-Integrated Query for Relational Data" whitepaper Dinesh Kulkarni, Luca Bolognese, Matt Warren, Anders Hejlsberg, Kit George that was updated in March 2007.

I encountered the most egregious example of the authors' disdain for VB in the following code snippet from the "Stored Procedures Invocation" topic:

<StoredProcedure(Name:= "VariableResultShapes")> _
  <ResultType(typeof(Customer))> _
  <ResultType(typeof(Order))> _
public VariableResultShapes(shape As Integer?) As IMultipleResults
  Dim result As IExecuteResult = ExecuteMethodCallWithMultipleResults(Me, _
    CType(MethodInfo.GetCurrentMethod(), MethodInfo), shape)
    return CType(result.ReturnValue, IMultipleResults)
End Function
The example uses C#'s typeof() instead of VB's GetType() function in lines 2 and 3, is missing the Function keyword on line 4, and uses the unsupported ? symbol for Nullable(Of T) on line 4. There are obvious errors on 50% of the lines of the example. Further, the following required Imports statements are not to found anywhere in the document:
Imports System.Data.Linq
Imports System.Data.Linq.Provider
Imports System.Reflection
It would behoove authors of whitepapers with a penchant for C# to at least check to see if their VB sample code compiles.

By the way, Kit George is a program manager on the VB team and presented the LINQ Overview Webcast on April 25, 2007.

Updated 4/27/2007: Dropped initial (off-topic) paragraphs.

Tuesday, April 24, 2007

LINQ and LINQ to SQL Webcast

Marc Schweigert of Microsoft's Public Sector DPE (Developer and Platform Evangelism) Team has produced a 90-minute Webcast entitled Introduction to LINQ + LINQ to SQL. The presentation aired at 2:00 PM EST on 4/24/2007, immediately after the VB team's Orcas Overview Webcast and the day before its LINQ Overview presentation. Registration is required to view the archived Webcast.

Following is the abstract:

Modern applications operate on data in several different forms: Relational tables, XML documents, and in-memory objects. Each of these domains can have profound differences in semantics, data types, and capabilities, and much of the complexity in today’s applications is the result of these mismatches. In this talk, we will explain how the Orcas release of Visual Studio aims to unify the programming models through LINQ capabilities in C#, a strongly typed data access framework, and an innovative Application Programming Interface (API) for manipulating and querying XML.

Database-centric applications have traditionally had to rely on two distinct programming languages: one for the database and one for the application. In the second part of this talk we will introduce LINQ to SQL, a component of the LINQ project designed to help integrate relational data and queries with C# and Visual Basic. LINQ to SQL enables developers to express queries and updates in terms of their local programming language without sacrificing the server-side execution model of today’s high-performance SQL-based approaches. Using these advances, database queries that previously were stored as opaque strings now benefit from static type checking, CLR metadata, design-time type inference, and of course IntelliSense. LINQ to SQL also supports a rich update capability that lets you save changes to an object graph back to the database using optimistic concurrency or transactions.

LINQ to SQL receives about the last 45 minutes of the presentation, most of which uses C#.

Saturday, April 21, 2007

Installing Orcas Beta 1 from the 5.32 GB ISO Image

Conventional single-layer DVD-R/RW and DVD+R/RW discs hold 4.7 GB. So unless you have a double-layer DVD burner and media, you won't be able to run the 5.32-GB Visual Studio Orcas Beta1 Setup program from a physical DVD.
Setup fails before completion of .NET Fx 3.5 when run from a networked drive. Believe Setup's Start Page where it says:
Some components require that network connections be temporarily suspended during setup.

Click for full-size image.

If you try installing from a network drive, which includes third-party apps that mount ISO images to virtual CD or DVD drives, you might experience what appears to be a non-fatal timeout message while Setup is copying its initial set of files. Even if not, you'll undoubtedly see this message during the installation of .NET Fx 3.5:

and end up with this screen when installation fails:

Click for full-size image.

This means that your double-layer DVD burner must be local to the machine on which you want to install Beta 1. Virtual machines connect to the host OS's physical drives as networked drives, so you're out of luck if you want to install Orcas Beta 1 on Vista as a guest OS.

The Solution

Use Daemon Tools' free Virtual Daemon Manager (VDM) to mount the en_visual_studio_orcas_beta_1_professional_dvd_23591.iso image file to a virtual DVD drive. The current VDM version (4.09) works fine under Windows Vista as a host or guest OS, despite what you might have heard on the Web.

Note: When you install VDM on virtualized Vista, save and run the setup file. You must reboot to finish VDM's installation. Under Virtual Server 2005 R2 RC1 with Vista as the guest OS, you must explicitly run daemon4091-x86.exe a second time to complete the installation after rebooting.

Update: 4/24/2007: Commenters have suggested using WinRAR or IsoBuster to extract the image to executable files. WinRAR isn't free (it has a 40-day trial license) and IsoBuster requires the PRO version ($29.95 personal, $49.95 professional) to open an image of this size. Some commenters have suggested other virtual DVD drive apps, but VDM is free and has proven itself under Vista.