views:

1116

answers:

4

I have two tables in a one to many relationship. (products and qty break pricing). At the database level I cannot create a relationship between the two tables. I brought those two tables into LINQ and created the association manually.

I need to do a big LINQ query and have the tables be joined. My problem is it's not using a join to get the data. LINQ is using 1 select on the main table, then 1 select for each row in that main table.

Dim db As New LSSStyleDataContext(connString)

Dim options As New DataLoadOptions()
options.LoadWith(Function(c As commerce_product) c.commerce_qty_breaks)
db.LoadOptions = options

Dim dbProducts = (From prods In db.commerce_products).ToList

Any thoughts on why this might be? Thanks! Paul

EDIT: here are the two tables:

CREATE TABLE [dbo].[commerce_product](
    [pf_id] [int] NOT NULL,
    [name] [varchar](500) COLLATE SQL_Latin1_General_CP1_CI_AS         
    [description] [text] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [restricted] [varchar](5) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   CONSTRAINT [PK_commerce_product_1] PRIMARY KEY NONCLUSTERED 
 (
    [pf_id] ASC
  ) ON [PRIMARY]
  ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

And the other table:

CREATE TABLE [dbo].[commerce_qty_break](
    [pf_id] [int] NOT NULL,
    [sku] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [qty] [int] NOT NULL,
    [list_price] [int] NOT NULL,
    [break_id] [int] NOT NULL,
    CONSTRAINT [PK_commerce_qty_break] PRIMARY KEY CLUSTERED 
    (
    [pf_id] ASC,
    [qty] ASC,
    [break_id] ASC
    ) ON [PRIMARY]
    ) ON [PRIMARY]

The DBML is straight forward with only the two tables. I created an association between the two tables, "commerce_product" being the parent and "commerce_qty_break" being the child joined by "PF_ID".

I can write something like this:

Dim dbproducts = From prods In db.commerce_products _
    Join qtys In db.commerce_qty_breaks On prods.pf_id Equals qtys.pf_id _
    Select prods

And i see that it joins on the table in the query, but as soon as i try and spin through the "qty_breaks" it starts executing selects to get that info.

I'm totally stumped.

Edit 2: Here is the DBML:

<?xml version="1.0" encoding="utf-8"?>
<Database Name="LSScommerceDB_DevB" Class="LSSStyleDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007"&gt;
  <Connection Mode="AppSettings" ConnectionString="***" SettingsObjectName="HSLPriceUpdate.My.MySettings" SettingsPropertyName="LSScommerceDB_DevBConnectionString" Provider="System.Data.SqlClient" />
  <Table Name="dbo.commerce_product" Member="commerce_products">
    <Type Name="commerce_product">
      <Column Name="pf_id" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
      <Column Name="name" Type="System.String" DbType="VarChar(500)" CanBeNull="true" />
      <Column Name="description" Type="System.String" DbType="Text" CanBeNull="true" UpdateCheck="Never" />
      <Column Name="list_price" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="image_file" Type="System.String" DbType="VarChar(255)" CanBeNull="true" />
      <Column Name="image_width" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="image_height" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="sale_price" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="sale_start" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
      <Column Name="sale_end" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
      <Column Name="attr_label1" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="attr_label2" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="attr_label3" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="attr_label4" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="attr_label5" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="sku" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
      <Column Name="UOM" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
      <Column Name="Sell_Pack" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
      <Column Name="mfg_model_number" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
      <Column Name="mfg_id" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="logo_file" Type="System.String" DbType="VarChar(255)" CanBeNull="true" />
      <Column Name="drop_ship" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
      <Column Name="lead_time" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="hazard_flag" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
      <Column Name="publish_date" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
      <Column Name="restricted" Type="System.String" DbType="VarChar(5)" CanBeNull="true" />
      <Association Name="commerce_product_commerce_qty_break" Member="commerce_qty_breaks" ThisKey="pf_id" OtherKey="pf_id" Type="commerce_qty_break" />
    </Type>
  </Table>
  <Table Name="dbo.commerce_qty_break" Member="commerce_qty_breaks">
    <Type Name="commerce_qty_break">
      <Column Name="pf_id" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
      <Column Name="sku" Type="System.String" DbType="VarChar(100) NOT NULL" CanBeNull="false" />
      <Column Name="qty" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
      <Column Name="list_price" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
      <Column Name="sale_price" Type="System.Int32" DbType="Int" CanBeNull="true" />
      <Column Name="sale_start" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" />
      <Column Name="sale_end" Type="System.DateTime" DbType="DateTime" CanBeNull="true" />
      <Column Name="break_id" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
      <Association Name="commerce_product_commerce_qty_break" Member="commerce_product" ThisKey="pf_id" OtherKey="pf_id" Type="commerce_product" IsForeignKey="true" />
    </Type>
  </Table>
</Database>

EDIT 3: Apparently this is only an issue in SQL 2000. SQL 2008 works fine. I have other tables that do eager loading in SQL 2000 and i can't figure out what the difference is between these two tables.

A: 

hey, not sure i completely understood your question, but here's some info,

By default LINQ to SQL adopts lazy binding, meaning it won't query the database until it needs to. That's why you're getting multiple queries.

There are few things you can do to avoid multiple hits on the DB:

  1. You can turn off lazy binding globally in the LINQ to SQL designer. But then you'll always perform JOINS if your tables have any relationships.

  2. You do the JOIN manually in the LINQ query. If you do this, you'll have to specify which "fields" you want to return in your LINQ query to get back the JOINED data. And if you do this, then you'll be returning an Anonymous type

NOTE: LINQ to SQL doesn't support LEFT JOINS. If you google LINQ to SQL Left Joins you'll see plenty of info on this particular subject

cheers, and good luck!

andy
Lazy loading doesn't apply to his situation, becaue he is using the DataLoadOptions
RobS
do my recommendations still apply, or is there something else going on?
andy
It's hard to say without seeing the DBML/Entity definitions - it's unclear how he manually created the association. I'd agree with #2 if he is trying to return a subset - depends how he has modelled it
RobS
hmm...LINQ To SQL, you gotta love it
andy
Rob, that is exactly why i'm stumped.
Paul Lemke
lemkepf,could you not apply #2?
andy
I'm going to try #2 today sometime.
Paul Lemke
I'm doing something wrong... I execute this with the load options: Dim dbproducst = (From p In db.commerce_products Select New With {.product = p, .qtybreak = p.commerce_qty_breaks}).ToListIt executes 1 select for commerce_product, then 10000+ to get the commerce_qty_breaks.
Paul Lemke
+1  A: 

Hi There. I created a VB console app and created the schema as you have it here.

Also - the relationship is PK -> PK so does this mean it's supposed to be a one-to-one relationship?

I populated the tables with a row each (see below) and ran the code you've listed above. I ran SQL Profiler and it only queried once:

SELECT [t0].[pf_id], [t0].[name], [t0].[description], [t0].[restricted], [t1].[pf_id] AS [pf_id2], [t1].[sku], [t1].[qty], [t1].[list_price], [t1].[break_id], ( SELECT COUNT(*) FROM [dbo].[commerce_qty_break] AS [t2] WHERE [t2].[pf_id] = [t0].[pf_id] ) AS [value] FROM [dbo].[commerce_product] AS [t0] LEFT OUTER JOIN [dbo].[commerce_qty_break] AS [t1] ON [t1].[pf_id] = [t0].[pf_id] ORDER BY [t0].[pf_id], [t1].[qty], [t1].[break_id]

I wanted to make sure that the Data Options was forcing a deep load, so I added some extra code - here's the full code I used (and only the single query as above was traced):

Dim options As New DataLoadOptions()

options.LoadWith(Function(c As commerce_product) c.commerce_qty_breaks)
db.LoadOptions = options

Dim dbProducts = (From prods In db.commerce_products).ToList

Dim dbProduct = dbProducts.First().commerce_qty_breaks
Dim x = dbProduct.First().list_price

Here's the test data:

INSERT INTO [Test].[dbo].[commerce_product] ([pf_id],[name],[description],[restricted]) VALUES (1,'Test','Test','Test')
GO
INSERT INTO [Test].[dbo].[commerce_qty_break] ([pf_id],[sku],[qty],[list_price],[break_id]) VALUES (1,'22',1,1,1)
GO
RobS
This was run against SQL Server 2008 from Visual Studio 2008 SP 1 (and running on .Net Framework 3.5)
RobS
Interesting... I copied the database to a SQL 2008 server and it worked just fine! The database currently lives on SQL 2000. Why would this matter? I thought the only diff for linq to sql was the "take" method?
Paul Lemke
Yeah same as my understanding also. I'll point it at a SQL 2000 db and see if I get the same result.
RobS
A: 

Go grab LINQPad. It's great for playing with stuff like this.

Did you try something like this?

Dim dbproducts = From prods In db.commerce_products _
    Join qtys In db.commerce_qty_breaks On prods.pf_id Equals qtys.pf_id _
    Select new {prods, qtys}
D. Lambert
A: 

I ended up using the entity framework and all is well.

Paul Lemke