how to use the Context object

Posts   
 
    
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 16-Sep-2005 21:38:08   

Greetings, I have a form that is displaying an entity object.: 1- User clicks on <Edit> so I store the entity in a context object


           'clear context of entity if present
            Me.GetContext.Remove(Me.CurrentEntity)

            'add entity to context
            Me.GetContext.Add(Me.CurrentEntity)

1- User changes data but clicks on <Cancel> so I re-store the entity from the context object


           Me.GetContext.Get(Me.CurrentEntity)

This seems not working as I expected. How can I use the context to get the desired behaviour? NOTE: before attempting to use the context, I re-fetched the entity from the DB when user <cancel> edit. I thought that context is perfectly suited to this senario flushed

bclubb
User
Posts: 934
Joined: 12-Feb-2004
# Posted on: 17-Sep-2005 03:00:33   

The context is used for uniquing, having the same instance for an entity which is loaded multiple times. The functionality you are looking for is provided in Field data versioning usin SaveFields. there is a good example in the help under Generated code - Unit of work and field data versioning, SelfServicing or Adapter.

 ' VB.NET
Dim customer As New CustomerEntity("CHOPS")
customer.SaveFields("BeforeUpdate")
Try
    ' show a form to the user which allows the user to
    ' edit the customer entity
    ShowModifyCustomerForm(customer)
Catch
    ' something went wrong. Entity can be altered. Roll back
    ' fields so further processing won't be affected by these
    ' changes which are not completed
    customer.RollbackFields("BeforeUpdate")
    Throw
End Try
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 17-Sep-2005 09:28:44   

bclubb wrote:

The context is used for uniquing, having the same instance for an entity which is loaded multiple times. The functionality you are looking for is provided in Field data versioning usin SaveFields. there is a good example in the help under Generated code - Unit of work and field data versioning, SelfServicing or Adapter.

Actually that what I was using prior to trying to use the Context object. The problem with FieldVersioning is that it does NOT keep a version of the related entities/collections. I had two solution for this: 1- binary-serialize the complete object graph and store it away, then de-serialize it when I need to re-set the entity to its pre-edit state 2- re-fetch the entity (and its related) from the DB

My reasoning was that option (2) would re-fetch the entity (and its related) from the context instead from the DB (as if the context cached the entity and its related). So the question becomes: Is possible to store an entity (and its related) in a context and later retrieve that entity's data (and its related) from the context instead from the DB?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 17-Sep-2005 11:28:51   

omar wrote:

bclubb wrote:

The context is used for uniquing, having the same instance for an entity which is loaded multiple times. The functionality you are looking for is provided in Field data versioning usin SaveFields. there is a good example in the help under Generated code - Unit of work and field data versioning, SelfServicing or Adapter.

Actually that what I was using prior to trying to use the Context object. The problem with FieldVersioning is that it does NOT keep a version of the related entities/collections.

Correct.

I had two solution for this: 1- binary-serialize the complete object graph and store it away, then de-serialize it when I need to re-set the entity to its pre-edit state 2- re-fetch the entity (and its related) from the DB

Indeed. Often 2 is more efficient.

My reasoning was that option (2) would re-fetch the entity (and its related) from the context instead from the DB (as if the context cached the entity and its related). So the question becomes: Is possible to store an entity (and its related) in a context and later retrieve that entity's data (and its related) from the context instead from the DB?

The fetcher always fetches again from the DB, unless you do a context.Get() to get an entity instance in the context, for example when you only need 1 entity. It always fetches from the db because it doesn't know if the context contains all the entities available in the db.

If a context is used for fetching, it will follow this procedure for every entity read from the db: - after the entity data is stored into a new instance of the entity type, it calls context.Get(). - context.Get, checks if it has an entity instance of the same type and the same PK. - if it does, it will grab that entity instance, and check if it has to set that entity's fields to the fields of the passed in entity. This is true by default. - the found entity is returned.

So after a fetch, you'll have the same entity instances, but with updated field data.

Frans Bouma | Lead developer LLBLGen Pro
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 17-Sep-2005 20:27:19   

Otis wrote:

The fetcher always fetches again from the DB, unless you do a context.Get() to get an entity instance in the context, for example when you only need 1 entity.

does this meen that I cannot use myContext.Get() to retreive an entity from the context, but rather I have to use the adapter's FetchEntity and the adapter would take care of checking the context for any entity with the requested PK values? How does the adapter know about my context (because I assume my adapter Fetch calls are the standard calls with no extra parameters that specify context object?) If I have more that one context object active, would the adpter check them all?

Otis wrote:

If a context is used for fetching, it will follow this procedure for every entity read from the db: - after the entity data is stored into a new instance of the entity type, it calls context.Get(). - context.Get, checks if it has an entity instance of the same type and the same PK. - if it does, it will grab that entity instance, and check if it has to set that entity's fields to the fields of the passed in entity. This is true by default. - the found entity is returned.

If I understand you correctly, this is how it works in code

'ent1 is an entity in Fetch status...
myContext.Add(ent1)  'stores ent1 in the context

'do some work with entity1 that changes its fields and its related entities and related collections

'ent2's PK fields have the same values as those of ent1, but ent2's status is New
adapter.FetchEntity(ent2) 'would this load ent2 from context's ent1 ??

Otis wrote:

So after a fetch, you'll have the same entity instances, but with updated field data.

does this meen that I have to use the same entity instance with the adapter Fetch and NOT a new entity instance? Previous code should be..

'ent1 is an entity in Fetch status...
myContext.Add(ent1)  'stores ent1 in the context
'do some work with entity1 that changes its fields and its related entities and related collections

'the following would fetch ent1 from the context and I would get ent1 exactly as it was
'when I added it to the context (including related entities and related collections)
adapter.FetchEntity(ent1)
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 19-Sep-2005 10:25:25   

omar wrote:

Otis wrote:

The fetcher always fetches again from the DB, unless you do a context.Get() to get an entity instance in the context, for example when you only need 1 entity.

does this meen that I cannot use myContext.Get() to retreive an entity from the context, but rather I have to use the adapter's FetchEntity and the adapter would take care of checking the context for any entity with the requested PK values?

Sure you can use Get to retrieve an entity from the context, but 'retrieve' here means you get a reference to the object in the context. This means that if you change a value in that object, the value is also changed in the object in the context, as it's the same object.

How does the adapter know about my context (because I assume my adapter Fetch calls are the standard calls with no extra parameters that specify context object?) If I have more that one context object active, would the adpter check them all?

By default an entity isn't in a context. If you add an entity to a context, its ActiveContext property is set to that context. Also, any related entities are added to the same context as well. You can have multiple contexts, thats no problem, the DataAccessAdapter checks teh ActiveContext property of the entity it works on.

Otis wrote:

If a context is used for fetching, it will follow this procedure for every entity read from the db: - after the entity data is stored into a new instance of the entity type, it calls context.Get(). - context.Get, checks if it has an entity instance of the same type and the same PK. - if it does, it will grab that entity instance, and check if it has to set that entity's fields to the fields of the passed in entity. This is true by default. - the found entity is returned.

If I understand you correctly, this is how it works in code

'ent1 is an entity in Fetch status...
myContext.Add(ent1)  'stores ent1 in the context

'do some work with entity1 that changes its fields and its related entities and related collections

'ent2's PK fields have the same values as those of ent1, but ent2's status is New
adapter.FetchEntity(ent2) 'would this load ent2 from context's ent1 ??

No, it would fetch ent2 into a new object. THough if you do: adapter.FetchEntity(ent2, myContext) it will first fetch ent2, then, it will do a myContext.Get(ent2), which means it will update ent1 with the data loaded by the fetch of ent2. However, if you do: adapter.FetchEntity(ent2) ent2 = myContext.Get(ent2) it means that the variable ent2 points to the instance ent1, as Get will return the object with the same PK values already in the context.

It's important to remember the difference between an instance and the reference variable pointing to the instance. 'ent2' is the referencing variable, not the entity itself. This knowledge is used to get the same instance back pointed to by ent1, and the context is used to do that.

On a similar note, you can grab ent1 from the context without a fetch: ' set ent2's pk field to the same pk field value as ent1: '1' Dim ent2 As new SomeEntity(1) = CType(myContext.Get(ent2), SomeEntity) now ent2 points to the same instance as ent1 pointed to.

You can also do: Dim ent2 As SomeEntity = CType(myContext.Get(New SomeEntityFactory(), 1), SomeEntity) which grabs the entity pointed to previously by ent1 from the context (the one with the PK value '1') or creates a new one if it doesn't exist (hence the factory)

Otis wrote:

So after a fetch, you'll have the same entity instances, but with updated field data.

does this meen that I have to use the same entity instance with the adapter Fetch and NOT a new entity instance? Previous code should be..

'ent1 is an entity in Fetch status...
myContext.Add(ent1)  'stores ent1 in the context
'do some work with entity1 that changes its fields and its related entities and related collections

'the following would fetch ent1 from the context and I would get ent1 exactly as it was
'when I added it to the context (including related entities and related collections)
adapter.FetchEntity(ent1)

No, that's not how the context works. The context doesn't preserve state, it only keeps references to objects, it's a way to find back objects you've created previously, it's not used to preserve state. This is a plan I'm trying to implement in v2, where I'll try to see if it will work: preserving state when an entity is added, but at the moment, you can't do that with the context. So if you store an entity in the context and change it afterwards, the entity in the cotnext is ofcourse also changed, simply because the ENTITY isn't really in the context, the reference to the entity is in the context: the context and your ent1 variable point to the same object in memory.

Frans Bouma | Lead developer LLBLGen Pro
Fabrice
User
Posts: 180
Joined: 25-May-2004
# Posted on: 21-Sep-2005 16:16:12   

Hi

I also have a question about the Context. We are checking to improve some performance on our SQL server and one way whe think about it to put a Cache on the Application server (where are put llblgen etc) and so decrease sql server queries. A bit history on our project : We are using the Adapter model in a webservices scenario on multiple databases with the same schemas. The IIS session store the database to use for the user connected.

So my question : - Do you think it's a good idea to put a Cache there ? Because the bad side I think is that we can move the performance problem on the Application server. - Do you think the Context can be used for that ? Or we need a more specific implementation ? - the Context is thread safe ? Because we have many threads (webservices calls), so I see it as a static property somewhere, and accessed by all threads.

Of course I suppose if we put a cache we need to put some process to - free the cache (remove unused objects) - save the modifications on the database (at specific interval ?) etc.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 22-Sep-2005 10:47:12   

Fabrice wrote:

Hi

I also have a question about the Context. We are checking to improve some performance on our SQL server and one way whe think about it to put a Cache on the Application server (where are put llblgen etc) and so decrease sql server queries. A bit history on our project : We are using the Adapter model in a webservices scenario on multiple databases with the same schemas. The IIS session store the database to use for the user connected.

So my question : - Do you think it's a good idea to put a Cache there ? Because the bad side I think is that we can move the performance problem on the Application server.

No. Cache as high up the call stack as possible. This means that an application calling the webservice should cache data, not the webservice. For example, data which changes not that often, can be read once a day and the client can re-use the cached data during the day. Processed data can be cached as well, for say, 1 hour. I'm not sure if this is possible of course. It might be you have thousands of users, which means that the client-side caching isn't taking effect after thousands of queries. So in THAT case, cache on the server, but as high in the callstack as possible.

  • Do you think the Context can be used for that ? Or we need a more specific implementation ?

Context is for uniquing. Caching is something else. It does keep reference of objects, but for uniquing purposes. This means that query strategies aren't using the context object till after the results are back. (A normal cache has to do that too anyway, because if you fetch all customers with a given filter, it doesn't know if all the customers in the db matching the query are in the cache, so it has to query the customers anyway. After that, it has to update the cached customers (if applicable), and add not-yet-cached customers to the cache.

The cache can save some cycles when you pull a list of PK's only from the db, compare that to the cached objects and pull the non-cached variants from the db and return the cached ones together with the non-cached ones. This CAN save some cycles, but it's also unsafe, as it might be your db version is updated, so you also need to keep versioning info in your entities.

Often it's a good strategy to simply cache everything for say 30 seconds. Sites like slashdot render itself completely (without the very dynamic stuff) every n seconds and cache that on a special box for that purpose. If the system can render itself every n seconds, you never have a slow site, because the requests are made to the cached version. If load gets too heavy, or rendering needs n+m seconds, you can tweak the parameter.

  • the Context is thread safe ? Because we have many threads (webservices calls), so I see it as a static property somewhere, and accessed by all threads.

Context isn't thread safe, it's a context specific uniquing object, so sharing it among threads doesnt make sense, as another thread automatically means another semantic context.

But before you dig in, first profile, and investigate. Nothing can be more harming to performance as micro-optimizations in the wrong place. So if some queries are executed a lot and also eat a lot of time away from the sqlserver box, see if you can optimize these queries, cache some of its data temporarily somewhere, add more indexes etc.. that's more effective than spending a lot of time optimizing things which aren't slow now.

Frans Bouma | Lead developer LLBLGen Pro
Fabrice
User
Posts: 180
Joined: 25-May-2004
# Posted on: 29-Sep-2005 14:40:15   

Thanks for your reply!