Monthly Archives: September 2007

Mentoring in I.T. and in the kitchen

One of the Women in Tech essays that has been published so far was about mentoring and inspiring young women who are interested in tech. Nelly Yusupova tells an amazing story about how she was so overwhelmed after day one of a computer class in college that she almost quit the class. But she decided that somehow she would overcome her terror and challenged herself to not just take the class but to get an A. And the rest is history. 🙂 She also talks about the importance of role models and mentoring.

I was really touched by her essay.

A few days later, I experienced a different type of mentoring and realized that, in a funny way, it had a lot in common with what Nelly was talking about.

I have a lot of tomatoes in my garden and wanted to make sauce and freeze it. Some friends said I should can it. I tried canning a few quarts and you could see that after two days they look like something that could potentially get me arrested for crimes against humanity.

My friend Geri spent 1/2 hour on the phone with me explaining how to really do it properly. I still thought I would be better off just freezing as I just did not have the confidence to try it again and didn’t want to ruin my whole summer’s bounty of tomatoes. But I talked myself into it and called my neighbor to borrow what I had learned from Geri was one of the key tools for canning – a big canning pot. My neighbor, Michaela, said she had finished her canning and loves to can and loves to share the knowledge.

So she brought down her pot and a notebook of pickling & canning recipes (and tips & tricks) that her mother in law had lovingly assembled for her as a Christmas present, then came back after I had gotten the water boiling (it’s a BIG pot). Michaela spent a few hours with me while I played assistant (that’s like code-monkey, I guess) as she canned the laughably small output of 4 quarts of sauce I had cooked. Everything but the tomato paste was from my garden – basil, oregano, thyme, chives, rosemary. When all was said and done, we listened to the satisfying pops as each jar sucked it’s lid in. That’s a pretty good indication that I won’t be the cause of family wide botchilism this winter.

I realized that this is an age old type of mentoring  – not just limited to a mother passing knowledge on to daughter. Sure, I had plenty of places I could read about canning in books and on the web. But having Michaela show me was more like sharing secrets and there is something intimate about it – the little things that you don’t get by reading. For example, watching her constantly tossing each tool she used for the canning process back into the boiling water without thinking about it – perhaps a trade secret. 😉

So much in history is about women passing knowledge to women, and men to men. Of course that’s changed a lot. (Like, duh! :-)) But there is still something very instictive and natural about it. In I.T. it’s a little harder because there are not so many women for younger women to seek out for that intimate passing on of the little secrets to our success.

Obvioulsy we all learn from and share with each other and I always fear that my thoughts will be taken out of context and challenged. I am not trying to poke anyone or start a debate. I’m only thinking out loud about how my canning experience made me think of Nelly’s essay and wondering why I was drawing that connection.

After I had cooked up the sauce I decided to throw in the gobs of sun-gold cherry tomatoes that were sitting on the vines in my garden which I just couldn’t eat quickly enough. Here’s a pic of them from before I destemmed them and threw them into the sauce.

Finishing up READ Stored Procs brought in by the Entity Framework EDM Wizard

When you build an EDM from an existing database in Entity Framework, you can get Views & Stored Procedures in addition to tables. You’ll find that if you select Stored procedures, you will also get your functions.

The stored procedures are only defined in the storage layer. The return type is not determined therefore there is no Entity created or the mapping that would be between the two.

The Entity Framework documentaiton describes how to build Insert, Update and Deleted sprocs but not how to build sprocs that return data.

I got some help on the forums which showed building a stored proc in the EDM from scratch, but I wanted to take one that had been put into the model and complete it. I’ve written a short article that walks you through step by step that will go up on www.DevSource.com shortly, but in the meantime, I wanted to show how I finished up the CustOrderHist procedure. The article goes into more detail and explains the what’s and why’s. I will put a link to it in this post when it goes online.

What you will see created by the wizard is this function in the storage model section of the EDMX.

<Function Name=”CustOrderHist” Aggregate=”false” BuiltIn=”false” NiladicFunction=”false” IsComposable=”false” ParameterTypeSemantics=”AllowImplicitConversion” Schema=”dbo”>
<Parameter Name=”CustomerID” Type=”nchar” Mode=”in” />
 </Function>

You’ll need to create an entity, entityset and FunctionImport in the conceptual model. In the storage model, you’ll need a matching entity and entity set and then you need to map the FunctionImport of the Conceptual model to teh Function in the storage as well as map the entitysets.

Here are what the rest of the code chunks look like.

I created the Entity using the designer and that automatically created the EntitySet.

<EntityType Name=”CustOrderHistResult” >
  <Key >
      <PropertyRef Name=”ProductName” />
  </Key>
   <Property Name=”ProductName” Type=”String” Nullable=”false” MaxLength=”40″ />
   <Property Name=”Total” Type=”Int32″ Nullable=”false”/>
</EntityType>

<EntitySet Name=”CustOrderHistResultSet” EntityType=”NWModel.CustOrderHistResult” />

The FunctionImport in the Conceptual layer which has pointers to the entity and entity set.

<FunctionImport Name=”CustOrderHist” EntitySet=”CustOrderHistResultSet”
ReturnType=”Collection(Self.CustOrderHistResult  >
   <Parameter Name=”CustomerID” Type=”String” Mode=”in” />
</FunctionImport>

In the storage layer, add the matching entity and entity set; they look like this. Remember to put them in the correct sections!

<EntityType Name=”CustOrderHistResult” >
  <Key >
      <PropertyRef Name=”ProductName” />
  </Key>
   <Property Name=”ProductName” Type=”nvarchar” Nullable=”false” MaxLength=”40″ />
   <Property Name=”Total” Type=”int” Nullable=”false”/>
</EntityType>

<EntitySet Name=”CustOrderHistResultSet” EntityType=”NWModel.Store.CustOrderHistResult”/>

And lastly the mappings:

The first one I did manually.

<FunctionImportMapping FunctionImportName=”CustOrderHist”       FunctionName=”NWModel.Store.CustOrderHist”/>

The second I actually created by using the designer. Here is the xml that was generated

<EntitySetMapping Name=”CustOrderHistResultSet” >
   <EntityTypeMapping TypeName=”IsTypeOf(NWModel.CustOrderHistResult)” > 
      <MappingFragment StoreEntitySet=”CustOrderHistResultSet” >
         <ScalarProperty Name=”ProductName” ColumnName=”ProductName” />
         <ScalarProperty Name=”Total” ColumnName=”Total” />
      </MappingFragment>
   </EntityTypeMapping>
</EntitySetMapping>

With all of this in place, I can now use or query the stored procs as I would any of the other entities in my model

Dim query = From CustOrders In nw.CustOrderHist(“ROMEY”) _
    Where CustOrders.Total > 10 Select CustOrders Order By CustOrders.ProductName

Convertinga LINQ to Entities query to a DataTable

[Update at bottom of post]

I wrote this in response to a question on the ADO.NET Orcas forums and thought I would copy it here.

Note that Danny Simmons writes more information about Entity Framework and DataTables in the thread.

Here’s my solution for now…

LINQ to Datasets does allow for conversions from queries to Datatables, but it doesn’t seem to be able to deal with ObjectQueries. I then queried my objectquery to return an IQueryable and also an IEnumerable, but I couldn’t get the datatable conversion to work. Look for CopyToDataTable in the VS2008 documentation for more info on that.

So since I couldn’t get at it I rolled my own using Mike Taulty’s previous example (not for ObjectQueries though) as a start.

This example uses the ObjectStateManager so I can dynamically get at the values. As long as I’m already using the ObjetStateManager, I also use it to get some metadata, even though I could have used the MetadataWorkspace for that.

Assuming we are starting with nwrows which is an ObjectQuery for a simple Linq to Entities query…

  Dim dt As New DataTable

  Dim currow As Integer = 0  ‘just a counter to keep track of my for each position

  For Each r In nwrows
  ‘get statemanager for current row
   Dim stateEntry = nwts.ObjectStateManager.GetObjectStateEntry(CType(r, Data.Objects.DataClasses.IEntityWithKey).EntityKey)

   ‘this will only happen once, build the dataColumns, I’m not bothering with their names

   If dt.Columns.Count = 0 Then
    For i = 0 To stateEntry.CurrentValues.FieldCount – 1
     dt.Columns.Add()
    Next
   End If

   ‘now we can add data, grabbing current values
   Dim dtrow As DataRow = dt.NewRow

   For idata = 0 To stateEntry.CurrentValues.FieldCount – 1
    dtrow.Item(idata) = stateEntry.CurrentValues(idata)
   Next

   dt.Rows.Add(dtrow)

   currow += 1

  Next

[Update]

Note: It looks like Andrew Conrad, who is on the ADO.NET team, also got inspired today by the forum question as he has written a much more indepth (read “not quite the hack that I wrote”) solution to the loss of the datatable conversion capability. In fact he explains why we can’t use it with non-Linq to Dataset queries:

In the original Linq CTP and the first Orcas Beta, we included a DataSet specific Linq operator called CopyToDataTable<T> (It was called ToDataTable at one point also).  For Beta 2 of Orcas, we ended up restricting this method to only work with DataRows (or some derived type) via a generic constraint on the method.  

So, like a good soldier, he wrote up some sweet code to do the job. I was only pulling in the values. He’s got tablenames and types and subtypes as well, with a completely different approach. Definitely check it out. I will probably just use this rather than my hack and try to consider my experimenting with StateObject a good use of my time yesterday! 🙂

How do LINQ to Entities queries surface in the database?

While Jim Wooley is looking at performance in LINQ to SQL, I’ve finally pulled the VS2008 installation on my VPC out of the mysteries of SQL Server Express and installed SQL Server Developer Edition so that I can look at my queries as they come into profiler.

Note that this is all based on VS2008 Beta2 and Entity Framework Beta2. I’m using AdventureWorksLT as my test db.

QUERY #1

A Simple Query

Let’s just grab customers with this code:

Dim custs = aw.Customer

While I might lazily write TSQL that looks like

select * from Customer

EF creates a more explicit query, which makes perfect sense to me.

SELECT
0 AS [C1],
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[NameStyle] AS [NameStyle],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[SalesPerson] AS [SalesPerson],
[Extent1].[EmailAddress] AS [EmailAddress],
[Extent1].[Phone] AS [Phone],
[Extent1].[PasswordHash] AS [PasswordHash],
[Extent1].[PasswordSalt] AS [PasswordSalt],
[Extent1].[rowguid] AS [rowguid],
[Extent1].[ModifiedDate] AS [ModifiedDate]
FROM [SalesLT].[Customer] AS [Extent1]

QUERY #2

When I just want to get one particular record, I ask for there are a few ways to do this.

First is to write a query that returns the whole iQueryable with a where clause to filter for the single item. Then I iterate through that one item “list”. Using method or query syntax:

Dim cust = aw.Customer.Where(Function(c) c.CustomerID = 37)
For Each c In cust
   Dim name = c.CompanyName
Next

Dim cust2 = From c In aw.Customer Where c.CustomerID = 37 Select c
  For Each c In cust2
   Dim name = c.CompanyName
  Next

I get a reasonable TSQL query on the other end

SELECT
0 AS [C1],
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[NameStyle] AS [NameStyle],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[SalesPerson] AS [SalesPerson],
[Extent1].[EmailAddress] AS [EmailAddress],
[Extent1].[Phone] AS [Phone],
[Extent1].[PasswordHash] AS [PasswordHash],
[Extent1].[PasswordSalt] AS [PasswordSalt],
[Extent1].[rowguid] AS [rowguid],
[Extent1].[ModifiedDate] AS [ModifiedDate]
FROM [SalesLT].[Customer] AS [Extent1]
WHERE 37 = [Extent1].[CustomerID]

QUERY #3

But watch what happens to the TSQL when I make my query more efficient, regardless of method or query syntax.

Dim cust = aw.Customer.Where(Function(c) c.CustomerID = 37).First
 Dim name = cust.CompanyName

SELECT
[Limit1].[C1] AS [C1],
[Limit1].[CustomerID] AS [CustomerID],
[Limit1].[NameStyle] AS [NameStyle],
[Limit1].[Title] AS [Title],
[Limit1].[FirstName] AS [FirstName],
[Limit1].[MiddleName] AS [MiddleName],
[Limit1].[LastName] AS [LastName],
[Limit1].[Suffix] AS [Suffix],
[Limit1].[CompanyName] AS [CompanyName],
[Limit1].[SalesPerson] AS [SalesPerson],
[Limit1].[EmailAddress] AS [EmailAddress],
[Limit1].[Phone] AS [Phone],
[Limit1].[PasswordHash] AS [PasswordHash],
[Limit1].[PasswordSalt] AS [PasswordSalt],
[Limit1].[rowguid] AS [rowguid],
[Limit1].[ModifiedDate] AS [ModifiedDate]
FROM   ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN  (SELECT TOP (1)
 [Extent1].[CustomerID] AS [CustomerID],
 [Extent1].[NameStyle] AS [NameStyle],
 [Extent1].[Title] AS [Title],
 [Extent1].[FirstName] AS [FirstName],
 [Extent1].[MiddleName] AS [MiddleName],
 [Extent1].[LastName] AS [LastName],
 [Extent1].[Suffix] AS [Suffix],
 [Extent1].[CompanyName] AS [CompanyName],
 [Extent1].[SalesPerson] AS [SalesPerson],
 [Extent1].[EmailAddress] AS [EmailAddress],
 [Extent1].[Phone] AS [Phone],
 [Extent1].[PasswordHash] AS [PasswordHash],
 [Extent1].[PasswordSalt] AS [PasswordSalt],
 [Extent1].[rowguid] AS [rowguid],
 [Extent1].[ModifiedDate] AS [ModifiedDate],
 0 AS [C1]
 FROM [SalesLT].[Customer] AS [Extent1]
 WHERE 37 = [Extent1].[CustomerID] ) AS [Limit1] ON cast(1 as bit) = cast(1 as bit)

What’s up with the outer join – to itself? Obviously by asking for First, which is an Element method, it changes the meaning of the query a lot. The more I think about it, if you need the flexibilitiy for First to be able to handle something like:

(From o in orders where o.Total>100 select o).First

where it’s not so obvious what the result may be as it is when filtering on a unique primary key.

I get the same effect if I use First this way, too:

Dim cust2 = From c In aw.Customer Where c.CustomerID = 37 Select c
  Dim cust = cust2.First

So that outer join is somehow necessary to pull it off. While I’d have to really push the envelope to see if there was any kind of performance difference to worry about, but it’s interesting to see the impact that using the method has on the TSQL.

QUERY #4 & QUERY #5

Next up is a table with foreign keys – 3 of them. I query for just one record (no children) in the SalesOrderHeader table.

I want to preface this by saying that I posted this on the ADO.NET Orcas forums here and was told

The Entity Framework (EF) team is aware of issues with this pattern (not the query, but the mapping). We should see huge improvements over the next couple of milestones. … The EF team is working to recognize and optimize for this common pattern, removing the redundant self-joins and including the foreign key values in a single projection.

But I didn’t want all of my exploration to go to waste, so this is what you’ll find today. We’ll just need to check back in later releases.

First I do it by getting the whole iQueryable then iterating through. Second method is to just use the First method and get only one object. I think you will quickly see why I pointed out the three foreign keys on this table.

Dim sorders = aw.SalesOrderHeader.Where(Function(so) so.SalesOrderID = 71797)
  For Each so In sorders
   Dim x = so.Comment
  Next

  Dim sorder2 = (From soh In aw.SalesOrderHeader Where soh.SalesOrderID = 71815).First
  Dim x2 = sorder2.Comment

The first query results in TSQL that creates outer joins for each foreign key, but it’s not too obnoxious.

SELECT
0 AS [C1],
[Extent1].[SalesOrderID] AS [SalesOrderID],
[Extent1].[RevisionNumber] AS [RevisionNumber],
[Extent1].[OrderDate] AS [OrderDate],
[Extent1].[DueDate] AS [DueDate],
[Extent1].[Status] AS [Status],
[Extent1].[OnlineOrderFlag] AS [OnlineOrderFlag],
[Extent1].[SalesOrderNumber] AS [SalesOrderNumber],
[Extent1].[PurchaseOrderNumber] AS [PurchaseOrderNumber],
[Extent1].[AccountNumber] AS [AccountNumber],
[Extent1].[CreditCardApprovalCode] AS [CreditCardApprovalCode],
[Extent1].[SubTotal] AS [SubTotal],
[Extent1].[TaxAmt] AS [TaxAmt],
[Extent1].[Freight] AS [Freight],
[Extent1].[TotalDue] AS [TotalDue],
[Extent1].[Comment] AS [Comment],
[Extent1].[rowguid] AS [rowguid],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent1].[ShipMethod] AS [ShipMethod],
[Extent1].[ShipDate] AS [ShipDate],
CASE WHEN ([Extent2].[SalesOrderID] IS NULL) THEN CAST(NULL AS int) ELSE 2 END AS [C2],
[Extent2].[BillToAddressID] AS [BillToAddressID],
CASE WHEN ([Extent3].[SalesOrderID] IS NULL) THEN CAST(NULL AS int) ELSE 2 END AS [C3],
[Extent3].[ShipToAddressID] AS [ShipToAddressID],
CASE WHEN ([Extent1].[SalesOrderID] IS NULL) THEN CAST(NULL AS int) ELSE 3 END AS [C4],
[Extent1].[CustomerID] AS [CustomerID]
FROM   [SalesLT].[SalesOrderHeader] AS [Extent1]
LEFT OUTER JOIN [SalesLT].[SalesOrderHeader] AS [Extent2] ON ([Extent1].[SalesOrderID] = [Extent2].[SalesOrderID]) AND ([Extent2].[BillToAddressID] IS NOT NULL)
LEFT OUTER JOIN [SalesLT].[SalesOrderHeader] AS [Extent3] ON ([Extent1].[SalesOrderID] = [Extent3].[SalesOrderID]) AND ([Extent3].[ShipToAddressID] IS NOT NULL)
WHERE 71797 = [Extent1].[SalesOrderID]

The second is much crazier.

SELECT
[Limit1].[C1] AS [C1],
[Limit1].[SalesOrderID] AS [SalesOrderID],
[Limit1].[RevisionNumber] AS [RevisionNumber],
[Limit1].[OrderDate] AS [OrderDate],
[Limit1].[DueDate] AS [DueDate],
[Limit1].[Status] AS [Status],
[Limit1].[OnlineOrderFlag] AS [OnlineOrderFlag],
[Limit1].[SalesOrderNumber] AS [SalesOrderNumber],
[Limit1].[PurchaseOrderNumber] AS [PurchaseOrderNumber],
[Limit1].[AccountNumber] AS [AccountNumber],
[Limit1].[CreditCardApprovalCode] AS [CreditCardApprovalCode],
[Limit1].[SubTotal] AS [SubTotal],
[Limit1].[TaxAmt] AS [TaxAmt],
[Limit1].[Freight] AS [Freight],
[Limit1].[TotalDue] AS [TotalDue],
[Limit1].[Comment] AS [Comment],
[Limit1].[rowguid] AS [rowguid],
[Limit1].[ModifiedDate] AS [ModifiedDate],
[Limit1].[ShipMethod] AS [ShipMethod],
[Limit1].[ShipDate] AS [ShipDate],
[Project3].[C1] AS [C2],
[Project3].[BillToAddressID] AS [BillToAddressID],
[Project5].[C1] AS [C3],
[Project5].[ShipToAddressID] AS [ShipToAddressID],
[Project7].[C1] AS [C4],
[Project7].[CustomerID] AS [CustomerID]
FROM      ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN  (SELECT TOP (1)
 [Extent1].[SalesOrderID] AS [SalesOrderID],
 [Extent1].[RevisionNumber] AS [RevisionNumber],
 [Extent1].[OrderDate] AS [OrderDate],
 [Extent1].[DueDate] AS [DueDate],
 [Extent1].[ShipDate] AS [ShipDate],
 [Extent1].[Status] AS [Status],
 [Extent1].[OnlineOrderFlag] AS [OnlineOrderFlag],
 [Extent1].[SalesOrderNumber] AS [SalesOrderNumber],
 [Extent1].[PurchaseOrderNumber] AS [PurchaseOrderNumber],
 [Extent1].[AccountNumber] AS [AccountNumber],
 [Extent1].[ShipMethod] AS [ShipMethod],
 [Extent1].[CreditCardApprovalCode] AS [CreditCardApprovalCode],
 [Extent1].[SubTotal] AS [SubTotal],
 [Extent1].[TaxAmt] AS [TaxAmt],
 [Extent1].[Freight] AS [Freight],
 [Extent1].[TotalDue] AS [TotalDue],
 [Extent1].[Comment] AS [Comment],
 [Extent1].[rowguid] AS [rowguid],
 [Extent1].[ModifiedDate] AS [ModifiedDate],
 0 AS [C1]
 FROM [SalesLT].[SalesOrderHeader] AS [Extent1]
 WHERE 71815 = [Extent1].[SalesOrderID] ) AS [Limit1] ON cast(1 as bit) = cast(1 as bit)
LEFT OUTER JOIN  (SELECT
 [Extent2].[BillToAddressID] AS [BillToAddressID],
 2 AS [C1]
 FROM  [SalesLT].[SalesOrderHeader] AS [Extent2]
 INNER JOIN  (SELECT [Limit2].[SalesOrderID] AS [SalesOrderID], [Limit2].[C1] AS [C1]
  FROM ( SELECT TOP (1)
   [Extent3].[SalesOrderID] AS [SalesOrderID],
   0 AS [C1]
   FROM [SalesLT].[SalesOrderHeader] AS [Extent3]
   WHERE 71815 = [Extent3].[SalesOrderID]
  )  AS [Limit2]
  WHERE [Limit2].[C1] = 0 ) AS [Filter3] ON [Filter3].[SalesOrderID] = [Extent2].[SalesOrderID]
 WHERE [Extent2].[BillToAddressID] IS NOT NULL ) AS [Project3] ON cast(1 as bit) = cast(1 as bit)
LEFT OUTER JOIN  (SELECT
 [Extent4].[ShipToAddressID] AS [ShipToAddressID],
 2 AS [C1]
 FROM  [SalesLT].[SalesOrderHeader] AS [Extent4]
 INNER JOIN  (SELECT [Limit3].[SalesOrderID] AS [SalesOrderID], [Limit3].[C1] AS [C1]
  FROM ( SELECT TOP (1)
   [Extent5].[SalesOrderID] AS [SalesOrderID],
   0 AS [C1]
   FROM [SalesLT].[SalesOrderHeader] AS [Extent5]
   WHERE 71815 = [Extent5].[SalesOrderID]
  )  AS [Limit3]
  WHERE [Limit3].[C1] = 0 ) AS [Filter6] ON [Filter6].[SalesOrderID] = [Extent4].[SalesOrderID]
 WHERE [Extent4].[ShipToAddressID] IS NOT NULL ) AS [Project5] ON cast(1 as bit) = cast(1 as bit)
LEFT OUTER JOIN  (SELECT
 [Extent6].[CustomerID] AS [CustomerID],
 3 AS [C1]
 FROM  [SalesLT].[SalesOrderHeader] AS [Extent6]
 INNER JOIN  (SELECT TOP (1)
  [Extent7].[SalesOrderID] AS [SalesOrderID],
  0 AS [C1]
  FROM [SalesLT].[SalesOrderHeader] AS [Extent7]
  WHERE 71815 = [Extent7].[SalesOrderID] ) AS [Limit4] ON [Limit4].[SalesOrderID] = [Extent6].[SalesOrderID]
 WHERE [Limit4].[C1] = 0 ) AS [Project7] ON cast(1 as bit) = cast(1 as bit)

QUERY #6 & QUERY #7 (#4 & #5 converted to C#)

Now before you go blaming this on VB parsing :-), I ran the last two queries in C#:

 var so1 = aw.SalesOrderHeader.Where(so => so.SalesOrderID == 71797);
 foreach (var so in so1)
   Debug.Print(so.Comment);

  var so2 = (from soh in aw.SalesOrderHeader where soh.SalesOrderID == 71815 select soh).First();
  Debug.Print(so2.Comment);

And these resulted in the same T-SQL as their VB counterparts.

There’s more that I’ve been looking at but this post is way too long already!

Bob & Chris Roadshow Attendees get free pass to REMIX and a LINQ book

The Burlington stop of the Bob & Chris Roadhow is this coming Monday, sept 10th.

In addition to a day of great (and free) training on

  • Dynamic Languages
  • Silverlight
  • Developer Productivity Tips & Tricks for VS2005 & VS2008
  • Understanding Service Oriented software

(and a free lunch :-))

Bob & chris will have passes to attend REMIX Boston (Oct 8/9th) for free as well as a trunk full of the MS Press book “Introducing LINQ”.

More info and sign up for Monday’s event here.

And just a note, VTdotNET’s monthly meeting will be Sept 17th. See more details on www.vtdotnet.org.

ReMIX Boston – October 8th & 9th

Another one of the “satellite” MIX07 events will take place in Boston in early October – REMIX07 Boston.

Brad Abrams will be doing the keynotes. I see Rocky Lhotka and Jeff Prosise doing talks too. The Eastern Region D.E.s who have organized this have also brought in some “local talent”. Luckily people like Fritz Onion (Maine) and Richard Hale Shaw (Boston) are part of our local talent! Additionally, they have allowed me a session on Astoria and I see fellow use group leaders Bill Wolff, Andy Beaulieu doing talks on WPF and Silverlight, respectively.

This is a two day event with 7 session slots and each session has 5 talks to choose from. Not quite as stressful as the number of talks to select from at MIX07 in Las Vegas, but enough to make you have to think pretty hard!

It’s only $299 for a registration fee. And it is also pretty easy to figure out how to attend for a lot less (or even free).

read more on Chris Bowen’s blog

LINQ to SQL Performance

Developers have been playing with LINQ to SQL long enough now that they are past the “how to” and thinking about performance. Jim Wooley blogs about LINQ to SQL Perf and using CompiledQuery.Compile to get the best performance. He also points to a series of posts on LINQ to SQL Performance by msdn blogger, Rico Mariani.

Of course, the next question for me is how LINQ to SQL will compare to LINQ to Entities and some of the other methods of extracting data via Entity Framework. While Entity Framework is still in earlier stages than LINQ to SQL and I’m guessing they are still working out perf, I did ask about this on the ADO.NET Orcas forums and was told that they’ll be working on writing something up about this.

Nullables in lambda expressions in LINQ to Entities – today’s gotcha

It took me a while to figure out what caused this problem, so I thought I’d share it here.

I was trying to write a query in VB to grab customers who have orders placed after July 1, 2007.

The query looks like this:

Dim q = From cust In nwentities.Customers _
  Where (cust.Orders.Any(Function(o) _
     o.OrderDate > New DateTime(2007, 7, 1)))

But it would not compile. I have Option Strict On and the error was

Option Strict On disallows implicit conversions from ‘Boolean?’ and ‘Boolean’.

What the heck was this “Boolean?”. Not google-able, that’s for sure!

Then I noticed that OrderDate was being defined as a “Date?”.

What I’m seeing is the shortcut for Nullables in VB. It’s really hard to google for that. But if you look it’s everywhere! Such as in this post by Bill McCarthy. (See Bill? I found it all by myself! :-))

OrderDate is nullable. “Date?” means nullable date.

And the VB’s compiler is casting the entire expression to a nullable Boolean then telling me “Yo! A Nullable boolean is NOT the same as a boolean! Sorry”.

Note that the designer showed Nullable as false but the actual database has Nullable=true.

So, while I can write a regular query with this model, such as:

  Dim ords = From ord In nwentities.Orders Where ord.OrderDate > New DateTime(2007, 7, 1)

and I can write my exact query in C# with no worries:

   var q=from cust in nw.Customers where cust.Orders.Any(o=>o.OrderDate>new DateTime(2007,7,1)) select cust;

I believe that in my scenario, I just need to rethink my query. But later…