Aliases breaking my WHERE clause

Posts   
 
    
tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 29-Oct-2008 07:42:00   

I'm attempting to upgrade a project to the latest (as of today) v2.5 LLBLGen. After some minor changes to get the compiling done, it turns out my TypedLists are now producing invalid queries. I have a hierarchy of:

Contact -> Individual -> Business -> Committee

I have typed lists for each subtype that gather data that includes fields from the super type Contact.

From the look of the generated SQL, it seems that it now produces table aliases, but does not use those aliases in the WHERE clause.

Here is the generated SQL, followed by some of the c# code used to produce this.


SELECT [dbo].[Address].[Street] AS [Address], [dbo].[Address].[City], [dbo].[Address].[State], [dbo].[Address].[ZipCode], SUM([dbo].[Contribution].[Amount]) AS [Amount], [dbo].[Email].[EmailAddress], [LPA_L2].[Title], [LPA_L2].[FirstName], [LPA_L2].[LastName], [LPA_L2].[Init], [LPA_L2].[Suffix], [LPA_L2].[Occupation], [dbo].[Phone].[PhoneNum] AS [Phone], [LPA_L2].[Employer], [LPA_L2].[ContactID], [LPA_L1].[ContactType], [LPA_L1].[Notes], [LPA_L1].[UserAdded], [LPA_L1].[Salutation], [LPA_L1].[Envelope], [LPA_L1].[DoNotMail], [LPA_L1].[DefaultAddressID], [LPA_L1].[DefaultPhoneID], [LPA_L1].[DefaultEmailID], [LPA_L1].[CreatedOn] FROM (((((( [dbo].[Contact] [LPA_L1]  INNER JOIN [dbo].[Individual] [LPA_L2]  ON  [LPA_L1].[ContactID]=[LPA_L2].[ContactID]) LEFT JOIN [dbo].[Address]  ON  [LPA_L2].[ContactID]=[dbo].[Address].[ContactID] AND ( ( [dbo].[Address].[AddressTypeID] = [LPA_L1].[DefaultAddressID]))) LEFT JOIN [dbo].[Phone]  ON  [LPA_L2].[ContactID]=[dbo].[Phone].[ContactID] AND ( ( [dbo].[Phone].[PhoneTypeID] = [LPA_L1].[DefaultPhoneID]))) LEFT JOIN [dbo].[Email]  ON  [LPA_L2].[ContactID]=[dbo].[Email].[ContactID] AND ( ( [dbo].[Email].[EmailTypeID] = [LPA_L1].[DefaultEmailID]))) LEFT JOIN [dbo].[Contribution]  ON  [LPA_L2].[ContactID]=[dbo].[Contribution].[ContactID]) LEFT JOIN [dbo].[ClientCommittee]  ON  [dbo].[ClientCommittee].[ClientCommitteeID]=[dbo].[Contribution].[CommitteeID]) WHERE ( ( ( [dbo].[Contribution].[CommitteeID] = @CommitteeID1) AND [dbo].[Contact].[DoNotMail] = @DoNotMail2 OR ( [dbo].[Contact].[DoNotMail] = @DoNotMail3 AND [dbo].[Contact].[UserAdded] = @UserAdded4)) AND ( ( [LPA_L2].[ContactID] IS NOT NULL))) GROUP BY [dbo].[Address].[Street], [dbo].[Address].[City], [dbo].[Address].[State], [dbo].[Address].[ZipCode], [dbo].[Email].[EmailAddress], [LPA_L2].[Title], [LPA_L2].[FirstName], [LPA_L2].[LastName], [LPA_L2].[Init], [LPA_L2].[Suffix], [LPA_L2].[Occupation], [dbo].[Phone].[PhoneNum], [LPA_L2].[Employer], [LPA_L2].[ContactID], [LPA_L1].[ContactType], [LPA_L1].[Notes], [LPA_L1].[UserAdded], [LPA_L1].[Salutation], [LPA_L1].[Envelope], [LPA_L1].[DoNotMail], [LPA_L1].[DefaultAddressID], [LPA_L1].[DefaultPhoneID], [LPA_L1].[DefaultEmailID], [LPA_L1].[CreatedOn] ORDER BY [LPA_L2].[LastName] ASC

c#


            IEntityFields fields = GetResultSet();
            IGroupByCollection groupByClause = DALHelper.GetGroupByClause(fields);
            CamelotAccess.DaoClasses.TypedListDAO dao = new CamelotAccess.DaoClasses.TypedListDAO();

            // Create Relations
            IRelationCollection rc = GetRelations();

            // This filter added to only include current committees in Aggregate
            IPredicateExpression filter = new PredicateExpression();
            DALHelper.ApplyCommitteeFilter(ref filter, ContributionFields.CommitteeID, CurrentCommittees);
            if (HideDNS)
                filter.AddWithAnd(ContactFields.DoNotMail == false);
            if (ShowUserAdded)
            {
                if (HideDNS)
                    filter.AddWithOr(ContactFields.DoNotMail == false & ContactFields.UserAdded == true);
                else
                    filter.AddWithOr(ContactFields.UserAdded == true);
            }

            // Data Retreival
            DataTable itl = this.Clone();
            if (this.Count == 0)
            {
                itl = this;
                // 'this' is base class that sits inbetween my IndividualTypedList generated class,
                //  and the TypedListBase from LLBLGen. 
                //  like so: IndividualTypedList : ContribuTracTypedList : TypedListBase
                if(pageCount != 0)
                    dbRowCount = GetDbCount(filter, rc);
            }

            SortExpression sort = GetSortExpression();
            dao.GetMultiAsDataTable(fields, itl, 0, sort, filter, rc, true, groupByClause, null, pageCount > 0 ? 1 : 0, this.Count == 0 ? pageCount : dbRowCount - PageCount);


Again, this was production code that worked great before the upgrade/DAL regeneration. Any help on this would be appreciated.

Thanks!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 29-Oct-2008 09:50:03   

Would you please attach the project file? Opne a HelpDesk thread for provacy if you want.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 29-Oct-2008 10:05:16   

Also, from which version did you upgrade? As it's adviced to upgrade to v2.6 instead, as v2.6 has a rewritten aliasing system.

Frans Bouma | Lead developer LLBLGen Pro
tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 29-Oct-2008 11:25:20   

I went ahead and downloaded 2.6...and upgraded the source to build with it. Unfortunately, the results are exactly the same.

I am posting the project file on the helpdesk.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 30-Oct-2008 10:56:36   

Would you please change the following piece of code?

if (HideDNS) filter.AddWithAnd(ContactFields.DoNotMail == false); if (ShowUserAdded) { if (HideDNS) filter.AddWithOr(ContactFields.DoNotMail == false & ContactFields.UserAdded == true); else filter.AddWithOr(ContactFields.UserAdded == true); }

Into:

if (HideDNS)
                filter.AddWithAnd(IndividualFields.DoNotMail == false);
            if (ShowUserAdded)
            {
                if (HideDNS)
                    filter.AddWithOr(IndividualFields.DoNotMail == false & IndividualFields.UserAdded == true);
                else
                    filter.AddWithOr(IndividualFields.UserAdded == true);
            }
tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 30-Oct-2008 19:12:24   

Changing the code does work, however that code is generic code used for all subtypes of Contact. It is base class code that retrieves rows for Individual, Business, or Committee, so altering the code here is not an option. Can I turn Aliasing off? The query generated by 2.0 code had no aliasing, and all worked fine.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 30-Oct-2008 19:13:47   

tomahawk wrote:

Changing the code does work, however that code is generic code used for all subtypes of Contact. It is base class code that retrieves rows for Individual, Business, or Committee, so altering the code here is not an option. Can I turn Aliasing off? The query generated by 2.0 code had no aliasing, and all worked fine.

Unfortunately, no. That it worked in 2.0 is actually just luck, there are cases where it doesn't work and different code was needed. As you specify a field on the base class, even though you pass in a typedlist with fields from derived types, it might fail. In v2.6 we rewrote the code to make it more flexible, however there is simply not enough information to make a proper guess. Sure, in v2.0 it worked, but it did a very naive guess for aliases, which worked out OK in your particular situation, however it breaks in others.

The main reason this happens is that the entities in the typedlist aren't aliased. If you specify an alias per entity in the typedlist, (and thus also with fields in filters), it should work OK as well, though I haven't tested it. This is probably more code to do, so likely not an option, but unfortunately, this is the way it is... the core issue is about guessing aliases for filter fields which simply have no alias specified and therefore rely on the fact how their entity in the join is aliased, but that entity isn't present in the typedlist, it's subtype is, and this gives mismatches and no alias is found in the original case of your code.

Frans Bouma | Lead developer LLBLGen Pro
tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 30-Oct-2008 19:24:41   

(edit) sorry Frans, for some reason when I read the post at home it was missing most of your text, so I only got the couple sentences. Understood I'll change the code. Great product and service, as always.

Ok, so it sounds like I may have to change my design.
The system cannot recognize the supertype in predicates and alias the where accordingly? It seems that if it is able to alias in the from, it should know to alias in the where. I'm sure it is far more complicated than that when generating these statements but I'm hoping...? simple_smile

tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 30-Oct-2008 22:44:16   

Sorry to reopen this, but I'm still having upgrade issues with the aliases...this time with a dynamic list.

This v2.0 compatible code produces an invalid query on v2.6:

c#



            ResultsetFields fields = new ResultsetFields(2);
            fields.DefineField(ContactFieldIndex.ContactID, 0,"ContactID","Contact");
            IRelationCollection relations = new RelationCollection();
            relations.Add(ContactEntity.Relations.ContributionEntityUsingContactID);
            relations.Add(ContributionEntity.Relations.ClientCommitteeEntityUsingCommitteeID);

            IGroupByCollection groupByClause = new GroupByCollection();
            groupByClause.Add(fields[0]);

fields.DefineField(ContributionFieldIndex.Amount,1,"Total","Contribution",SD.LLBLGen.Pro.ORMSupportClasses.AggregateFunction.Sum);

                DataTable electionTotals = new DataTable();
                IPredicateExpression filter = new PredicateExpression();
                filter.Add(ContributionFields.ElectionName == "Primary");
                IPredicateExpression comFilter = null;
                DataHelper.ApplyCommitteeFilter(ref comFilter,ContributionFields.CommitteeID);
                filter.AddWithAnd(comFilter);

                CamelotAccess.DaoClasses.TypedListDAO dao = new CamelotAccess.DaoClasses.TypedListDAO();
                dao.GetMultiAsDataTable(fields, electionTotals, 0, null, filter, relations, true, groupByClause, null, 0,0);


and the query


SELECT [Contact].[ContactID], SUM([Contribution].[Amount]) AS [Total] FROM (( [dbo].[Contact] [LPA_L1]  INNER JOIN [dbo].[Contribution]  ON  [LPA_L1].[ContactID]=[dbo].[Contribution].[ContactID]) INNER JOIN [dbo].[ClientCommittee]  ON  [dbo].[ClientCommittee].[ClientCommitteeID]=[dbo].[Contribution].[CommitteeID]) WHERE ( ( [dbo].[Contribution].[ElectionName] = 'Primary' AND ( [dbo].[Contribution].[CommitteeID] = 10086))) GROUP BY [Contact].[ContactID]

tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 31-Oct-2008 03:18:45   

I altered my dynamic list code to include Aliases on everything. The group by is now fixed, but the where clauses are still screwed up.

c# (just lines 1-8 above, remaining the same)


            ResultsetFields fields = new ResultsetFields(2);
            fields.DefineField(ContactFieldIndex.ContactID, 0,"ContactID","Contact");
            IRelationCollection relations = new RelationCollection();
            relations.Add(ContactEntity.Relations.ContributionEntityUsingContactID, "Contact", "Contribution", JoinHint.None);
            relations.Add(ContributionEntity.Relations.ClientCommitteeEntityUsingCommitteeID, "Contribution", "ClientCommittee", JoinHint.None);

            IGroupByCollection groupByClause = new GroupByCollection();
            groupByClause.Add(fields[0]);
            groupByClause[0].Alias = "Contact";

tsql


    Query: SELECT [LPA_C1].[ContactID] AS [Contact], SUM([LPA_C2].[Amount]) AS [TotalToOfficeholder 2010] FROM (( [dbo].[Contact] [LPA_C1]  INNER JOIN [dbo].[Contribution] [LPA_C2]  ON  [LPA_C1].[ContactID]=[LPA_C2].[ContactID]) INNER JOIN [dbo].[ClientCommittee] [LPA_C3]  ON  [LPA_C3].[ClientCommitteeID]=[LPA_C2].[CommitteeID]) WHERE ( ( [dbo].[Contribution].[ElectionName] = @ElectionName1 AND ( [dbo].[Contribution].[CommitteeID] = @CommitteeID2))) GROUP BY [LPA_C1].[ContactID]
    Parameter: @ElectionName1 : AnsiString. Length: 255. Precision: 0. Scale: 0. Direction: Input. Value: "Cedillo OH - 06/07/08 - 03/05/10 - New".
    Parameter: @CommitteeID2 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 10086.

Help?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 31-Oct-2008 09:32:31   

You code misses the filter code, could you post that too please?

Frans Bouma | Lead developer LLBLGen Pro
tomahawk
User
Posts: 169
Joined: 02-Mar-2005
# Posted on: 31-Oct-2008 20:25:27   

Sorry, here is that function:


        public static bool ApplyCommitteeFilter(ref IPredicateExpression filter, IEntityField field)
        {
            bool isEmpty = false;
            if(filter == null)
                filter = new PredicateExpression();
            if (Settings.Default.CurrentCommittees != null && Settings.Default.CurrentCommittees.Count > 0)
            {
                foreach (object o in Settings.Default.CurrentCommittees)
                {
                    int ID = (int)o;
                    filter.AddWithOr(new FieldCompareValuePredicate(field,ComparisonOperator.Equal,ID));
                }
            }
            else
                isEmpty = true;

            return isEmpty;
        }

This function is referenced in my above post(s).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-Nov-2008 13:51:59   

You have to specify an ObjectAlias in the predicate as well. (it's a parameter on an overload of the constructor of the fieldcomparevaluepredicate. Give it the same alias as the entity you want to filter in the resultset.

Frans Bouma | Lead developer LLBLGen Pro