Dynamic Sql Bug for Left join hints on Inherited Types

Posts   
1  /  2
 
    
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 24-Mar-2011 18:42:33   

Other Details attached: A complaint table contains an optional id for a Beneficiary. A Beneficiary inherits from the Person Table. When building a typedList for Complaints, the Beneficiary entity is added with a LEFT JOIN hint but since Beneficiary inherits from Person it simply does an inner join with Person and adds a WHERE clause for PersonID not null effectively illiminating the intended effect of the LEFT JOIN.

LEFT JOIN [MEDIC_CMS].[dbo].[Beneficiary] [LPA_L2] ON
[LPA_L2].[BeneficiaryPersonID]=[MEDIC_CMS].[dbo].[Complaint].[BeneficiaryPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Person] [LPA_L1] ON
[LPA_L1].[PersonID]=[LPA_L2].[BeneficiaryPersonID])........ WHERE ( ( ( [LPA_L2].[BeneficiaryPersonID] IS NOT NULL)))

If the LEFT JOIN was extended to the inherited Person table - it would work. Ideally, the Sql would look like this though:

left JOIN ([MEDIC_CMS].[dbo].[Beneficiary] [LPA_L6]
JOIN [MEDIC_CMS].[dbo].[Person] [LPA_L5] ON
[LPA_L5].[PersonID]=[LPA_L6].[BeneficiaryPersonID]) ON [LPA_L6].[BeneficiaryPersonID]=[MEDIC_CMS].[dbo].[Complaint].[BeneficiaryPersonID] )) Without any need for a WHERE clause.

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 24-Mar-2011 21:24:12   

Thanks for the additional info - one last thing, please can you post the actual code that you use to generate the typed list, and details of how the list is set up in the designer ?

Thanks

Matt

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 24-Mar-2011 21:54:56   

Matt,

AppGlobal.complaints = new ComplaintsTypedList(); AppGlobal.complaints.Fill();

or

AppGlobal.complaints = new ComplaintsTypedList(); complaints.ObeyWeakRelations = true; AppGlobal.complaints.Fill();

The typed list designer details were included as a jpg in the first attachment. Simple Complaint - left join - Beneficiary (m:1) and there were no fields even selected from Beneficiary. Complaints w/o a beneficiaryID are not returned.

Thanks, rob

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 25-Mar-2011 05:36:35   

Hi Rob,

  • You are using an old version. I think that doesn't have to do with your situation, but I recommend you to update to the latest version.

  • Does that query actually affect your results? The INNER JOIN is just between Beneficiary and Person. The LEFT JOIN Remains in the Complaint table.

David Elizondo | LLBLGen Support Team
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 25-Mar-2011 14:52:32   

Daelmo,

Yeh, I didn't see any fixes in the release notes for this. It absolutely kills the left join functionality and affects my results by not bringing back ALL complaints. Because of the way LLBLGENPro generates the SQL the INNER join on the Person table ends up being an inner join on everything prior to that statement. I included as an attachment the CORRECT way to write the SQL (a set of parenths around the inner join with person) but you could get the same result by extending the LEFT keyword to the person table on a LEFT join hint of an inherited type. I also have other 'person' types that do the same thing when I add them -

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 25-Mar-2011 17:22:29   

Verified now - the latest version doesn't fix the bug.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 26-Mar-2011 05:53:26   

Ok. I asked because I tested the situation with similar scenario. I am receiving all the results from the fetch.

Could you prepare a tiny repro solution for us? (DDL db script, llblgen project, and a console app that reproduces the issue, no dlls, just that files zipped into one).

David Elizondo | LLBLGen Support Team
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 28-Mar-2011 15:52:21   

Can you post the sql from your scenario? tkssimple_smile

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 28-Mar-2011 17:08:11   

After reboot I now get these errors everywhere (after upgrading on Friday).

1) Error 4 An object reference is required for the non-static field, method, or property 'SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CompatibilityLevel.get' C:\Vault\DFMC_Repository\Rob\MEDIC\HI_PLATFORM\HI_PLATFORM\DaoClasses\CommonDaoBase.cs 66 4 HI_PLATFORM

2) Keyword not supported: 'sqlservercompatibilitylevel'.

--------------- MORE INFO ---------------

the LLBLGENPro .NET20.dll is version v2.0.50727

this is the code from DQE metadata:

public class DynamicQueryEngine : DynamicQueryEngineBase
{
    public static bool ArithAbortOn;
    public static bool UseNoLockHintOnSelects;

    public DynamicQueryEngine();

    public SqlServerCompatibilityLevel CompatibilityLevel
    {
        get;
        set;
    }
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 28-Mar-2011 17:34:02   

You're referencing a 3.0 runtime lib in a 3.1 generated code it seems. Please check whether you've referenced the wrong runtime lib.

Frans Bouma | Lead developer LLBLGen Pro
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 28-Mar-2011 17:42:49   

Figured. Tks

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 28-Mar-2011 18:00:59   

Still giving same error. All references were deleted to break the project. Then added back from the 3.1 folder.

The version on the dll properties is 3.1.11.221.

From the code at runtime I get RuntimeLibraryVersion.Build = 03182011

??

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 28-Mar-2011 20:43:00   

I'm 'dead-in-the-water' now. Is it possible to 'uninstall' 3.1 and go back to the old version?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 29-Mar-2011 07:13:11   

rap wrote:

I'm 'dead-in-the-water' now. Is it possible to 'uninstall' 3.1 and go back to the old version?

Yes, it's possible. Just uninstall it. However, I think you are missing something trivial in my opinion. Please try again. Create a project in v3.1, generate code to an empty folder and double check that the referenced assemblies (you can pick them up from the GAC tab, not necessary from the Browse one) are v3.1. Compile your generated projects. Then add you own project. Everything should compile just fine.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 29-Mar-2011 09:04:10   

Also, make sure 'specific version' in the properties of the runtime dll's in vs.net is set to 'true'. If it's set to 'false', vs.net will sometimes pick the wrong assembly. This is very annoying, but it's not something we can fix however. We had it too with our unittest projects, sometimes they kept referencing older runtime libs while everything was configured properly. Setting 'specific version' to true fixed it. Click the reference in vs.net and open the properties grid, you'll see the 'specific version' property there.

Frans Bouma | Lead developer LLBLGen Pro
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 29-Mar-2011 16:50:41   

Using VS 2010 - didn't see 'Specific version' tab. Here's the metadata definition - its giving a v3.1 path:

region Assembly SD.LLBLGen.Pro.DQE.SqlServer.NET20.dll, v2.0.50727

// C:\Program Files\Solutions Design\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\Net2.0\SD.LLBLGen.Pro.DQE.SqlServer.NET20.dll

endregion

using SD.LLBLGen.Pro.ORMSupportClasses; using System; using System.Collections.Generic; using System.Data.Common;

namespace SD.LLBLGen.Pro.DQE.SqlServer { public class DynamicQueryEngine : DynamicQueryEngineBase { public static bool ArithAbortOn; public static bool UseNoLockHintOnSelects;

    public DynamicQueryEngine();

    public SqlServerCompatibilityLevel CompatibilityLevel
    {
        get;
        set;
    }
    public static SqlServerCompatibilityLevel DefaultCompatibilityLevel
    {
        get;
        set;
    }
    public override FunctionMappingStore FunctionMappings
    {
        get;
    }
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 29-Mar-2011 17:29:21   

I removed the method below to compile (but it will regenerate)

/// <summary>Compatibility level used by the DQE. Default is SqlServer2000. To utilize SqlServer 2005 specific features, set this parameter /// to SqlServer2005, either through a setting in the .config file of your application or by setting this parameter once in your application. /// Compatibility level influences the query generated for paging, sequence name (@@IDENTITY/SCOPE_IDENTITY()), and usage of newsequenceid() in inserts. /// It also influences the provider to use. This way you can switch between SqlServer server client 'SqlClient' and SqlServer CE Desktop.</summary> /// <remarks>Setting this property will overrule a similar setting in the .config file. Don't set this property when queries are executed as /// it might switch factories for ADO.NET elements which could result in undefined behavior. Set this property at startup of your application as it's /// a global setting (affects all queries in your application using this DQE)</remarks> public static void SetSqlServerCompatibilityLevel(SqlServerCompatibilityLevel compatibilityLevel) { DynamicQueryEngine.CompatibilityLevel = compatibilityLevel; }

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 29-Mar-2011 18:03:04   

I figured it out. I was using the LLBLGenPro_NET40.exe for my project. I had to close the designer then delete the 3.0 folder and reset my shortcut to the 4.0.exe in the 3.1 folder. Then everything generated and compiled as expected.

So now back to the original SQL problem. Daelmo, could you post the sql from your test for me?

Thanks,

Rob

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 30-Mar-2011 10:18:43   

While we are waiting for David to come back with his sql.

Could you please attach a very simple lean repro? I see you have attached some DDL, but these are far from being lean. Just create the smallest possible repro that could do the job.

Thanks.

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 30-Mar-2011 15:52:24   

lol - it was in doing just that when the other bug thread i have going was generated. I would say that just a 'visual' inspection of the sql would clearly show the error, but I'm totally perplexed by the appearance of the 'where' clause since there is no predicate added. But besides that, the join sql is just plain wrong on inherited types. Placement of the ')' could fix it or extend the 'LEFT' keyword to the supertype could fix it. But the 'where' clause? I'll work on the test scenario.

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 30-Mar-2011 16:23:13   

Thanks for your understanding simple_smile

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 31-Mar-2011 08:43:25   

rap wrote:

I figured it out. I was using the LLBLGenPro_NET40.exe for my project. I had to close the designer then delete the 3.0 folder and reset my shortcut to the 4.0.exe in the 3.1 folder. Then everything generated and compiled as expected.

So now back to the original SQL problem. Daelmo, could you post the sql from your test for me?

Hi Rob. I don't have the tests here. I will post them during the day.

David Elizondo | LLBLGen Support Team
rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 31-Mar-2011 20:35:54   

OK - I have created one monster of a typed list now with three inherited types. Using proper aliasing I can now get the intended result set by extending the LEFT JOINS all the way through the projection for the inherited types(which did not work before due to aliasing). Attached is the designer(simple). The simplified code for three inherited types is: SELECT [MEDIC_CMS].[dbo].[Complaint].[ComplaintID] FROM (((((((((((((( [MEDIC_CMS].[dbo].[Person] [LPA_B1] INNER JOIN [MEDIC_CMS].[dbo].[Beneficiary] [LPA_B2] ON [LPA_B1].[PersonID]=[LPA_B2].[BeneficiaryPersonID]) RIGHT JOIN [MEDIC_CMS].[dbo].[Complaint] ON [LPA_B2].[BeneficiaryPersonID]=[MEDIC_CMS].[dbo].[Complaint].[BeneficiaryPersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Subject] [LPA_S4] ON [LPA_S4].[SubjectPersonID]=[MEDIC_CMS].[dbo].[Complaint].[SubjectPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Person] [LPA_S3] ON [LPA_S3].[PersonID]=[LPA_S4].[SubjectPersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Complainant] [LPA_C6] ON [LPA_C6].[ComplainantPersonID]=[MEDIC_CMS].[dbo].[Complaint].[ComplainantPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Person] [LPA_C5] ON [LPA_C5].[PersonID]=[LPA_C6].[ComplainantPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Case] ON
[MEDIC_CMS].[dbo].[Case].[CaseID]=[MEDIC_CMS].[dbo].[Complaint].[CaseID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_B7] ON
[LPA_B1].[PersonID]=[LPA_B7].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_S8] ON
[LPA_S3].[PersonID]=[LPA_S8].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_C9] ON
[LPA_C5].[PersonID]=[LPA_C9].[PersonID]) left JOIN [MEDIC_CMS].[dbo].[CaseResolution] ON
[MEDIC_CMS].[dbo].[Case].[CaseID]=[MEDIC_CMS].[dbo].[CaseResolution].[CaseID]) LEFT JOIN [MEDIC_CMS].[dbo].[Person] [LPA_B10] ON
[LPA_B10].[PersonID]=[LPA_B7].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Person] [LPA_S11] ON [LPA_S11].[PersonID]=[LPA_S8].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Person] [LPA_C12] ON [LPA_C12].[PersonID]=[LPA_C9].[PersonID])

Many Thanks!

rap
User
Posts: 72
Joined: 14-Dec-2010
# Posted on: 31-Mar-2011 23:11:11   

I spoke too soon. The bug is now simplified to this: All tables have left joins to Complaint and that works fine if I select complaintID I get all complaints back. But as soon as I check off any field in Beneficiary(or any of the inherited types but no other tables) it TACKS ON THE DREADED WHERE CLAUSE! It should not! WHERE ( ( ( [LPA_B2].[BeneficiaryPersonID] IS NOT NULL)))

full sql: size value='1']SELECT [LPA_C3].[ComplaintID], [LPA_B1].[FullName] FROM (((((((((((((((((((((((((((((( [MEDIC_CMS].[dbo].[Person] [LPA_B1] INNER JOIN [MEDIC_CMS].[dbo].[Beneficiary] [LPA_B2] ON [LPA_B1].[PersonID]=[LPA_B2].[BeneficiaryPersonID]) RIGHT JOIN [MEDIC_CMS].[dbo].[Complaint] [LPA_C3] ON [LPA_B2].[BeneficiaryPersonID]=[LPA_C3].[BeneficiaryPersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Subject] [LPA_S5] ON
[LPA_S5].[SubjectPersonID]=[LPA_C3].[SubjectPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Person] [LPA_S4] ON
[LPA_S4].[PersonID]=[LPA_S5].[SubjectPersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[Complainant] [LPA_C7] ON
[LPA_C7].[ComplainantPersonID]=[LPA_C3].[ComplainantPersonID]) INNER JOIN [MEDIC_CMS].[dbo].[Person] [LPA_C6] ON
[LPA_C6].[PersonID]=[LPA_C7].[ComplainantPersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[AllegationType] ON
[MEDIC_CMS].[dbo].[AllegationType].[AllegationTypeID]=[LPA_C3].[AllegationCode]) LEFT JOIN [MEDIC_CMS].[dbo].[ComplaintType] ON
[MEDIC_CMS].[dbo].[ComplaintType].[ComplaintTypeID]=[LPA_C3].[ComplaintTypeID]) LEFT JOIN [MEDIC_CMS].[dbo].[Disposition] ON
[MEDIC_CMS].[dbo].[Disposition].[DispositionID]=[LPA_C3].[DispositionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Case] ON
[MEDIC_CMS].[dbo].[Case].[CaseID]=[LPA_C3].[CaseID]) LEFT JOIN [MEDIC_CMS].[dbo].[ComplaintMedium] ON
[MEDIC_CMS].[dbo].[ComplaintMedium].[ComplaintMediumID]=[LPA_C3].[ComplaintMediumID]) LEFT JOIN [MEDIC_CMS].[dbo].[ComplaintSource] ON
[MEDIC_CMS].[dbo].[ComplaintSource].[ComplaintSourceID]=[LPA_C3].[ComplaintSourceID]) LEFT JOIN [MEDIC_CMS].[dbo].[ComplaintStatus] ON
[MEDIC_CMS].[dbo].[ComplaintStatus].[ComplaintStatusID]=[LPA_C3].[ComplaintStatusID]) LEFT JOIN [MEDIC_CMS].[dbo].[InappropriateCallType]
ON [MEDIC_CMS].[dbo].[InappropriateCallType].[InAppropriateCallID]=[LPA_C3].[InappropriateCallID]) LEFT JOIN [MEDIC_CMS].[dbo].[Priority] ON [MEDIC_CMS].[dbo].[Priority].[PriorityID]=[LPA_C3].[PriorityID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_B8] ON [LPA_B1].[PersonID]=[LPA_B8].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_S9] ON [LPA_S4].[PersonID]=[LPA_S9].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[PersonAddress] [LPA_C10] ON
[LPA_C6].[PersonID]=[LPA_C10].[PersonID]) LEFT JOIN [MEDIC_CMS].[dbo].[CaseResolution] ON
[MEDIC_CMS].[dbo].[Case].[CaseID]=[MEDIC_CMS].[dbo].[CaseResolution].[CaseID]) LEFT JOIN [MEDIC_CMS].[dbo].[Response] ON
[MEDIC_CMS].[dbo].[Case].[CaseID]=[MEDIC_CMS].[dbo].[Response].[CaseID]) LEFT JOIN [MEDIC_CMS].[dbo].[Address] [LPA_B11] ON
[LPA_B11].[AddressID]=[LPA_B8].[AddressID]) LEFT JOIN [MEDIC_CMS].[dbo].[Address] [LPA_S12] ON
[LPA_S12].[AddressID]=[LPA_S9].[AddressID]) LEFT JOIN [MEDIC_CMS].[dbo].[Address] [LPA_C13] ON
[LPA_C13].[AddressID]=[LPA_C10].[AddressID]) LEFT JOIN [MEDIC_CMS].[dbo].[DeclineReason] ON
[MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[DeclineReason].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[CaseClosure] ON
[MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[CaseClosure].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Advisement] ON
[MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[Advisement].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Education] ON [MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[Education].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Forfeiture] ON
[MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[Forfeiture].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Referral] ON [MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[Referral].[CaseResolutionID]) LEFT JOIN [MEDIC_CMS].[dbo].[Suspension] ON
[MEDIC_CMS].[dbo].[CaseResolution].[CaseResolutionID]=[MEDIC_CMS].[dbo].[Suspension].[CaseResolutionID]) WHERE ( ( ( [LPA_B2].[BeneficiaryPersonID] IS NOT NULL)))

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 01-Apr-2011 01:48:53   

Ok. I think I reproduced your situation. I tested before but it was slightly different.

Here are my results (with AdventureWorks):

LLBLGen RTL 3.1.11.318

TypedList (attached the .llblgenproj file) Its a SalesOrderHeader related to SalesPerson (subtype of Employee). The relation is m:1 and SalesPersonId is Optional.

Test1 (indicate Left join in the Designer, ObeyWeakRelation=false (default) in code):

[TestMethod]
public void CheckSalesOrderTypedListWithNullSalesPerson()
{           
    // the SalesPersonId is null for this SalesOrder
    IRelationPredicateBucket filter = new RelationPredicateBucket(SalesOrderHeaderFields.SalesOrderId == 43697);

    // fetch the TypedList
    SalesOrderHeaderAndSalesPersonTypedList ordersWithSalesPersonTpl = new SalesOrderHeaderAndSalesPersonTypedList();

    using (DataAccessAdapter adapter = new DataAccessAdapter())         
    {
        adapter.FetchTypedList(ordersWithSalesPersonTpl, filter.PredicateExpression);
    }

    // test: as a LEFT JOIN is emmited, we expect the order fetched
    // FAILS
    Assert.AreEqual(1, ordersWithSalesPersonTpl.Count);
    // test: the SalesPersonId should be null
    Assert.AreEqual(null, ordersWithSalesPersonTpl[0].SalesPersonId);
}

Generated SQL:


SELECT 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] AS [SalesOrderId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber], 
[LPA_L1].[BirthDate], 
[LPA_L2].[Bonus], 
[LPA_L2].[SalesPersonID] AS [EmployeeId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID] AS [SalesPersonId] 

FROM 
(
    ( [AdventureWorks].[HumanResources].[Employee] [LPA_L1]  
        INNER JOIN [AdventureWorks].[Sales].[SalesPerson] [LPA_L2]  
            ON  [LPA_L1].[EmployeeID]=[LPA_L2].[SalesPersonID] 
    ) 
    
    RIGHT JOIN [AdventureWorks].[Sales].[SalesOrderHeader]  
        ON  [LPA_L2].[SalesPersonID]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID]
) 

WHERE 
( 
    ( 
        ( [AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] = 43697)
    ) 
    
    AND 
    ( 
        ( [LPA_L2].[SalesPersonID] IS NOT NULL)
    )
)

Test2 (indicate Inner join in the Designer, ObeyWeakRelation=true (default) in code): The code is the same as above, the difference is I set_ ObeyWeakRelation=true_

Generated SQL:

SELECT 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] AS [SalesOrderId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber], 
[LPA_L1].[BirthDate], 
[LPA_L2].[Bonus], 
[LPA_L2].[SalesPersonID] AS [EmployeeId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID] AS [SalesPersonId] 

FROM 
(
    ( [AdventureWorks].[HumanResources].[Employee] [LPA_L1]  
        LEFT JOIN [AdventureWorks].[Sales].[SalesPerson] [LPA_L2]  
            ON  [LPA_L1].[EmployeeID]=[LPA_L2].[SalesPersonID]
    ) 
    
    INNER JOIN [AdventureWorks].[Sales].[SalesOrderHeader]  
        ON  [LPA_L2].[SalesPersonID]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID]
) 

WHERE 
( 
    ( 
        ( [AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] = 43697)
    ) 
    
    AND 
    ( 
        ( [LPA_L2].[SalesPersonID] IS NOT NULL)
    )
)

**Test3 **(indicate Left join in the Designer, ObeyWeakRelation=true (default) in code): code is the same as above, the relation typedlist in the designer is LEFT.

Generated SQL:

SELECT 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] AS [SalesOrderId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber], 
[LPA_L1].[BirthDate], [LPA_L2].[Bonus], 
[LPA_L2].[SalesPersonID] AS [EmployeeId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID] AS [SalesPersonId] 

FROM 
(
    ( [AdventureWorks].[HumanResources].[Employee] [LPA_L1]  
        LEFT JOIN [AdventureWorks].[Sales].[SalesPerson] [LPA_L2]  
            ON  [LPA_L1].[EmployeeID]=[LPA_L2].[SalesPersonID]
    ) 
    
    RIGHT JOIN [AdventureWorks].[Sales].[SalesOrderHeader]  
        ON  [LPA_L2].[SalesPersonID]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID]
) 

WHERE 
( 
    ( 
        ( [AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] = 43697)
    ) 
    
    AND 
    ( 
        ( [LPA_L2].[SalesPersonID] IS NOT NULL)
    )
)

**Test4 **(test this with DynamicList)

[TestMethod]
public void CheckSalesOrderDynamicListWithNullSalesPerson()
{
    // setyp the fields
    ResultsetFields fields = new ResultsetFields(7);
    fields.DefineField(SalesOrderHeaderFields.SalesOrderId, 0);
    fields.DefineField(SalesOrderHeaderFields.OrderDate, 1);
    fields.DefineField(SalesOrderHeaderFields.SalesOrderNumber, 2);
    fields.DefineField(SalesOrderHeaderFields.SalesPersonId, 3);
    fields.DefineField(SalesPersonFields.BirthDate, 4);
    fields.DefineField(SalesPersonFields.Bonus, 5);
    fields.DefineField(SalesPersonFields.EmployeeId, 6);

    // the SalesPersonId is null for this SalesOrder
    IRelationPredicateBucket filter = new RelationPredicateBucket(SalesOrderHeaderFields.SalesOrderId == 43697);
    filter.Relations.Add(SalesOrderHeaderEntity.Relations.SalesPersonEntityUsingSalesPersonId);

    // fetch the DynamicList, obey weak relations to force a LEFT JOIN
    filter.Relations.ObeyWeakRelations = true;
    DataTable results = new DataTable();

    using (DataAccessAdapter adapter = new DataAccessAdapter())
    {
        adapter.FetchTypedList(fields, results, filter);
    }

    // test: as a LEFT JOIN is emmited, we expect the order fetched
    Assert.AreEqual(1, results.Rows.Count);
    // test: the SalesPersonId should be null
    Assert.AreEqual(null, results.Rows[0]["SalesPersonId"]);
}

Generated SQL:

SELECT 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] AS [SalesOrderId], 
[AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber], 
[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID] AS [SalesPersonId], 
[LPA_L1].[BirthDate], [LPA_L2].[Bonus], 
[LPA_L2].[SalesPersonID] AS [EmployeeId] 

FROM 
(
    ( [AdventureWorks].[Sales].[SalesPerson] [LPA_L2]  
        RIGHT JOIN [AdventureWorks].[Sales].[SalesOrderHeader]  
            ON  [LPA_L2].[SalesPersonID]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesPersonID]
    ) 
    
    RIGHT JOIN [AdventureWorks].[HumanResources].[Employee] [LPA_L1]  
        ON  [LPA_L1].[EmployeeID]=[LPA_L2].[SalesPersonID]
) 
        
WHERE 
( 
    ( [AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID] = 43697) 
    
    AND 
    ( 
        ( [LPA_L2].[SalesPersonID] IS NOT NULL)
    )
)

In all cases, the fist Assert fails. I admit this is not the apparently expected results, we will see into this.

David Elizondo | LLBLGen Support Team
1  /  2