Hi,

I want to track DateLastModified (DateTime) and LastModifiedBy (User) in all of my entities.

What is the most efficient way?

I currently override the OnValidate() method and update these two fields. However, when I'm creating an entity, there is an INSERT query that inserts all of the other fields of the entity, then a second query that uses UPDATE to update the DateLastModified and LastModifiedBy fields.

I'd like for all of this to be done in 1 query. One option is to override OnPropertyChanged and set the two field's values there, but I feel that maybe this will affect performance for entities with a large number of fields.

The other option is to use ITransactionEventWatcher.OnBeforeTransactionCommit(...). In this method, is it possible to get all of the entities that have been modified and are being committed? That way I could update their DateLastModified and LastModifiedBy fields. Would this lead to 1 query?

asked Nov 26 '10 at 17:04

ara's gravatar image

ara
395878791

Hello, Ara

I've investigated the issue today. The bad news: current version can't insert new instance in a single query. But there also exist good news: I've fixed Session.Persist() method a bit and now it is able to use single INSERT query per instance in certain circumnstances, otherwise it will be exactly two queries INSERT+UPDATE.

(The two stages insert required e.g. in the case when data object has refernce fields and CreateForeignKeys database option is used.)

I'll going to test the fixes tomorrow. After that I'll publish recommended implementation for DateLastModified here.

(Nov 29 '10 at 10:51) Alex Ustinov Alex%20Ustinov's gravatar image

2 Answers:

Hello

After investigations and fixes the most efficient way to implement such functionality is following:

public abstract class Person : DataObject
{
    public abstract string Name { get; set; }

    public abstract DateTime? DateLastModified { get; set; }

    protected override void OnPersist()
    {
        DateLastModified = DateTime.Now;
    }
}

Obvious that it is most straigtforward approach and now it works. While there still possible situations (two cases) when object creation completes with two queries DataObjects will try to use single INSERT whenever it is possible. Let me explain when DataObjects will perform two queries:

  1. The instance being persisted contains ReferenceFields and CreateForeignKeys database option is applied: Newly created instances may refer each other so foreign key conflicts are possible if a sequence of insertions is inproper. We didn't use topological sort in this case because two stage update is simple to implement.
  2. The instance being persisted contains an ExternalField like e.g. DataObjectCollectionField. This field is [LoadOnDemand] and LoadOnDemandThreshold is greater than zero: FastLoadData in this case must contain the ExternalField content if a size of the content is lower than threshold. So three stages are required. 1) Persisting ExternalField owner; 2) Persisting ExternalField itself; 3) Calculating and persisting FastLoadData. The first and the third stages will correspond to pair of INSERT+UPDATE queries.

That is all. As you can see our OnPersist handler itself can't lead to additional queries.

Let now consider implementation of DateLastModified with OnValidate handler you currently use. It works but it is not optimal because it brings additional UPDATE query in any case. So you can get even three stage insertion in this case. The reason of such behavior is following: Before executing validate stage DataObjects begins new internal transaction (actualy places savepoint) to be able to rollback to pre-validate state in a case of error. This action, in turn, requires to flush all changes cached in memory into database.

Well. You need the new assembly to try the suggested aproach. It will soon available at our website or I can send it by e-mail.

answered Dec 01 '10 at 05:27

Alex%20Ustinov's gravatar image

Alex Ustinov
2484

Thank you, Alex. Could you please email me the DLL?

(Dec 06 '10 at 14:33) ara ara's gravatar image

While Alex Ustinov keep silence about v3.X, I'll give an answer on how to do the same in v4.X - just to keep the people informed ;)

Actually, there is a wide set of options:

  1. Override Entity.OnInitialize (this.PersistenceState==PersistenceState.New there indicates entity is created, but not materialized) and Entity.OnSetFieldValue` in some of your base types and update necessary properties there.
  2. Achieve the same by subscribing to Created and EntityChanging events in session events.
  3. There are some other alternatives - e.g. you can subscribe to EntityVersionInfoChanged event, or rely on Session.Operations events.

IMO, #2 is the nicest option:

  • You can check if entity supports IHasCreateModifyDates there, and do necessary changes only if this is really necessary.
  • So to get automatic support of such updates, you should just support this interface @ certain types.
  • Also, you can implement IModule injecting this capability to any Session created @ your Domain. Since modules are registered via DomainConfiguration.Types, you should just register the assembly where it's declared to get the feature working.

Btw, you should think about tracking EntitySet events as well.

answered Nov 28 '10 at 16:29

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

edited Nov 28 '10 at 16:30

Hi Alex!

I've found that #3 works perfectly for me, could you tell us why do you prefer #2?

(Nov 29 '10 at 18:17) tao99 tao99's gravatar image
1

Well, #2 and #3 aren't much different:

  • EntityVersionInfoChanged isn't raised if field is marked as [Version(VersionMode.Skip)] - but normally that's exactly what we need in this case.
  • From other points, this approach is simpler (no necessity to subscribe to multiple events);
  • Moreover, EntityVersionInfoChanged normally is triggered just once per transaction - that's good as well.

So likely, #3 is really better :)

(Nov 29 '10 at 21:53) Alex Yakunin Alex%20Yakunin's gravatar image

Btw, concerning INSERT + UPDATE: there is no such problem with DO4. If it can do this at once (i.e. there is no explicit demand to flush the changes between creation and version update) - it will.

Moreover, as you know, insertions, updates and regular queries are batched there.

(Nov 29 '10 at 21:55) Alex Yakunin Alex%20Yakunin's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA