Adding custom Fields to generated entities

Posts   
 
    
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 02-Jul-2008 08:08:53   

I want to add some additional custom fields to some entities.

Am I correct in thinking that I need to do this in two places: 1) In the entity itself in OnInitClassMemberComplete() for the case when a ctor is called. 2) In the entity factory in CreateFields() so that if the entity is created via a prefeth path.

I'm still playing with some test code but it seems to me that although OnInitClassMemberComplete() will be called in both scenarios, for the prefetch way, it is too late for the generated query since it will only use what is returned via the factory's CreateFields()

Cheers Simon

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 02-Jul-2008 10:18:24   

Do you want to extend the entityFields or maybe all you need is some custom properties?

If you want to extend the entityFields, would you please explain why?

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 02-Jul-2008 10:38:38   

Walaa wrote:

Do you want to extend the entityFields or maybe all you need is some custom properties?

If you want to extend the entityFields, would you please explain why?

There are some field I want to have on the Entity that can't easily be setup in the Designer. I am testing doing this with both custom entities and typedlists - both have their pros and cons.

Here is some sample code for one of the custom entities and its factory...



    public partial class ShipDFNEntity
    {
        public const int FollowUpMessagesFieldIndex = (int) ShipDFNFieldIndex.AmountOfFields;
        public const int HasPriorityMessagesFieldIndex = FollowUpMessagesFieldIndex + 1;

        public virtual int FollowUpMessagesCount
        {
            get { return (int) GetValue(FollowUpMessagesFieldIndex, true); }
            set { SetValue(FollowUpMessagesFieldIndex, value); }
        }

        public virtual bool HasPriorityMessages
        {
            get { return (int) GetValue(HasPriorityMessagesFieldIndex, true) > 0; }
            set { SetValue(HasPriorityMessagesFieldIndex, value); }
        }

        protected override void OnInitClassMembersComplete()
        {
            ShipDFNEntityFactory.ExtendFields(Fields);
        }

        protected override string GetFormatString(char pattern)
        {
            switch(pattern)
            {
                case 'n':
                    //TODO: Remove this hack!!!
                    return StringHelper.ToTitleCase(Name);

                case 'g':
                case 'G':
                    return Name;

                default:
                    return base.GetFormatString(pattern);
            }
        }
    }


using System;

using BPOSS.Entities.EntityClasses;
using BPOSS.Entities.HelperClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;

namespace BPOSS.Entities.FactoryClasses
{
    public partial class ShipDFNEntityFactory
    {
        static ShipDFNEntityFactory()
        {
            docRelations.Add(DocumentTransactionEntity.Relations.DocumentEntityUsingDocID, JoinHint.Left);
        }

        static readonly RelationCollection docRelations = new RelationCollection();

        static readonly Predicate documentFilter = ShipDFNFields.ID == DocumentTransactionFields.ShipID &
                                                   DocumentTransactionFields.VoyageID == DBNull.Value &
                                                   DocumentTransactionFields.AllocatedFlag == true;

        static readonly ScalarQueryExpression followUpMessagesQuery =
            new ScalarQueryExpression(
                DocumentFields.ID.SetAggregateFunction(AggregateFunction.Count),
                documentFilter & DocumentFields.FollowUpFlag == true, docRelations);

        static readonly ScalarQueryExpression hasPriorityMessagesQuery =
            new ScalarQueryExpression(
                DocumentFields.ID.SetAggregateFunction(AggregateFunction.Count),
                documentFilter & DocumentFields.PriorityFlag == true, docRelations);

        public override IEntityFields2 CreateFields()
        {
            return ExtendFields(base.CreateFields());
        }

        internal static IEntityFields2 ExtendFields(IEntityFields2 fields)
        {
            if (fields.Count == (int)ShipDFNFieldIndex.AmountOfFields)
            {
                fields.Expand(2);

                fields.DefineField(new EntityField2("FollowUpMessagesCount", followUpMessagesQuery), ShipDFNEntity.FollowUpMessagesFieldIndex);
                fields.DefineField(new EntityField2("HasPriorityMessages", hasPriorityMessagesQuery), ShipDFNEntity.HasPriorityMessagesFieldIndex);
            }

            return fields;
        }
    }
}

I am using the same field expanding code (in ExtendFields(IEntitiyFields)) and calling it from two places: when the entity is created and when CreateFields on the factory is called.

This code seems to work though but I just wanted to confirm this is the necessary way to do it as I would have thought there would be a single place where the Fields for an entity are created and only one override was required.

Cheers Simon

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 02-Jul-2008 12:07:25   

I think you only need the CreateFields() override.

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 02-Jul-2008 12:20:00   

Walaa wrote:

I think you only need the CreateFields() override.

Thats what I thought but I think it goes into the generated FieldInfroProviderCore and ignores the factory. I'll have another go just to be sure.

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 02-Jul-2008 12:21:43   

If you want to have writable custom fields, you also have to provide mapping info, which is a tad more difficult. If you want to have read-only fields only, please take a look at the asp.net article I wrote some time ago which explains what to do: http://weblogs.asp.net/fbouma/archive/2006/06/09/LLBLGen-Pro-v2.0-with-ASP.NET-2.0.aspx

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 02-Jul-2008 13:29:00   

Otis wrote:

If you want to have writable custom fields, you also have to provide mapping info, which is a tad more difficult. If you want to have read-only fields only, please take a look at the asp.net article I wrote some time ago which explains what to do: http://weblogs.asp.net/fbouma/archive/2006/06/09/LLBLGen-Pro-v2.0-with-ASP.NET-2.0.aspx

The code is based on that article. smile

The difference is that the additional fields are always be created so I was looking at a partial class on the original factory to override CreateFields rather than having an additional factory.

What I am trying to work out in my mind is how Fields get created for entities so I can ensure the code works for all circumstances. So 1) via its ctor. 2) when it is created because it is in a prefetch path. 3) <any other circumstances>

If I comment out the OnInitClassMembersComplete() overload in the code above then my unit test fails so it seems that the ctor way of creating an entity doesn't use the factory and therefore there is no single place to override - have to put the code in a method accessible to both the entity and the factory.

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 03-Jul-2008 09:49:58   

It always uses the factory to create the fields. However there are some details which might become handy: - when you instantiate a new empty entity instance, it uses the CreateFields() method internally, which simply calls the fields factory and passes the entity enum to it. - when you fetch a collection with instances of these same entities, the factory set in the collection is used. Here, the factory is asked to produce fields, which are then filled and the filled fields object is passed to the entity factory's create method, which passes it to the entity's ctor which accepts a fields object.

A prefetch path uses normal entity collection fetch methods, so that's not different from fetching a collection.

Frans Bouma | Lead developer LLBLGen Pro