Search code examples
c#subsonic

What happens if I use `AndExpression` without `CloseExpression`


I have this subsonic query:

Select.AllColumnsFrom<Data.Group>()
    .Where(Data.Group.GroupIDColumn).IsNotEqualTo(m_sRootGroupID)
    .AndExpression(Data.Group.Columns.OwnerPersonID).IsEqualTo(gUserID)
    .OrExpression(Data.Group.Columns.OwnerPersonID).IsEqualTo(gUserContextID)
    .AndExpression(Data.Group.Columns.IsCallList).IsEqualTo(true)
    .CheckLogicalDelete().ExecuteTypedList<Groups>();

At first sight, it seems that the previous developer has mistaken AndExpression for And. I know that AndExpression encloses the preceding statements inside parentheses.

How is the above subsonic query be translated into SQL?

This is what I'm thinking:

SELECT * 
FROM tblGroups
WHERE GroupID <> m_sRootGroupID
  AND ( --first [AndExpression]
    OwnerPersonID = `gUserID
    OR ( -- [OrExpression]
        OwnerPersonID = gUserContextID
        AND ( -- second [AndExpression]
             IsCallList = true
        )
    )
) -- first [AndExpression]
AND ISNULL(IsDeleted,0) = 0

How is the ..Expressions being handled if there is no CloseExpression?


Solution

  • As has been pointed out by @MikeWalsh in his answer, calling .ToString() shows what the subsonic query looks like when translated to SQL.

    Thus, the above code can be translated into:

    SELECT * --subsonic specifies all columns
    
     FROM [dbo].[tblGroups]
     WHERE [dbo].[tblGroups].[GroupID] <> @GroupID0
     AND ([dbo].[tblGroups].[OwnerPersonID] = @OwnerPersonID1
    )
     OR ([dbo].[tblGroups].[OwnerPersonID] = @OwnerPersonID3
    )
     AND ([dbo].[tblGroups].[IsCallList] = @IsCallList5
    )
     AND ([dbo].[tblGroups].[IsDeleted] IS NULL OR [dbo].[tblGroups].[IsDeleted] = 0)
    

    Which tells us that calling AndExpression/OrExpression without a CloseExpression, subsonic assumes there is only one condition inside the AND statement enclosed in parentheses, thus:

    .AndExpression(Data.Group.Columns.OwnerPersonID).IsEqualTo(gUserID)

    is translated into: AND ([dbo].[tblGroups].[OwnerPersonID] = @OwnerPersonID1)


    EDIT:

    That's what I thought. However, when using this subsonic query:

    Select.AllColumnsFrom<Data.Group>()
        .Where(Data.Group.GroupIDColumn).IsNotEqualTo(m_sRootGroupID)
        .AndExpression(Data.Group.Columns.OwnerPersonID).IsEqualTo("someownerID")
        .Or(Data.Group.Columns.OwnerPersonID).IsEqualTo("SomeContextID")
        .AndExpression(Data.Group.Columns.IsCallList).IsEqualTo(true)
        .CheckLogicalDelete().ToString();
    

    I got this query (formatting mine):

    SELECT <All columns>
    
     FROM [dbo].[tblGroups]
     WHERE [dbo].[tblGroups].[GroupID] <> @GroupID0
     AND ( -- first [AndExpression]
       [dbo].[tblGroups].[OwnerPersonID] = @OwnerPersonID1 --Data.Group.Columns.OwnerPersonID).IsEqualTo("someownerID")
       OR [dbo].[tblGroups].[OwnerPersonID] = @OwnerPersonID2 --.Or(Data.Group.Columns.OwnerPersonID).IsEqualTo("SomeContextID")
     ) -- close for first [AndExpression]
     AND ( -- second [AndExpression]
        [dbo].[tblGroups].[IsCallList] = @IsCallList4
     ) -- close for second [AndExpression]
     AND ([dbo].[tblGroups].[IsDeleted] IS NULL OR [dbo].[tblGroups].[IsDeleted] = 0)
    

    As you can see, the first AndExpression includes .Or(Data.Group.Columns.OwnerPersonID).IsEqualTo("SomeContextID") inside its parentheses, which implies that ..Expression's continue to include preceding statements inside the parentheses until it finds another ..Expression (as we can observe on the second AndExpression.

    Adding additional And/Or methods after the first AndExpression strengthens the proof that

    Using ..Expression's without CloseExpression will encapsulate preceding statements in parentheses until another ..Expression is called.