Hi,

When we create an Entity with the 'new' keyword, we make some changes to it that initialize the Entity to a valid one (f.i. the CreatedBy and CreatedOn (date) are set). This means that when no other changes take place we do not need to persist this object at all.

We implemented a 'HasChanges' property like this:

public Dossier()
 {
     CreatedOn = DateTime.Now;
     CreatedBy = User.Id;

     // Secure the VersionInfo so we can test if the Entity has changes or not
     m_VersionInfo = VersionInfo;
 }

 private VersionInfo m_VersionInfo;

 public bool HasChanges
 {
     get
     {
         return m_VersionInfo != VersionInfo;
     }
 }

The HasChanges property is 'false' until another property changes. The question now is. When the Persist() method is called on the Session object we would like to prevent persisting Entities that have HasChanges == false. How, if at all possible, do we do that?

Regards Paul Sinnema Diartis AG


Updated at 19.06.2010 8:04:47

Hi Alex,

Thanks that story is clear to me. I guess we will have to use the Pin() method to pin down those entities we don't want to save an dispose them when we do Persist(). This brings on however another problem. Let me describe the scenario I'm trying to implement.

  • In our Model a Person has one or more LastNames, bound to a time-frame (i.e. Only 1 LastName is current, or has the youngest datetime).

  • When a user creates a new Person, we automatically create 1 empty LastName. We need the empty one because we want to display a list containing at least 1 entry.

  • When the user decides not to enter any LastName (meaning no change to the entry) we don't want to store this LastName but instead create a new empty LastName on Load of the Person.

If we dispose the LastName before the Persist() this empty entry will disappear from the list. Solution means we will have to re-initialize this empty entry after Persist().

After writing the scenario above I think we can implement a solution in our generated code for that. I'll let you know how it goes.

Regards Paul Sinnema Diartis AG


Updated at 20.06.2010 10:59:39

Hi Alex,

Do you ever sleep ;-) Yes, as a matter of fact we do have a CurrentLastName. We also have a FullName that relies on the CurrentLastName. In the case of LastName we almost always need to display all valid LastNames however, so we'll need to load them always.

Regards Paul


Updated at 20.06.2010 14:02:28

Hi Alex,

No you misunderstood. But it is not a big issue. A requirement is that the user can show/hide all historized LastNames. On all Masks we created, there's a history button that gives the user the ability to show/hide all LastNames. We could of course lazy load the list and only load the complete list when the user unhides all historized data, but that would make the already pretty complex code even more complex. In most cases we're talking about 2 to 3 LastNames per Person, no more. So the overhead for loading all of them isn't much.

Regards Paul Sinnema Diartis AG

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Jun 17 '10 at 09:05

Paul%20Sinnema's gravatar image

Paul Sinnema
261878896

edited Sep 06 '10 at 04:53

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412


One Answer:

Alex (Xtensive) wrote:

See viewtopic.php?f=56&t=5877&p=14522&hilit=Pin#p14522 (Session.Pin method).


Alex (Xtensive) wrote:

The case is clear - may be I can give you a better idea:

  • Implement custom collection allowing to manage the whole set of LastNames, and expose it as non-persistent property

  • When it gets changed, ensure it is serialized to e.g. string field (persistent) and additionally updates EntitySet<lastname>

  • When it is accessed for the first time in current outermost transaction, it tries to deserialize itself from the serialized string field.

You get a set fo benefits in this case:

  • The whole set of LastNames is always available immediately along with the Person (because there is its serialized version)

  • On the other hand, there are relational structure behind it allowing to deal with LastNames in queries.

Why this is important here: as far as I can judge, LastNames collection is actually tiny: there is almost always just 1 ot 2 items. But fetching it implies you'll need to either join or prefetch it - and this is relatively costly. That's why I'd try to implement a "dual" structure for this (relational - for queries, and non-relational - for faster fetches).

May be we should think about supporting this scenario directly by DO4.


Alex (Xtensive) wrote:

Btw, do you have something like "CurrentLastName" inside Person type? Most likely, it's enough to have just this additional property, if it is used by almost everywhere, while "LastNames" collection is used only when drop-down list of LastNames is populated.


Alex (Xtensive) wrote:

> Do you ever sleep

Unfortunately, yes ;)

> Yes, as a matter of fact we do have a CurrentLastName...

So, as I understood, there is no necessity to care much about fast LastNames collection loading? I.e. the case when it is loaded is relateively rare?


AndresRohr wrote:

I'm having a DisconnectedState that is connected to a session. In the DisconnectedState I created a new Entity (state is IsNew == true and HasChanges == false). I would like to exclude this entity in 'ApplyChanges()' because I don't want to fill the DB with empy entities. I tried to pin it with "session.Pin(entity)". The pinning returned a valid IDisposable, ApplyChanges() succeeded and unpinnig worked also which looked great but the entity is saved regardless of the pinning. Is that a bug? Or, what is the correct way to prevent a new unchanged entity from being saved?


Alex (Xtensive) wrote:

No, that's not a bug. Pinning affects only on Session.Persist(), and works only inside transaction.

To exclude such entities from being applied, you manually filter OperationLog manually:

  • Enumerate it

  • Build two logs from it: one with operations to apply, and another - with operations to leave

  • Apply the first log by your own. That's tricky, if you want to update DisconnectedState.Versions as well - in fact, you should re-implement our own DisconnectedState.Apply method with filtering.

  • Bind log with left operations back to DisconnectedState. Currently this is possible only via reflection (but pretty easy).

As a solution, we can overload ApplyChanges method in DisconnectedState with one more implementation allowing you to provide your custom OperationLog filter (+ add corresponding member to DisconnectedState to enable its implicit usage) - it won't take much time @ our side.

answered Jun 17 '10 at 19:51

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

Clear then - actually, prefetching them for some page will be fast. But I decided you need this even more frequently - i.e. in almost any case when dealing with Person.

(Jun 17 '10 at 19:51) Alex Yakunin Alex%20Yakunin's gravatar image

We implemented an API allowing do handle this: http://code.google.com/p/dataobjectsdotnet/issues/detail?id=784#c1

(Aug 28 '10 at 08:01) 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