Author |
Share Topic Topic Search Topic Options
|
Sinnema
Groupie
Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
|
Post Options
Quote Reply
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
|
|
Sinnema
Groupie
Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
|
Post Options
Quote Reply
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 ;-)
|
|
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
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.
|
|
Sinnema
Groupie
Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
|
Post Options
Quote Reply
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
|
|
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
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.
|
|
skingaby
DevForce MVP
Joined: 23-Apr-2008
Location: United States
Posts: 146
|
Post Options
Quote Reply
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.
|
|
Sinnema
Groupie
Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
|
Post Options
Quote Reply
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
|
|
Sinnema
Groupie
Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
|
Post Options
Quote Reply
Posted: 14-Oct-2009 at 5:00am |
Bump
|
|
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
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.
|
|
pk55
Senior Member
Joined: 22-Jul-2009
Location: CA
Posts: 105
|
Post Options
Quote Reply
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?
|
|
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
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).
|
|
pk55
Senior Member
Joined: 22-Jul-2009
Location: CA
Posts: 105
|
Post Options
Quote Reply
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.
|
|
patsissons
Newbie
Joined: 22-Feb-2011
Location: Canada
Posts: 1
|
Post Options
Quote Reply
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
|
|