New Custom Column in Typed List

Posts   
 
    
Bashar
User
Posts: 108
Joined: 11-Nov-2004
# Posted on: 13-Nov-2006 08:57:16   

Hi People,

I know you've probably answered this a thousand times, I apologize for this.

I'm trying to load around 100,000 records into a combo (I know this in nuts, but bear with me here), so I created a typed list and bound it to the combo.

Now, I want to create a new field that holds the following information: Field1 & " " & Field2, and add it to the typed list. I will use this field as the 'DisplayMember' of the combo.

How do I do that? I've tried reading other threads, but I don't want to confuse myself any further.

Thanks, Bashar

jbb avatar
jbb
User
Posts: 267
Joined: 29-Nov-2005
# Posted on: 13-Nov-2006 15:30:43   

Hello,

you can modify the typedlist you generated. In the code generated, go to InitClass Method and you will have a comment like this : ' __LLBLGENPRO_USER_CODE_REGION_START InitClass ' __LLBLGENPRO_USER_CODE_REGION_END

Between the two lines, add your field as it : Dim myField As New DataColumn("MyField", GetType(String), "Field1 + Field2") myField.ReadOnly=True Me.Columns.Add(myField)

And after you can use your typedlist and bind "MyField" as DisplayMember.

Bashar
User
Posts: 108
Joined: 11-Nov-2004
# Posted on: 14-Nov-2006 09:26:15   

Wow, that worked simple_smile

Thank you very much.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 14-Nov-2006 09:39:10   

In .net 2.0, you can also use partial classes as shown in the example below which is part of this forum: (selfservicing example, but in adapter, it's similar)


using System;
using System.Collections.Generic;
using System.Text;
using SD.LLBLGen.Pro.ORMSupportClasses;
using SD.HnD.DAL.HelperClasses;
using System.Data;

namespace SD.HnD.DAL.TypedListClasses
{
    /// <summary>
    /// Extension class which extends the typedlist MessagesInThreadTypedList and adds a scalar query expression column to the typedlist. 
    /// </summary>
    public partial class MessagesInThreadTypedList
    {
        #region Class Member Declarations
        private DataColumn _columnAmountOfAttachments;
        #endregion

        /// <summary>
        /// Called when the typedlist's resultset has been build. This is the spot to add additional columns to the typedlist in code. 
        /// We do this in code because we can't add a scalar query expression in the typedlist designer. 
        /// </summary>
        /// <param name="fields">The typedlist resultset fields.</param>
        protected override void OnResultsetBuilt(IEntityFields fields)
        {
            // expand the fields with 1 slot, so we can add our scalar query expression to that slot
            int index = fields.Count;
            fields.Expand(1);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression 
            // performs a SELECT COUNT(AttachmentID) FROM Attachments WHERE MessageID = Message.MessageID
            // query. 
            fields.DefineField(new EntityField("AmountOfAttachments",
                                new ScalarQueryExpression(AttachmentFields.AttachmentID.SetAggregateFunction(AggregateFunction.Count),
                                    (AttachmentFields.MessageID == MessageFields.MessageID))), index);

            // done
            base.OnResultsetBuilt(fields);
        }


        /// <summary>
        /// Called when InitClass of the typedlist ended. 
        /// </summary>
        protected override void OnInitialized()
        {
            _columnAmountOfAttachments = new DataColumn("AmountOfAttachments", typeof(int), null, MappingType.Element);
            _columnAmountOfAttachments.ReadOnly = true;
            this.Columns.Add(_columnAmountOfAttachments);
            base.OnInitialized();
        }


        #region Class Property Declarations
        /// <summary>
        /// Gets the amount of attachments column.
        /// </summary>
        /// <value>The amount of attachments column.</value>
        internal DataColumn AmountOfAttachmentsColumn
        {
            get { return _columnAmountOfAttachments; }
        }
        #endregion
    }



    /// <summary>
    /// Extension class for the row class of the MessagesInThread typedlist. 
    /// </summary>
    public partial class MessagesInThreadRow
    {
        /// <summary>Gets / sets the value of the TypedList field AmountOfAttachments<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of attachments for each message</remarks>
        public int AmountOfAttachments
        {
            get
            {
                if(IsAmountOfAttachmentsNull())
                {
                    return 0;
                }
                else
                {
                    return (int)this[_parent.AmountOfAttachmentsColumn];
                }
            }
        }

        /// <summary>Returns true if the TypedList field MessageID is NULL, false otherwise.</summary>
        public bool IsAmountOfAttachmentsNull()
        {
            return IsNull(_parent.MessageIDColumn);
        }
    }
}

Frans Bouma | Lead developer LLBLGen Pro
Bashar
User
Posts: 108
Joined: 11-Nov-2004
# Posted on: 14-Nov-2006 10:38:57   

Again, I'm speechless! smile

Thank you very much.

One last question, a scalar expression Sum(Field) for example would return DBNull if there are no records returned.

Examine this code;

        'Get all Cash payments
        Dim cashPayments As InvAppPaymentCashCollection = New InvestmentApplicationPaymentCashCollection

        Dim totalCash As Decimal = CDec(cashPayments.GetScalar(DAL.InvAppPaymentCashFieldIndex.DecAmount, Nothing, AggregateFunction.Sum, (InvAppPaymentCashFields.StrApplicationId = ApplicationId)))

How would I remedy that?

Thank you very much in advance.

jbb avatar
jbb
User
Posts: 267
Joined: 29-Nov-2005
# Posted on: 14-Nov-2006 16:04:14   

Hello,

you could get first the result of your GetScalar method in an object and after test if is dbnull or not.


'Get all Cash payments
        Dim cashPayments As InvAppPaymentCashCollection = New InvestmentApplicationPaymentCashCollection
dim totalCash as decimal
dim myResult as object
myResult=cashPayments.GetScalar(DAL.InvAppPaymentCashFieldIndex.DecAmount, Nothing, AggregateFunction.Sum, (InvAppPaymentCashFields.StrApplicationId = ApplicationId))
if not myResult is DBNull.Value then 
totalCash=cdec(myResult)
else
totalCash=0
end if

bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 23-Aug-2007 04:20:54   

Fran,

I did like you did and here are my code is below.

DB relationship: batch.id 1:n claim-bill.batch-id claimbill.id 1:n claim-bill-error.claim-bill.id

Desire: I'd like to get all the claim-bill-error that belongs to each batch.

Problem: In the second field (i.e.

fields.DefineField(new EntityField2("NumberOfBillsWithError", new ScalarQueryExpression(ClaimBillErrorFields.ClaimBillId.SetAggregateFunction(AggregateFunction.CountDistinct), BatchFields.Id == ClaimBillFields.BatchId_ & ClaimBillFields.Id ==ClaimBillErrorFields.ClaimBillId, relations)),index++);

) I got the count of number of claim-bill-ID for ALL claim-bill-errors where the claim-bill-errors.claim-bill-id matches with the id of the claim bill.
What I would like to get is the count of number of claim-bill-ID for the claim-bill-errors where the claim-bill-errors.claim-bill-id matches with the id of the claim bill AND the claim-bill.batchid matches the batch ID.
In other words, the predicate BatchFields.Id == ClaimBillFields.BatchId_ seemed to be ignored.

BTW, this is for adapter instead of self-servicing. Is it OK?



    public partial class BatchSummaryTypedList
    {
        #region Class Member Declarations
        private DataColumn _columnNumberOfBills;
        private DataColumn _columnNumberOfBillsWithError;
        #endregion

        #region Class Property Declarations
        /// <summary>
        /// Gets the amount of bills column.
        /// </summary>
        /// <value>The amount of bills column.</value>
        internal DataColumn NumberOfBillsColumn
        {
            get { return _columnNumberOfBills; }
        }
        /// <summary>
        /// Gets the amount of bills-with-error column.
        /// </summary>
        /// <value>The amount of bills-with-error column.</value>
        internal DataColumn NumberOfBillsWithErrorColumn
        {
            get { return _columnNumberOfBillsWithError; }
        }
        #endregion

        /// <summary>
        /// Called when InitClass of the typedlist ended.
        /// </summary>
        protected override void OnInitialized()
        {
            _columnNumberOfBills = new DataColumn("NumberOfBills", typeof(int), null, MappingType.Element);
            _columnNumberOfBills.ReadOnly = true;
            this.Columns.Add(_columnNumberOfBills);

            _columnNumberOfBillsWithError = new DataColumn("NumberOfBillsWithError", typeof(int), null, MappingType.Element);
            _columnNumberOfBillsWithError.ReadOnly = true;
            this.Columns.Add(_columnNumberOfBillsWithError);
            base.OnInitialized();
        }

        /// <summary>
        /// Called when the typedlist's resultset has been build. This is the spot to add additional columns to the typedlist in code.
        /// We do this in code because we can't add a scalar query expression in the typedlist designer.
        /// </summary>
        /// <param name="fields">The typedlist resultset fields.</param>
        protected override void OnResultsetBuilt(IEntityFields2 fields)
        {
            // expand the fields with 2 slots, so we can add our scalar query expression to those slots
            int index = fields.Count;
            fields.Expand(2);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression
            // performs a SELECT COUNT(ClaimBill) FROM ClaimBill WHERE BatchId = Batch.ID_ query.
            // (note that we use ClaimBillFields.BatchId_ cuz this corresponds to Batch_ID in the table instead of the legacy BatchID field).
            fields.DefineField(new EntityField2("NumberOfBills",
                                                new ScalarQueryExpression(ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.Count),
                                                                          (BatchFields.Id == ClaimBillFields.BatchId_))),
                               index++);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression
            // performs a 
            // [ 
            //   SELECT COUNT(distinct Claim_Bill_Error.Claim_Bill_Id ) FROM Claim_Bill_Error 
            //   INNER JOIN Claim_Bill ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id
            //   INNER JOIN Batch on Batch.Id = Claim_Bill.Batch_Id
            //   WHERE Batch.ID = batchid
            // ]
            // query.

            IRelationCollection relations = new RelationCollection();
            relations.Add(BatchEntity.Relations.ClaimBillEntityUsingBatchId_);
            relations.Add(ClaimBillEntity.Relations.ClaimBillErrorEntityUsingClaimBillId);
            fields.DefineField(new EntityField2("NumberOfBillsWithError",
                                                new ScalarQueryExpression (ClaimBillErrorFields.ClaimBillId.SetAggregateFunction(AggregateFunction.CountDistinct),
                                                                           BatchFields.Id == ClaimBillFields.BatchId_ & ClaimBillFields.Id == ClaimBillErrorFields.ClaimBillId,
                                                                           relations
                                                                           )
                                                ),
                               index++);
            // done
            base.OnResultsetBuilt(fields);
        }
    }

    /// <summary>
    /// Extension class for the row class of the MessagesInThread typedlist.
    /// </summary>
    public partial class BatchSummaryRow
    {
        /// <summary>Gets / sets the value of the TypedList field NumberOfBills<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of bils for each batch</remarks>
        public int NumberOfBills
        {
            get
            {
                return (int)this[_parent.NumberOfBillsColumn];
            }
        }
        /// <summary>Gets / sets the value of the TypedList field NumberOfBills<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of bills-with-error for each batch</remarks>
        public int NumberOfBillsWithError
        {
            get
            {
                return (int)this[_parent.NumberOfBillsWithErrorColumn];
            }
        }
    }

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 23-Aug-2007 12:02:48   

I'd like to get all the claim-bill-error that belongs to each batch

I understand the above line.

I got the count of number of claim-bill-ID for ALL claim-bill-errors where the claim-bill-errors.claim-bill-id matches with the id of the claim bill. What I would like to get is the count of number of claim-bill-ID for the claim-bill-errors where the claim-bill-errors.claim-bill-id matches with the id of the claim bill AND the claim-bill.batchid matches the batch ID.

I don't understand the above paragraph.

In other words, the predicate BatchFields.Id == ClaimBillFields.BatchId_ seemed to be ignored.

I guess your TypedList doesn't contain a field from the BatchEntity, does it? From the first quote, I think you should displaying some fields from the BatchEntity with an additional field that represents the number of the related ClaimBillErrors...right?

bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 24-Aug-2007 01:19:09   

Let me try to explain this again:

Here's my database relationship. Basically a batch has many bills and each bill can have many errors.

DB relationship: batch.id - claim-bill.batch-id (1:n) claimbill.id - claim-bill-error.claim-bill-id (1:n)

In the batch there's no field for claim-bill nor claim-bill-error at all. It is the claim-bill that has a field for batch-id and similarly the claim-bill-error table has a field for claim-bill-id.

Now, what I want to do is to display, in a gridview, a list of batches. The gridview must shows the number of bills that each batch contains. The gridview must also shows the number of bills-that-has-error that each batch contains. To achieve this, I created a batch typedlist and I named it BatchSummaryTypedList. Since I cannot create a scalar field in the typedlist in the llblgenpro designer, per Fran's posting I used partial class and override the method OnResultsetBuilt( ).

Result: The gridview lists the batches with correct number of claim-bills for each batch. But it does not display the correct number of claim-bills-that-has-error for each batch. As a matter of fact, it list ALL the claim-bill-that-has-error for each batch.

For example, lets say I have 3 batches (batch ID=1, 2, &3) and each batch has 1 claim bill (claimbill ID=1, 2, &3). Lets say claim bill 2 & 3 has 1 error each (thus there are 2 claim-bill-error, ID=1&2).
The resulting gridview shows each batch has 1 claim-bill and has 2 claim-bill-that-has-error.

I hope this is more clear.

Thanks.

PS: yes the batch typedlist has fields from the batch (such as batch-id, batch-number, batch-created-date). I also want to add scalar fields that represent number-of-bills-belong-to-the-batch and number-of-bills-that-has-error-belong-to-the-batch.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 24-Aug-2007 09:18:26   

The gridview must also shows the number of bills-that-has-error that each batch contains.

I think the sub-query should be:

 SELECT COUNT(distinct Claim_Bill.Id ) FROM Claim_Bill 
 INNER JOIN Claim_Bill_Error ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id
 WHERE Claim_Bill.batchid = Batch.ID

And the corresponding code should be:

            IRelationCollection relations = new RelationCollection();
            relations.Add(ClaimBillEntity.Relations.ClaimBillErrorEntityUsingClaimBillId);

            fields.DefineField(new EntityField2("NumberOfBillsWithError",
                                                new ScalarQueryExpression (ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.CountDistinct),
                                                                         BatchFields.Id == ClaimBillFields.BatchId,
                                                                         relations
                                                                         )
                                                ),
                             index++);
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 24-Aug-2007 20:36:59   

Walaa,

Thanks. It works like a charm. I'd like to elaborate on this a little bit to better understand. Please feel free to give your comment. Thanks.

In SQL, either query returns the same result.

  1. SELECT COUNT(distinct Claim_Bill.Id ) FROM Claim_Bill INNER JOIN Claim_Bill_Error ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id WHERE Claim_Bill.batch_id = '1799'

  2. SELECT COUNT(distinct Claim_Bill_Error.Claim_Bill_Id ) FROM Claim_Bill_Error INNER JOIN Claim_Bill ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id INNER JOIN Batch on Batch.Id = Claim_Bill.Batch_Id WHERE Batch.ID = '1798'

However, the latter sql statement is more complex and cannot be expressed in llblgenpro where I want to retrieve the number of claim-bill-with-error that belongs to each batch.

In 1) the count is executed against the claim-bill table which is directly related to the batch table. Thus scalarqueryexpression is (which works):

ScalarQueryExpression(ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.CountDistinct), BatchFields.Id == ClaimBillFields.BatchId_, relations )

In 2) the count is executed against the claim-bill-error table which is not directly related tot eh batch table. Thus scalarqueryexpression is (which doesn't work):

ScalarQueryExpression (ClaimBillErrorFields.ClaimBillId.SetAggregateFunction(AggregateFunction.CountDistinct), ClaimBillErrorFields.ClaimBillId == ClaimBillFields.Id & BatchFields.Id == ClaimBillFields.BatchId_, relations )

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 26-Aug-2007 21:09:15   

Could you post the generated SQL of both (1) and (2)? (LLBLGenPro Help - Using generated code - Troubleshooting and Debugging).

David Elizondo | LLBLGen Support Team
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 27-Aug-2007 20:24:22   

Daelmo,

How do I get/retrieve the generated sql?

Thanks

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 28-Aug-2007 11:20:50   

bunzee wrote:

Daelmo,

How do I get/retrieve the generated sql?

Thanks

Manual -> Troubleshooting and debugging -> Tracing simple_smile

Frans Bouma | Lead developer LLBLGen Pro
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 10-Sep-2007 23:47:42   

Otis,

llblgenpro v2.5 sql2000 asp.net 2.0

using System;
using System.Collections.Generic;
using System.Text;
using SD.LLBLGen.Pro.ORMSupportClasses;
using MW.DAL.HelperClasses;
using MW.DAL.EntityClasses;
using System.Data;

namespace MW.DAL.TypedListClasses
{

    /// <summary>
    /// Extension class which extends the typedlist BatchSummaryTypedList and adds a scalar query expression column to the typedlist.
    /// This code is derived from: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=8110
    /// </summary>
    public partial class BatchSummaryTypedList
    {
        #region Class Member Declarations
        private DataColumn _columnNumberOfBills;
        private DataColumn _columnNumberOfBillsWithError;
        private DataColumn _columnNumberOfBillsWithLineError;
        #endregion

        #region Class Property Declarations
        /// <summary>
        /// Gets the amount of bills column.
        /// </summary>
        /// <value>The amount of bills column.</value>
        internal DataColumn NumberOfBillsColumn
        {
            get { return _columnNumberOfBills; }
        }
        /// <summary>
        /// Gets the amount of bills-with-error column.
        /// </summary>
        /// <value>The amount of bills-with-error column.</value>
        internal DataColumn NumberOfBillsWithErrorColumn
        {
            get { return _columnNumberOfBillsWithError; }
        }
        /// <summary>
        /// Gets the amount of bills-with-line_error column.
        /// </summary>
        /// <value>The amount of bills-with-line_error column.</value>
        internal DataColumn NumberOfBillsWithLineErrorColumn
        {
            get { return _columnNumberOfBillsWithLineError; }
        }
        #endregion

        /// <summary>
        /// Called when InitClass of the typedlist ended.
        /// </summary>
        protected override void OnInitialized()
        {
            _columnNumberOfBills = new DataColumn("NumberOfBills", typeof(int), null, MappingType.Element);
            _columnNumberOfBills.ReadOnly = true;
            this.Columns.Add(_columnNumberOfBills);

            _columnNumberOfBillsWithError = new DataColumn("NumberOfBillsWithError", typeof(int), null, MappingType.Element);
            _columnNumberOfBillsWithError.ReadOnly = true;
            this.Columns.Add(_columnNumberOfBillsWithError);

            _columnNumberOfBillsWithLineError = new DataColumn("NumberOfBillsWithLineError", typeof(int), null, MappingType.Element);
            _columnNumberOfBillsWithLineError.ReadOnly = true;
            this.Columns.Add(_columnNumberOfBillsWithLineError);
            base.OnInitialized();
        }

        /// <summary>
        /// Called when the typedlist's resultset has been build. This is the spot to add additional columns to the typedlist in code.
        /// We do this in code because we can't add a scalar query expression in the typedlist designer.
        /// </summary>
        /// <param name="fields">The typedlist resultset fields.</param>
        protected override void OnResultsetBuilt(IEntityFields2 fields)
        {
            // expand the fields with 2 slots, so we can add our scalar query expression to those slots
            int index = fields.Count;
            fields.Expand(3);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression
            // performs a SELECT COUNT(ClaimBill) FROM ClaimBill WHERE BatchId = Batch.ID_ query.
            // (note that we use ClaimBillFields.BatchId_ cuz this corresponds to Batch_ID in the table instead of the legacy BatchID field).
            fields.DefineField(new EntityField2("NumberOfBills",
                                                new ScalarQueryExpression(ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.Count),
                                                                          (BatchFields.Id == ClaimBillFields.BatchId_))),
                               index++);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression
            // performs the query:
            #region deleted
            // [ 
            //   SELECT COUNT(distinct Claim_Bill_Error.Claim_Bill_Id ) FROM Claim_Bill_Error 
            //   INNER JOIN Claim_Bill ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id
            //   INNER JOIN Batch on Batch.Id = Claim_Bill.Batch_Id
            //   WHERE Batch.ID = batchid
            // ]
            // query.
            //IRelationCollection relations = new RelationCollection();
            //relations.Add(ClaimBillErrorEntity.Relations.ClaimBillEntityUsingClaimBillId);
            //relations.Add(ClaimBillEntity.Relations.BatchEntityUsingBatchId_);
            //fields.DefineField(new EntityField2("NumberOfBillsWithError",
            //                                  new ScalarQueryExpression (ClaimBillErrorFields.ClaimBillId.SetAggregateFunction(AggregateFunction.CountDistinct),
            //                                                             ClaimBillErrorFields.ClaimBillId == ClaimBillFields.Id & BatchFields.Id == ClaimBillFields.BatchId_,
            //                                                             relations
            //                                                             )
            //                                  ),
            //                 index++);
            #endregion
            // [ 
            //   SELECT COUNT(distinct Claim_Bill.Id ) FROM Claim_Bill 
            //   INNER JOIN Claim_Bill_Error ON Claim_Bill_Error.Claim_Bill_Id = Claim_Bill.Id
            //   WHERE Batch.ID = batchid
            // ]
            IRelationCollection relations = new RelationCollection();
            relations.Add(ClaimBillEntity.Relations.ClaimBillErrorEntityUsingClaimBillId);

            fields.DefineField(new EntityField2("NumberOfBillsWithError",
                                                new ScalarQueryExpression(ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.CountDistinct),
                                                                         BatchFields.Id == ClaimBillFields.BatchId_,
                                                                         relations
                                                                         )
                                                ),
                                index++);
            // add a scalar query expression to the list of fields in the typedlist. The scalar query expression
            // performs the query:
            // [ 
            //   SELECT COUNT(distinct Claim_Bill.Id ) FROM Claim_Bill 
            //   INNER JOIN Claim_Bill_Line ON Claim_Bill_Line.Claim_Bill_Id = Claim_Bill.Id
            //   INNER JOIN Claim_Bill_Line_Error ON Claim_Bill_Line.Id = Claim_Bill_Line_Error.Claim_Bill_Line_Id
            //   WHERE Batch.ID = batchid
            // ]
            IRelationCollection relations2 = new RelationCollection();
            relations2.Add(ClaimBillEntity.Relations.ClaimBillLineEntityUsingClaimId);
            relations2.Add(ClaimBillLineEntity.Relations.ClaimBillLineErrorEntityUsingClaimBillLineId);

            fields.DefineField(new EntityField2("NumberOfBillsWithLineError",
                                                new ScalarQueryExpression(ClaimBillFields.Id.SetAggregateFunction(AggregateFunction.CountDistinct),
                                                                         BatchFields.Id == ClaimBillFields.BatchId_,
                                                                         relations2
                                                                         )
                                                ),
                                index++);
            // done
            base.OnResultsetBuilt(fields);
        }
    }

    /// <summary>
    /// Extension class for the row class of the MessagesInThread typedlist.
    /// </summary>
    public partial class BatchSummaryRow
    {
        /// <summary>Gets / sets the value of the TypedList field NumberOfBills<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of bils for each batch</remarks>
        public int NumberOfBills
        {
            get
            {
                return (int)this[_parent.NumberOfBillsColumn];
            }
        }
        /// <summary>Gets / sets the value of the TypedList field NumberOfBillsWithError<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of bills-with-error for each batch</remarks>
        public int NumberOfBillsWithError
        {
            get
            {
                return (int)this[_parent.NumberOfBillsWithErrorColumn];
            }
        }
        /// <summary>Gets / sets the value of the TypedList field NumberOfBillsWithLineError<br/><br/>
        /// </summary>
        /// <remarks>Mapped on: the scalar query expression for retrieving the # of bills-with-error for each batch</remarks>
        public int NumberOfBillsWithLineError
        {
            get
            {
                return (int)this[_parent.NumberOfBillsWithLineErrorColumn];
            }
        }
    }

}

Above was the code that I created correspond to the posting that you had originally. It works fine with llblgen2.0. Recently I upgraded to llblgen 2.5 and I regenerated the code. It no longer works.

Looking at the differences between the generated BatchSummaryTypedList.cs file (BTW I put the above code in a file names BatchSummaryTypedListEx.cs), I notice the following differences (in bold):

v2.0 generated:

        private void InitClass(bool obeyWeakRelations)
        {

            base.ObeyWeakRelations = obeyWeakRelations;

            _columnId = new DataColumn("Id", typeof(System.Int32), null, MappingType.Element);
            _columnId.ReadOnly = true;
            _columnId.Caption = @"Id";
            this.Columns.Add(_columnId);
            _columnBatchNumber = new DataColumn("BatchNumber", typeof(System.String), null, MappingType.Element);
            _columnBatchNumber.ReadOnly = true;
            _columnBatchNumber.Caption = @"BatchNumber";
            this.Columns.Add(_columnBatchNumber);
            _columnBatchStatusName = new DataColumn("BatchStatusName", typeof(System.String), null, MappingType.Element);
            _columnBatchStatusName.ReadOnly = true;
            _columnBatchStatusName.Caption = @"Name";
            this.Columns.Add(_columnBatchStatusName);
            _columnReceivedOn = new DataColumn("ReceivedOn", typeof(System.DateTime), null, MappingType.Element);
            _columnReceivedOn.ReadOnly = true;
            _columnReceivedOn.Caption = @"ReceivedOn";
            this.Columns.Add(_columnReceivedOn);
            _columnClientName = new DataColumn("ClientName", typeof(System.String), null, MappingType.Element);
            _columnClientName.ReadOnly = true;
            _columnClientName.Caption = @"ClientName";
            this.Columns.Add(_columnClientName);
            _columnEmployerName = new DataColumn("EmployerName", typeof(System.String), null, MappingType.Element);
            _columnEmployerName.ReadOnly = true;
            _columnEmployerName.Caption = @"EmployerName";
            this.Columns.Add(_columnEmployerName);
            _columnAspnetUsersUserName = new DataColumn("AspnetUsersUserName", typeof(System.String), null, MappingType.Element);
            _columnAspnetUsersUserName.ReadOnly = true;
            _columnAspnetUsersUserName.Caption = @"UserName";
            this.Columns.Add(_columnAspnetUsersUserName);
            
            // __LLBLGENPRO_USER_CODE_REGION_START InitClass
            // __LLBLGENPRO_USER_CODE_REGION_END


            BuildResultset();
            _filterBucket = new RelationPredicateBucket();
            BuildRelationSet();
            OnInitialized();
        }


        /// <summary>Initializes the members, after a clone action.</summary>
        private void InitMembers()
        {
            _columnId = this.Columns["Id"];
            _columnBatchNumber = this.Columns["BatchNumber"];
            _columnBatchStatusName = this.Columns["BatchStatusName"];
            _columnReceivedOn = this.Columns["ReceivedOn"];
            _columnClientName = this.Columns["ClientName"];
            _columnEmployerName = this.Columns["EmployerName"];
            _columnAspnetUsersUserName = this.Columns["AspnetUsersUserName"];
            
            // __LLBLGENPRO_USER_CODE_REGION_START InitMembers
            // __LLBLGENPRO_USER_CODE_REGION_END


        }

V2.5 generated:

    protected **override **void InitClass(bool obeyWeakRelations)
    {
        TableName = "BatchSummary";     
        base.ObeyWeakRelations = obeyWeakRelations;

        _columnId = new DataColumn("Id", typeof(System.Int32), null, MappingType.Element);
        _columnId.ReadOnly = true;
        _columnId.Caption = @"Id";
        this.Columns.Add(_columnId);
        _columnBatchNumber = new DataColumn("BatchNumber", typeof(System.String), null, MappingType.Element);
        _columnBatchNumber.ReadOnly = true;
        _columnBatchNumber.Caption = @"BatchNumber";
        this.Columns.Add(_columnBatchNumber);
        _columnBatchStatusName = new DataColumn("BatchStatusName", typeof(System.String), null, MappingType.Element);
        _columnBatchStatusName.ReadOnly = true;
        _columnBatchStatusName.Caption = @"Name";
        this.Columns.Add(_columnBatchStatusName);
        _columnReceivedOn = new DataColumn("ReceivedOn", typeof(System.DateTime), null, MappingType.Element);
        _columnReceivedOn.ReadOnly = true;
        _columnReceivedOn.Caption = @"ReceivedOn";
        this.Columns.Add(_columnReceivedOn);
        _columnClientName = new DataColumn("ClientName", typeof(System.String), null, MappingType.Element);
        _columnClientName.ReadOnly = true;
        _columnClientName.Caption = @"ClientName";
        this.Columns.Add(_columnClientName);
        _columnEmployerName = new DataColumn("EmployerName", typeof(System.String), null, MappingType.Element);
        _columnEmployerName.ReadOnly = true;
        _columnEmployerName.Caption = @"EmployerName";
        this.Columns.Add(_columnEmployerName);
        _columnAspnetUsersUserName = new DataColumn("AspnetUsersUserName", typeof(System.String), null, MappingType.Element);
        _columnAspnetUsersUserName.ReadOnly = true;
        _columnAspnetUsersUserName.Caption = @"UserName";
        this.Columns.Add(_columnAspnetUsersUserName);

        // __LLBLGENPRO_USER_CODE_REGION_START InitClass
        // __LLBLGENPRO_USER_CODE_REGION_END


        BuildResultset();
        _filterBucket = new RelationPredicateBucket();
        BuildRelationSet();
        OnInitialized();
    }


    /// <summary>Initializes the members, after a clone action.</summary>
    private void InitMembers()
    {
        _columnId = this.Columns["Id"];
        _columnBatchNumber = this.Columns["BatchNumber"];
        _columnBatchStatusName = this.Columns["BatchStatusName"];
        _columnReceivedOn = this.Columns["ReceivedOn"];
        _columnClientName = this.Columns["ClientName"];
        _columnEmployerName = this.Columns["EmployerName"];
        _columnAspnetUsersUserName = this.Columns["AspnetUsersUserName"];

        // __LLBLGENPRO_USER_CODE_REGION_START InitMembers
        // __LLBLGENPRO_USER_CODE_REGION_END

        **OnInitialized();**
    }

Questions: 1. InitMembers in v2.5 called OnInitialized but InitMembers in v2.0 does not. Thus I got the errors listed below. Please advice me what should I do to overcome this!!!

Source Error:

Line 56: _columnNumberOfBills = new DataColumn("NumberOfBills", typeof(int), null, MappingType.Element); Line 57: _columnNumberOfBills.ReadOnly = true; Line 58: this.Columns.Add(_columnNumberOfBills); Line 59: Line 60: _columnNumberOfBillsWithError = new DataColumn("NumberOfBillsWithError", typeof(int), null, MappingType.Element);

Source File: D:\projects\from 2cs-swe\trunk\DAL\DatabaseGeneric\TypedListClasses\BatchSummaryTypedListExt.cs Line: 58

Stack Trace:

[DuplicateNameException: A column named 'NumberOfBills' already belongs to this DataTable.] System.Data.DataColumnCollection.RegisterColumnName(String name, DataColumn column, DataTable table) +84 System.Data.DataColumnCollection.BaseAdd(DataColumn column) +241 System.Data.DataColumnCollection.AddAt(Int32 index, DataColumn column) +284

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 11-Sep-2007 13:44:21   

Please make sure the TypedList doesn't already have a field defined with the following name "NumberOfBills",

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 12-Sep-2007 10:39:14   

One request: next time, please start a new thread, no matter what similarities you see between your problem and someone elses problem in another thread. Thanks.

  1. InitMembers in v2.5 called OnInitialized but InitMembers in v2.0 does not. Thus I got the errors listed below.

You got the errors in v2.5 or in v2.0? I assume in v2.5, correct? I also want to mention that you earlier on said that some scalar query can't be expressed in llblgen pro code, though it can. But let's not get side-tracked as this thread is already confusing enough.

InitMembers is called by the deserialization constructor. The OnInitialized call is required there, to fix a bug in v2.0 where deserialization of a typedlist didn't call any logic stored inside the OnInitialized method.

It's unfortunate you run into this. You indeed have to check if the column is already there in OnInitialized (i.e.: the column in the Columns collection). If not, add it, if it is, don't add it. So instead of:

_columnNumberOfBills = new DataColumn("NumberOfBills", typeof(int), null, MappingType.Element);
_columnNumberOfBills.ReadOnly = true;
this.Columns.Add(_columnNumberOfBills);

do:


_columnNumberOfBills = this.Columns["NumberOfBills"];
if(_columnNumberOfBills==null)
{
    _columnNumberOfBills new DataColumn("NumberOfBills", typeof(int), null, MappingType.Element);
    _columnNumberOfBills.ReadOnly = true;
    this.Columns.Add(_columnNumberOfBills);
}

Frans Bouma | Lead developer LLBLGen Pro
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 12-Sep-2007 22:15:23   

Otis,

Yes the querying for scalar was resolved and I was able to get the value I wanted.

Confirmed the issue regarding creating a new thread in the forum no matter how similar it is to an existing thread.

Regarding having to "[i]check if the column is already there in OnInitialized (i.e.: the column in the Columns collection)[/i]", yes I did something similar and it worked OK.

Thanks for everything.

BZ

Posts: 28
Joined: 27-Mar-2007
# Posted on: 05-Oct-2007 20:07:35   

Hi, I need something similar. I have a typelist with a potentially large number of images. This is tied to a grid view to limit the number of images displayed at one time. The customers would like to select one or more images and then view the collection. It would be useful to 'add' a selected field to the typelist which I could then use as a checkbox field. I could pick up the set of selected items via the PerformWork event which would be triggered from the 'Done' button. Note, selections are not limited to the one page. I tried the solution


    // __LLBLGENPRO_USER_CODE_REGION_START InitClass

            DataColumn myField = new DataColumn("Selected", System.Type.GetType("Boolean"));
            this.Columns.Add(myField);

            // __LLBLGENPRO_USER_CODE_REGION_END


But when binding to the datagrid it didn't appear in the available properties.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 06-Oct-2007 06:25:42   

I think something is missing at your code. Did you add that as the post 44765 of this big thread suggest? (http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=8110&StartAtMessage=0&#44765)

You need to add the a new Column property to _YourTypedList _class and a Property to _YourDataRow _class.

Are you using region codes or partial classes?

David Elizondo | LLBLGen Support Team
Posts: 28
Joined: 27-Mar-2007
# Posted on: 08-Oct-2007 10:43:03   

Hi David, The typedlist is generated code for VS2005. The typedlist code does use a partial class. I used the suggestion at the begining of the thread as this seemed the simplest. The additional field would be used simply as a marker in the datasource to retrieve selected records. The marker itself was not intended to be stored in the database. The alternative would be to accumulate the selected items in a separate list. I assumed that as per the example code added a new field was all that was needed. Not sure I understand
The aim was to have the additional 'Selected' field appear in the gridview as a checkbox. Not sure what your mean by 'Adding to data row' I haven't looked into using OnResultsetBuilt as the examples appear more focused on using existing fields. I did spot this section in the generated code // __LLBLGENPRO_USER_CODE_REGION_START AdditionalFields // be sure to call _fields.Expand(number of new fields) first. // __LLBLGENPRO_USER_CODE_REGION_END and wondered if this was the section I needed to add something. I'm using LLBLGen Pro 2.0 and VS2005 Team Edition.

Regards Michael Mason

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 08-Oct-2007 12:03:10   

This is tied to a grid view to limit the number of images displayed at one time. The customers would like to select one or more images and then view the collection. It would be useful to 'add' a selected field to the typelist which I could then use as a checkbox field.

IMHO, I think you don't need to add a new column to the typedList, just to add a Checkbox column. You can add a new column to the grid view (un-bound column) of type CheckBoxField.

Posts: 28
Joined: 27-Mar-2007
# Posted on: 11-Oct-2007 11:55:15   

Hi walaa, Thanks for the information. I'll give it a go.

Regards Michael Mason