Entity with additional fields form other tables in one select

Posts   
 
    
Andrius
User
Posts: 68
Joined: 04-Apr-2005
# Posted on: 09-Mar-2006 09:28:44   

Many times I have come up with situation that some additional columns from other tables must be selected (plain or aggregates) together with an entity.

LLBL provides this functionality by defining additional properties for an entity in designer and fetchng them with prefetch paths (which is ussully additional select). This solves a problem, but situations could be very different in a code, and there is an overhead of playing with designer and regenerating code. In situations like mine the columns I needed from other tables usually alredy have been include in Relations for select so additional select via prefetch path sounded like a bit overhead to me. Usually those fields are read only in Client GUI, so data binding was not required, but later we also added this functionality too simple_smile

I've come up with idea of extra fields and want to share it with LLBL comunity.

Basic idea is that all my entities are derived from class which has additional IEntityFields2 member. And my adapter just fills in those fields if collection of extra fields to fetch is provided as template as parameter. Pleace notice that if I want to fetch some aggregates then GroupBy must be provided, and i create default group by automatically.

Some caution however is needed there when using it, i.e understand of what SQL statement will be produced by this code


abstract class MyEntityBase: EntityBase2 
{
        [NonSerialized]
        private IEntityFields2 extraFields = null;

        public IEntityFields2 ExtraFields
        {
            get { return extraFields; }
            set { extraFields != value;}
        }
}

and code for fetching them, even with prefetch path support simple_smile


        public void FetchEntityCollection(IEntityCollection2 collectionToFill,
            IRelationPredicateBucket bucket,
            IEntityFields2 extraFields,
            int maxNumberOfItemsToReturn,
            ISortExpression sortClauses,
            IPredicateExpression havingClause, IPrefetchPath2 prefetchPath)
        {
            if (extraFields == null || extraFields.Count == 0)
            {
                this.FetchEntityCollection(collectionToFill, bucket, maxNumberOfItemsToReturn, sortClauses, prefetchPath);
                return;
            } else
            {
                this.FetchEntityCollection(collectionToFill,bucket, extraFields,maxNumberOfItemsToReturn, sortClauses, havingClause);
                if (prefetchPath != null)
                {
                    this.AdapterInternal.FetchPrefetchPath(collectionToFill, bucket, prefetchPath);
                } 
            }
        }
        
        public void FetchEntityCollection(IEntityCollection2 collectionToFill,
                                          IRelationPredicateBucket bucket,
                                          IEntityFields2 extraFields,
                                          int maxNumberOfItemsToReturn,
                                          ISortExpression sortClauses,
                                          IPredicateExpression havingClause)
        {
            if (extraFields == null || extraFields.Count == 0)
            {
                this.FetchEntityCollection(collectionToFill, bucket, maxNumberOfItemsToReturn, sortClauses, null);
                return;
            }
            IEntityFactory2 entityFactoryToUse = collectionToFill.EntityFactoryToUse;
            IEntityFields2 entityFields = entityFactoryToUse.CreateFields();

            IEntityFields2 allFields = new EntityFields2(entityFields.Count + extraFields.Count);
            {
                int i = 0;
                for (; i < entityFields.Count; i++)
                {
                    IEntityField2 field = entityFields[i];
                    allFields[i] = field;
                }
                for (int j = 0; j < extraFields.Count; j++, i++)
                {
                    IEntityField2 field = extraFields[j];
                    if (field == null)
                    {
                        throw new ApplicationException(String.Format("ExtraField at index {0} for entitity {1}  is null", i, entityFactoryToUse.Create().LLBLGenProEntityName));
                    }
                    allFields[i] = field;
                }
            }
            IGroupByCollection groupByCollection = this.CreateDefaultGroupByCollection(allFields);
            if (groupByCollection != null && havingClause != null)
            {
                groupByCollection.HavingClause = havingClause;
            }
            DataTable dataTable = new DataTable();
            this.AdapterInternal.FetchTypedList(allFields,
                                                dataTable,
                                                bucket,
                                                maxNumberOfItemsToReturn,
                                                sortClauses,
                                                true,
                                                groupByCollection);
            int rowCount = dataTable.Rows.Count;
            if (rowCount > 0)
            {
                bool isReadOnly = collectionToFill.IsReadOnly;
                collectionToFill.IsReadOnly = false;
                for (int i = 0; i < rowCount; ++i)
                {
                    DataRow dataRow = dataTable.Rows[i];

                    MyEntityBase newEntity = (MyEntityBase) EntityFactoryToUse.Create();
                    newEntity.IsNew = false;
                    int columnIndex = 0;
                    for (int j = 0; j < newEntity.Fields.Count; ++j, ++columnIndex)
                    {
                        object valueInField = dataRow[columnIndex];
                        IEntityField2 fieldDestination = newEntity.Fields[j];
                        this.SetNewFieldValueFromRow(fieldDestination, valueInField);
                        if (fieldDestination.IsPrimaryKey && valueInField == DBNull.Value)
                        {
                            newEntity.IsNew = true;
                        }
                    }
                    newEntity.Fields.AcceptChanges();

                    IEntityFields2 newExtraFields = new EntityFields2(extraFields.Count);
                    for (int j = 0; j < extraFields.Count; ++j, ++columnIndex)
                    {
                        IEntityFields extraField = (EntityField2) ((ICloneable) extraFields[j]).Clone();
                        newExtraFields[j] = extraField;
                        object valueInField = dataRow[columnIndex];
                        this.SetNewFieldValueFromRow(extraField, valueInField);
                    }
                    newExtraFields.AcceptChanges();
                    newEntity.ExtraFields = newExtraFields;
                    collectionToFill.Add(newEntity);
                }
                collectionToFill.IsReadOnly = isReadOnly;
            }
        }

        private IGroupByCollection CreateDefaultGroupByCollection(IEntityFields2 fields)
        {
            IGroupByCollection groupByClause = new GroupByCollection();
            if (fields != null)
            {
                for (int i = 0; i < fields.Count; ++i)
                {
                    IEntityField2 field = fields[i];
                    if (field == null)
                    {
                        throw new ArgumentNullException(String.Format("Field at index {0} is null in fields", i));
                    }
                    if (field.AggregateFunctionToApply == AggregateFunction.None)
                    {
                        EntityField2 esgField = (EntityField2) field;
                        groupByClause.Add(field);
                    } 
                }
            }
            return groupByClause.Count > 0 && groupByClause.Count < fields.Count ? groupByClause : null;
        }

        private void SetNewFieldValueFromRow(IEntityField2 fieldDestination, object valueInField)
        {
            bool isColumnValueDBNull = (valueInField == DBNull.Value);
            fieldDestination.IsNull = isColumnValueDBNull;
            if (isColumnValueDBNull)
            {
                // Store default value for null for type
                fieldDestination.ForcedCurrentValueWrite(
                    this.AdapterInternal.TypeDefaultValueSupplier.DefaultValue(fieldDestination.DataType), null);
            }
            else
            {
                // simply store the value
                fieldDestination.ForcedCurrentValueWrite(valueInField, valueInField);
            }
        }