Hi, I'm new to DataObjects and have an initial beginners question. How best to implement a persistent calculated read-only property in DO4? For example, if I have 3 persistent Fields - Qty, Price and Total - how would I define Total (Qty*Price) given an Entity's attributes and lifecycle? -- AlexH Updated at 16.12.2009 20:24:15Many thanks Alex. One of the things that concerned me, was that if using the manual approach, one would need to have complex checks like "IsLoading" or "IsSaving" etc. so that (parts of) the setter would not be called many times. But that doesn't seem to be the case? This thread was imported from our support forum. The original discussion may contain more detailed answer. Original topic by alexh. |
Alex Kofman wrote:There are two principal ways: Override OnSetFieldValue method or implement property accessors manually: Overriding OnSetFieldValue:
Manual property accessors:
Note, that I used private setter for read-only property in both cases. Alex (Xtensive) wrote:See Advanced LINQ Translation chapter as well, especially, "Writing custom LINQ expression rewriter" topic there. It's pretty easy to make such calculated properties work in LINQ queries as well. Alex (Xtensive) wrote:Exactly, you shouldn't check something like IsLoading\IsSaving - DO4 automatically loads any value, if it isn't available. See http://dataobjectsdotnet.googlecode.com ... rmance.htm - there is a good explanation of lazy loading (work on this chapter is in progress, but this part is finished). Alex (Xtensive) wrote:So first of all, Alex Kofman's answer was absolutely precise. But I'd like to provide an "advanced" reply. Imagine that we don't want to store a value of calculated property, but want to just cache it in memory. Key ideas to keep in mind:
Basically, there are two possible ways of detecting this: a) After caching a value, you can subscribe on all the events that might change the result, including Session.TransactionOpen, TransactionRollbacked and notify your cached property on changes in sources. Any of these events must invalidate cached value. b) Instead of subscribing to transaction related events, you may store a reference to Transaction object along with cached value referencing a Transaction where the value was cached. But luckily, there is already TransactionalValue<t> type. Approach a) is less attractive also because you must notify all the entities on completion of certain events - so this must happen even if the value cached inside these entities won't be used at all further. That's why we prefer (and use internally - to cache various transactional state) way b). Check out the sample for way b) (it was committed just today to our repository). Points of interest:
Alex (Xtensive) wrote:Later I'll show how to do similar tricks with queries to cache more complex calculations - there is almost nothing special (the only new type I'll use there will be CachedSequence<t>), but since caching of query results seems pretty common, feel I must show this as well. psulek wrote:
Sorry, it seems I made a mistake by reading your question not carefully. You want to properly cache calculated value as well. I'll provide an example for this shortly. alexh wrote: Thanks Alex, that example you mention would be much appreciated. Yes, these links points to the tip revision of repository, so since the file was recently moved, the old one became obsolete. Thanks for pointing on this! alexh wrote: Many thanks for for all the explanation Alex - Happy New Year to all. -- Alex Hoffman |