All posts by Julie

Entity Data Model Associations: Where’s my Foreign Key?

 

    If you’ve been playing with Entity Framework and used the wizard to create a model from an existing database, you’ll see that the resulting model  looks something like this which is a small portion of the EDM built from Northwind.

     

     

    Compare the Orders Entity above to the Order table schema

     

     

    and you’ll quickly notice that the foreign keys for CustomerID, EmployeeID and ShipVia are "missing".

     

    We are used to using foreign keys as a means for defining relationships between tables and realizing them through the joins when we query. In our objects, we use the foreign keys as a means of finding our ways back to another object.

     

    In the Entity Framework we have a different method of finding our way from one table to another – Associations & Navigations (which come as a team).

     

    The model is more "holistic" than what we are used to.

     

    The fact that there is a relationship between Orders & Customers is identified by an association (which in the model is represented by the lines between the entities). The Association is named "FK_Orders_Customers" and describes that in this relationship there will be 0 or 1 customers and many orders. (Though I can’t figure out how any order could be without a customer…)

     

    So far this is only a part of the puzzle. Still nothing about CustomerID in there.

     

    Then there are the Navigation properties. If you are working with a Customer entity, it uses it’s Orders navigation property to know that it has a relationship with orders and that relationship is defined by the association.

     

    Okay so stop there a minute! Why have the Navigation property if we already have the Assocation?

    The association is not part of the customer (nor part of the order). It floats outside of those. So when you are wokring with a customer, you need one of the properties to give you access to the orders, therefore a navigation property. The association is shared because the Orders entity has a property that allows you to navigate over to the customer when you are working with the objects.

     

    When you create an Order object, while you don’t have Order.CustomerID, you do have Order.Customers, the Navigation property and that gets you to customers without having to say "where order.customerid=customer.customerid"

     

    (From o In nw.Orders Where o.OrderID = 10281 Select o.Customers).First

     

    This query will bring back  the customer for the specified order.

     

    If you looked at the xml which defines the Associaitons and the Navigation proeprties in the conceptual model, you will see that there are still no references to customerid. So how does it work?

     

    The fact that it is the customerid that is the foreignkey is defined in the Storage model. The Order entity in the storage model has a customerid and the FK_Orders_Customers association in the storage model has a reference to the CustomerID in it’s ReferentialContraint element.

     

    Finally in the mappings, there is an AssociationSetMapping that very clearly defines that CustomerID is the means for relating Orders and Customers.

     

    When the data is surfaced in the Conceptual model, all of the relationships have been sorted out and there is no longer (for most intents & purposes) a need to have the customerid in the order entity. We shouldn’t need to use that to navigate between orders and customers.

     

    Some people beg to differ on that matter.  See this forum thread [http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1663304&SiteID=1] and also note that in response to a list of Entity Framwork suggestions by Roger Jennings , Mike Pizzo replies to suggestion #9.

     

    9. Provide read-only access to foreign key values.

    This is actually a feature I’m fighting to get into our final milestone for V1. Can you describe the scenarios where this is used? Do you need the ability to query on the foreign key value, or simply expose it on the domain object?

     

    There’s one other thing that needs pointing out here. OrderDetails has an OrderID and a ProductID. So am I full of it? Well, that could be a different topic for debate, but in this case OrderID and ProductID are not only Foreign Keys but they are both Primary Keys in the Order Detail table as well. They become EntityKeys in the OrderDetails entity. Note the primary key icon for the two fields.

Vermont IT Jobs: Web Developer in Georgia Vermont

Web Developer –  PBM, (www.pbmproducts.com) an expanding, privately owned $200 million enterprise, specializes in manufacturing, distributing, and marketing consumer food, nutritional, and pharmaceutical products. The PBM family of companies has been named in Entrepreneur Magazine’s Hot 500 for 2007, the magazine’s annual ranking of America’s fastest growing businesses.


Headquartered near Charlottesville, Virginia, PBM consists of PBM Products, PBM Nutritionals, and PBM Pharmaceuticals. We have an opening at our Vermont location for an experienced Web Developer to meet our growing demands. We offer a competitive benefit package to include 3 weeks vacation, 401k with match, company incentive plan and more

Duties:
Responsible for contributing to all aspects of internet functionality, working on website design and development, user interface, navigation flow, layout of specific pages and creation of graphic elements to include:
• Maintenance of web sites.
• Ensures that all design elements, templates and finished solutions work within a variety of browser types and screen settings along with the constraints set by development and design.
• Participates in efforts to improve quality of web sites.
• Responsible for pay search advertising to ensure prominent visibility of business.
• Responsible for managing the web agenda.
• Works with marketing and development staff on style and approach for all projects.
• Provides design elements, comps, templates, and finished solutions.
• Designs solutions to business issues using web based infrastructure.
• Responsible for database design to support web solutions.

Job Requirements:
• 4-5 years experience in Web-based interface design, search engine optimization and/or server/database (back-end) maintenance
• Knowledge of HTML/CSS technologies and applications required.
• Knowledge of Photoshop, Javascript, and SQL required.
• Ability to learn & take on new projects essential.
• Ability to coordinate with internal customers.
• Effective written and verbal communication skills.
• Programming with ASP.net (using VB).
• Team player.
• Following are preferred requirements:
• Online charting software.
• Online store.
• Content management.
• Paid search words.

contact
Briarley Hazzard BHazzard@pbmproducts.com
HR Manager
PBM Products LLC
Tel:  (540) 832-3282 ext 118
Fax:  (540) 832-0193
PBM Nutritionals
Tel: (802) 527-0521 ext 217

Vermont IT Jobs: Web Developer at VT Teddy Bear in Shelburne VT

Web Applications and Content Developer at Vermont Teddy Bear in Shelburne, VT


Experienced web developer needed to develop functionality and content for our busy and award-winning ecommerce stores and other websites. The successful candidate will have:
 • Familiarity with ecommerce platforms such as Yahoo!Store, ePages, Demandware, or Microsoft Commerce Server.
 • HTML, hand-coding skills, CSS, Dreamweaver and PhotoShop. Good sense of graphic design and usability.
 • Web programming experience (JavaScript, Perl, Yahoo!Store RTML, PHP or ASP/ASP.Net a plus)
 • Knowledge of search-engine optimization best practices.
 • GoogleAnalytics or other web analytics experience.

Apply online at https://home.eease.com/recruit/?id=15679 or send resume to jobs@vtbear.com.  EOE.

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.