Relations

Posts   
 
    
Isz
User
Posts: 108
Joined: 26-Jan-2006
# Posted on: 05-Jun-2008 21:14:33   

Hi,

We downloaded the latest rc and have noticed an issue with joining fields on related tables. A table AccountRegister containing a nullable FK, InvestementVehiclePortfolioId creates a left join correctly to InvestementVehiclePortfolio, but a subsequent join from InvestementVehiclePortfolio to InvestmentVehicle on a FK InvestementVehicleId, that is not nullable, results in an undesirable inner join, and thus does not return the expected result set.

The following query shows this expression:


            e.QueryableSource = from ar in this._linqMetaData.AccountRegister
                                select new
                                {
                                    AccountRegisterId = ar.AccountRegisterId,
                                    TransactionDate = ar.TransactionDate,
                                    Amount = ar.Amount,
                                    AccountRegisterTypeName = ar.AccountRegisterType.Name,
                                    InvestmentVehicleName = ar.InvestmentVehiclePortfolio.InvestmentVehicle.Name,
                                    Notes = ar.Notes,
                                    FederalReferenceNumber = ar.FederalReferenceNumber,
                                    TransmitDate = ar.DirectiveTransmittedDate,
                                    EnteredBy = ar.CreateSecurityUser.Name,
                                    EnteredDate = ar.CreateDate,
                                    ApprovedBy = ar.ProofedBySecurityUser.Name
                                }
                                ;


The profile:


SELECT DISTINCT TOP(@top0) COUNT(*) AS [LPAV_] FROM (
    SELECT [LPA_L3].[account_register_id] AS [AccountRegisterId], 
    [LPA_L3].[transaction_date] AS [TransactionDate], 
    [LPA_L3].[amount] AS [Amount], 
    [LPA_L2].[name] AS [AccountRegisterTypeName], 
    [LPA_L5].[name] AS [InvestmentVehicleName], 
    [LPA_L3].[notes] AS [Notes], 
    [LPA_L3].[federal_reference_number] AS [FederalReferenceNumber], 
    [LPA_L3].[directive_transmitted_date] AS [TransmitDate], 
    [LPA_L6].[name] AS [EnteredBy], 
    [LPA_L3].[create_date] AS [EnteredDate], 
    [LPA_L7].[name] AS [ApprovedBy] 
        FROM ((((( [dbo].[account_register_type] [LPA_L2]  
        INNER JOIN [dbo].[account_register] [LPA_L3]  ON  [LPA_L2].[account_register_type_id]=[LPA_L3].[account_register_type_id]) 
        LEFT JOIN [dbo].[investment_vehicle_portfolio] [LPA_L4]  ON  [LPA_L4].[investment_vehicle_portfolio_id]=[LPA_L3].[investment_vehicle_portfolio_id]) 
        INNER JOIN [dbo].[investment_vehicle] [LPA_L5]  ON  [LPA_L5].[investment_vehicle_id]=[LPA_L4].[investment_vehicle_id]) 
        INNER JOIN [dbo].[security_user] [LPA_L6]  ON  [LPA_L6].[user_id]=[LPA_L3].[create_user_id]) 
        LEFT JOIN [dbo].[security_user] [LPA_L7]  ON  [LPA_L7].[user_id]=[LPA_L3].[proofed_by_user_id])) [LPA_L1]


From the above, the left join to InvestmentVehiclePortfolio is correct, but the following join to InvestmentVehicle appears not so.

To work around this, we wrote our own joins, as the following code show:


            e.QueryableSource = from ar in this._linqMetaData.AccountRegister
                                join art in this._linqMetaData.AccountRegisterType on ar.AccountRegisterTypeId equals art.AccountRegisterTypeId
                                join ivp in this._linqMetaData.InvestmentVehiclePortfolio on ar.InvestmentVehiclePortfolioId
                                    equals ivp.InvestmentVehiclePortfolioId into investmentVehiclePortfolios
                                from investmentVehiclePortfolio in investmentVehiclePortfolios.DefaultIfEmpty()
                                join iv in this._linqMetaData.InvestmentVehicle on investmentVehiclePortfolio.InvestmentVehicleId
                                    equals iv.InvestmentVehicleId into investmentVehicles
                                from investmentVehicle in investmentVehicles.DefaultIfEmpty()
                                select new
                                {
                                    AccountRegisterId = ar.AccountRegisterId,
                                    TransactionDate = ar.TransactionDate,
                                    Amount = ar.Amount,
                                    AccountRegisterTypeName = art.Name,
                                    InvestmentVehicleName = investmentVehicle.Name,
                                    Notes = ar.Notes,
                                    FederalReferenceNumber = ar.FederalReferenceNumber,
                                    TransmitDate = ar.DirectiveTransmittedDate,
                                    EnteredDate = ar.CreateDate,
                                }
                                ;


In the codes, I saw this info...

Left/Inner join switch on relations resulting from related entity references. The relations resulting from related entity references in the query (e.g. o.Customer), will be INNER joins or equi-joins, if the FK fields aren't nullable. If the FK fields (or one of them) is nullable, the relation is a LEFT join towards the FK side (so FK LEFT JOIN PK).

I am not sure if I am to interpret **(or one of them) ** to mean if there is one left join, all others to follow it should be left as well.

Is this a bug?

Thanks!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39612
Joined: 17-Aug-2003
# Posted on: 05-Jun-2008 21:16:53   

I've to think this through. I don't think it's as easy as saying 'it all should be left joins', but you do have a point.

Frans Bouma | Lead developer LLBLGen Pro
Isz
User
Posts: 108
Joined: 26-Jan-2006
# Posted on: 05-Jun-2008 22:20:13   

IIRC, previously it was up to the developer to specify the subsequent join hint, so I agree it doesn't seem like they should all be leftees, but my db experience can't answer for sure flushed .

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39612
Joined: 17-Aug-2003
# Posted on: 06-Jun-2008 09:55:16   

It's similar to the weak relations feature I think. If you switch that on, and one relation is weak, everything joined to that is also considered weak, otherwise the inner joins will make the whole point moot. simple_smile

I'm not sure if this is easily fixable, but I'll give it a try simple_smile .

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39612
Joined: 17-Aug-2003
# Posted on: 06-Jun-2008 12:12:36   

With a query on some adventureworks entities I could reproduce this. (Product - ProductSubCategory - ProductCategory). It returned less entities than expected because the inner join caused the whole set to become an innerjoin.

Will see how I can fix this simple_smile

(edit). hmm... It's a bit complicated. If there are solely m:1 relations, it's easy: just always set it as FK LEFT JOIN PK. This always works, and gives the correct set, regardless if FK is nullable or not.

However, this then doesn't work: ... bars = a.Foo.Bar.Count(), ...

where a.Foo = m:1, nullable FK and Foo.bar is 1:n, nullable FK in Bar. If Foo.Bar is added as FK LEFT JOIN PK, it will add all Bars to all rows, which isn't what we want. It has to be PK LEFT JOIN FK in that situation...

I hope I can borrow some of the relationcollection code which already does check on this...

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39612
Joined: 17-Aug-2003
# Posted on: 06-Jun-2008 13:58:09   

Fixed it. simple_smile

Likely the RTM build btw.

Frans Bouma | Lead developer LLBLGen Pro