New Posts New Posts RSS Feed: When to initialize an Entity after Load
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

When to initialize an Entity after Load

 Post Reply Post Reply
Author
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Topic: When to initialize an Entity after Load
    Posted: 14-Sep-2009 at 8:33am

Hi,

We would like to initialize some properties in the Entity after it has been Fetched from the DB. We've tried it in the Constructor but that seems to be too early. We've even tried the code below. This construction was looping indefenitly when we don't unregister the Fetched event.
 
We've already spent 4 hours trying to solve this ourselves but we can't seem to find the right answer. Please help us.
 
Regards,
Paul Sinnema
Diartis AG
 
  // Constructor for Person
  public Person()
     {
   InitEntity(InitEntityCommand.Load);
     }
  public override void InitEntity(InitEntityCommand initEntityCommand)
  {
   base.InitEntity(initEntityCommand);
   switch (initEntityCommand)
   {
    case InitEntityCommand.Load:
     EntityAspect.AddToManager();
     EntityAspect.EntityManager.Fetched += new EventHandler<EntityFetchedEventArgs>(EntityManagerFetched);
     break;
    case InitEntityCommand.Create:
     InitLastNameInterceptor();
     break;
    default:
     break;
   }
  }
  void EntityManagerFetched(object sender, EntityFetchedEventArgs e)
  {
   EntityAspect.EntityManager.Fetched -= new EventHandler<EntityFetchedEventArgs>(EntityManagerFetched);
   SetFullName();
   InitLastNameInterceptor();
  }
 


Edited by Sinnema - 14-Sep-2009 at 8:34am
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Sep-2009 at 1:21pm

It would be nice if someone would give us a hint today. The time-shift always poses a problem for us Europeans ;-)

Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 14-Sep-2009 at 2:08pm
The entity constructor is too early for just about anything - at construction the entity is not yet attached to an EntityManager and most (if not all) of its "entity aspect" type of features will not be present.  The constructor is also invoked both for a wholly new item, as fetched items are loaded, and during de-serialization when the item is moved between tiers.
 
At present, probably the best place for you to add initialization logic is via the EntityChanged event on the containing EntityGroup.  An action is passed with the arguments to help determine what is happening with the entity.  So something like this would be better:
 

_entityManager.GetEntityGroup(typeof(Person)).EntityChanged += (o, e) => {
   if (e.Action == EntityAction.AddOnQuery) {
      // Init for the entity instance ...
   }
};
 
We've had other requests for construction / post-construction interception points so we may do something to make this easier in a future release.
 
There are a couple of other things to watch out for too -
1) You probably don't want to call AddToManager from the constructor, since the parameterless constructor can be called in situations where you might not expect.
2) You definitely don't want to add a Fetched event handler for every single entity.  The Fetched event is defined on the EntityManager and will fire for any queries made on the EM that will be sent to the data source.  Setting a handler up for each constructed entity means your handler will be called many more times than needed.
 
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Sep-2009 at 11:22pm
Hi Kim,
 
Thanks for the reply. Aha, the EntityChanged event on the EntityGroup. Wouldn't have thought of that very soon. I guess we get events for all Entities changing in the group so this is going to be a work-around at best.
 
We realy find it hard to believe that these kind of interception points aren't available yet. We absolutely believe extending DevForce with these (post-)construction events is a must.
 
Regards,
Paul Sinnema
Diartis AG
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 15-Sep-2009 at 8:36am
EntityChanging / EntityChanged events fire a little less often, and in a more controlled manner, than the entity constructor is called.  The EntityGroup holds all entities of a given type, and these events fire when entities are added (for various reasons), removed or modified.  The Action and Entity properties of the EntityChangedEventArgs provided to the handler will tell you the specifics of why the event fired.
 
We're looking at providing a better means of (post)-construction interception.
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 17-Sep-2009 at 8:01pm
Since there is so much going on in the construction / serialization / fetching of entities. We do not use the constructor for post-init. Instead, we have followed the pattern shown in the Prism Explorer sample where a static Create method is added to the entity class. This Create method calls the constructor and then performs init work like setting the Parent entity and initializing defaults.

For example:

This is the base Create method in the BaseEntity class (upon which most of our Entities are based):

        #region Create

        protected static BaseEntity Create<T>(User user) where T : BaseEntity
        {
            InitManager();
            T item = Manager.CreateEntity<T>();
            GetATemporaryKey(item);
            item.InitializeDefaults(user);
            AddToManager(item);
            item.InitializeChildren(user);
            return item;
        }

        protected static void GetATemporaryKey<T>(T item) where T : Entity
        {
            if (Manager == null)
            {
               InitManager();
            }
            var key = item.EntityAspect.EntityMetadata.KeyProperties[0];
            if (key.IsAutoIncrementing)
            {
               try
               {
                    Manager.GenerateId(item, key);
               }
               catch (IdeaBlade.Core.IdeaBladeException)
               {
                    //eat the exception thrown if this class does not have a Generator
               }
               catch (Exception)
               {
                    throw;
               }
            }
        }

        protected static void AddToManager(Entity entity)
        {
            entity.EntityAspect.AddToManager();
        }

        protected virtual void InitializeDefaults(User user) { }

        protected virtual void InitializeChildren(User user) { }


        #endregion

The Entity then has a Create method that looks like this, with whatever parameters needed being passed in:

        #region Create

        public static Deal Create(User user)
        {
            return (Deal)Create<Deal>(user);
        }

        protected override void InitializeDefaults(User user)
        {
            this.EnteredBy = user.UserId;
            this.DateEntered = DateTime.Now;
            this.DateOfDeal = DateTime.Today;
            this.FlowDateStart = DateTime.Today.AddDays(1);
            this.FlowDateEnd = DateTime.Today.AddWeekdays(1);
        }

        protected override void InitializeChildren(User user)
        {
            AddDealPricing(user);
        }

        #endregion

The Create method in the DealPricing (a child class) looks like this:

        #region Create

        public static DealPricing Create(Deal deal, User user)
        {
            var dp = (DealPricing)Create<DealPricing>(user);
            dp.Deal = deal;
            return dp;
        }

        protected override void InitializeDefaults(User user)
        {
            this.EnteredBy = user.UserId;
        }

        #endregion

Maybe this will help? Let me know what you think.
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 17-Sep-2009 at 11:18pm
Hi  Gaby
 
Thanks for the reply. I guess you didn't read the post correctly. I didn't ask a question about the Create, we've got that topic covered correctly. I was asking a question about the Load of an existing entity in the DB.
 
Thanks for the code anyway. I've learned something new from it.
 
Regards,
Paul Sinnema
Diartis AG
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Oct-2009 at 5:00am
Bump
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 14-Oct-2009 at 8:54am
Sorry we have not yet addressed this -- as I said in an earlier post it's something (i.e., a feature) we will look into.  Right now the EntityChanging/EntityChanged events are probably the best option if you need to perform special logic for newly loaded entities.
 
I should add too that property interceptors should not be added on an entity instance basis, they should be added for an entity type.   If InitLastNameInterceptor is adding interceptor action(s) then it should instead be called once only for the Person entity type, not repeatedly for every person instance.
 
And just to be clear on these events, the Fetching and Fetched events fire on the EntityManager, so need to be initialized only once per EntityManager.  The EntityChanging and EntityChanged events fire on a specific EntityGroup, e.g. the Person EntityGroup, so these events should be initialized only once for that group.
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 18-Dec-2009 at 3:32pm
I see this still hasn't been addressed in the Dec 14th release (based on what I can tell from the Release Notes).  Are there any updates about when or if this will be addressed?
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 22-Dec-2009 at 8:51am
Our next release is not until March, and we don't currently have this feature scheduled for it.   Please let us know if this is an important feature for you (and maybe also tell us about your use case).
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 27-Dec-2009 at 5:36pm

I was able to use a combination of the EntityGroup EntityChanged and EntityPropertyChanged events to do what I need (setting computed non-persisted properties for example) so this is no longer a priority for us.

Back to Top
patsissons View Drop Down
Newbie
Newbie


Joined: 22-Feb-2011
Location: Canada
Posts: 1
Post Options Post Options   Quote patsissons Quote  Post ReplyReply Direct Link To This Post Posted: 22-Feb-2011 at 11:46am
I ran into a similar scenario where i need to do some post-load initialization.  My approach was the following:

public partial class MyEntity
{
    private bool m_Initialized = false;

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        if (m_Initialized == false)
        {
            Initialize();
            m_Initialized = true;
        }
    }

    private void Initialize()
    {
        // Do whatever in here.
    }
}

Because the entity framework will fire a property changed event (with e.propertyName == null) as soon as the entity is loaded, you can basically use this to inject your own init code into the entity life cycle.  This is certainly far from an optimal solution, but it works, it is relatively simple, and it is reasonably efficient.

I ended up using this approach to initialize collection change handlers for a collection nav prop that existed on a specific type of entity.  The collection change event needed to be handled by every entity of that type that was loaded from the db.


Edited by patsissons - 22-Feb-2011 at 11:48am
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down