Custom 2nd Level Cache Handling

11 posts, 0 answers
  1. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 15 Sep 2014 Link to this post

    Is it possible, or in the works, to be able to provide a custom mechanism for handling the 2nd level cache? Most of our clients will not permit the installation of MSMQ. We want to be able to use our own WCF database syncing service to handle working with the 2nd level cache but there doesn't seem to be any API calls for it.

  2. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 16 Sep 2014 in reply to Greg Link to this post

    Or is there possibly a workaround for detaching an object from a context. I've implemented an event system that works without having to touch the 2nd level cache however there is no good way to remove an object from a context (bypassing the mark for deletion).
  3. DevCraft banner
  4. Viktor Zhivkov
    Admin
    Viktor Zhivkov avatar
    291 posts

    Posted 17 Sep 2014 Link to this post

    Hello Greg,

    I can share your concern about un-availability of MSMQ in certain deployment scenarios and we have identified this as area requiring further improvement on our side. Unfortunately I cannot give you any time frame when we will be able to implement this feature request.

    Regarding your second post - detaching object (or object graph) from a context, I am not sure if you have seen our Attach/Detach API or if it matches your desired behavior.
    I believe you will be able to utilize the Detach API to build custom cache of objects not managed by OpenAccessContext instance. If it was your idea I believe it is worth the shot.
    If not please describe your idea for a work around so we can give you feedback and any advice how to implement it in most efficient way.

    We are looking forward your feedback.

    Regards,
    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  5. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 17 Sep 2014 in reply to Viktor Zhivkov Link to this post

    Well the Detach API allows me to create a detached object from the context. I want to be able to detach an object that I know is already being tracked by the context without calling Remove then SaveChanges as this sends a call to the database.
  6. Viktor Zhivkov
    Admin
    Viktor Zhivkov avatar
    291 posts

    Posted 19 Sep 2014 Link to this post

    Hello Greg,

    Can you please clarify what do you mean by this sentence:
    "I want to be able to detach an object that I know is already being tracked by the context without calling Remove then SaveChanges as this sends a call to the database."

    If you call context.Remove(entity) the object will be deleted permanently. If you have in mind different meaning for the context.Remove() method you will need a different way to do so.

    Calling context.CreateDetachedCopy(entity) will result in having a separate entity copy detached from the context and the original still linked to it. The original attached entity instance will be removed from the context-level cache only when you dispose the context, call context.SaveChanges() or ClearChanges().
    Removing the original object from Level 2 Cache is a bit more complex - you can use context.LevelTwoCache.Evict() for the local cache instance. Without MSMQ you will have to implement synchronization manually and call on the remote application again one of the Evict() methods.

    If this information does not help you achieve your goals, please describe your scenario in finer details and give us a use case scenario that will enable us to understand the challenge better.

    Regards,

    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  7. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 22 Sep 2014 in reply to Viktor Zhivkov Link to this post

    Since I can not use MSMQ I have created my own WCF syncing service that I manually call by overriding the SaveChanges() method. Take the following example:

    1) Process A deletes an entry from the data source
    2) If this operation is successful, the WCF service is invoked with the action that was done (deleted in this case) and the affected object
    3) Process B receives the request from Program A and performs some logic to determine what to do with the change
    4) Process B figures out the action was a delete

    At this point Process B needs a way to completely remove this object from it's context. Currently I am calling Delete(), SaveChanges(), and then catching any exceptions that occur because I know that the entry is no longer in the data source. I am wondering if there is some other way to completely remove the object from the context or is this the only workaround.
  8. Viktor Zhivkov
    Admin
    Viktor Zhivkov avatar
    291 posts

    Posted 24 Sep 2014 Link to this post

    Hi Greg,

    Thank you for the detailed explanation.
    I believe you  should be concerned only with invalidation of Level 2 cache and not the object data inside the OpenAccessContext instance(s) itself (as we call it Level 1 Cache).
    We have deliberately prohibited the API that you want to use because of the complexity of possible scenarios and associations between objects in the context level data container.

    In general any changes pending on context level over objects that are modified or deleted from another context/application are subject of concurrency control. You can check this article how to set up concurrency control on entity type level and here you can see how to handle concurrency conflicts when saving data.

    For scenarios where you need the latest fresh data from the database my suggestion is to always use a fresh instance of your OpenAccessContext class to perform a query. After disposing the context instance the associated Level 1 Cache container will be destroyed and this way won't require remote invalidation.
    Please note that the whole setup described above assumes that you are using short-living OpenAccessContext instances that are used to perform single business operation and are destroyed after it. If you are using singleton or another form of long-living/shared context instance my suggestion is to evaluate if you can move to short-living pattern because it has plenty of inherent benefits and allows you to avoid issues like committing half-baked data due to shared transaction control or locking in multi-threaded scenarios.

    Regards,
    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  9. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 25 Sep 2014 in reply to Viktor Zhivkov Link to this post

    I have tried to use short-lived contexts before with out any luck. I always get the error "Object references between two different object scopes are not allowed", even if I call Cache.Release or Cache.ReleaseAll. Take the following example:

    var item = new Item();
    item.Name = "Item";

    using (var context = new DbContext(ConnectionString))
    {
       try
       {
         context.Add(item);
         context.SaveChanges();
       }
       finally
       {
          context.Cache.Release(item);
       }
    }

    using (var context = new DbContext(ConnectionString))
    {
       item.Name = "Item2";

       try
       {
         context.Add(item); //Error thrown
         context.SaveChanges();
       }
       finally
       {
          context.Cache.Release(item);
       }
    }







    Object references between two different object scopes are not allowed.






    Object references between two different object scopes are not allowed.






    Object references between two different object scopes are not allowed.







    Object references between two different object scopes are not allowed.







    Object references between two different object scopes are not allowed.








    Object references between two different object scopes are not allowed.






    Object references between two different object scopes are not allowed.
  10. Viktor Zhivkov
    Admin
    Viktor Zhivkov avatar
    291 posts

    Posted 26 Sep 2014 Link to this post

    Hello Greg,

    Assuming that the code snippet you have posted is the body of a single method, I would say that this is a wrong way to use Telerik Data Access persistence.
    The moment you call context.Add(item) on any new entity instance it will be attached to the context and managed by it. As you already know trying to do the same using another context instance results in exception. This is caused by the fact that we are injecting state-tracking code in every persistent type that is included in your data model. This state information is tied closely to the context instance and the only way to break that link is through the CreateDetachedCopy() method. Even using the Detach API you will get a copy of the object and not the original one because it may be part of a larger graph that is still managed by the original context instance and having gaps in our knowledge for object states would results in unpredictable operations during context.SaveChanges().
    So getting back to your scenario - if you want to create two similar Item instances you will have to call the constructor twice and have two separate objects. 
    The scenario where you may want to load an object using one context instance and then update it through another is similar - you have to either use the same context instance for both operations or you will have to detach the object from the first one and then attach it back to the second context and then persist changes.

    Calling context.Cache.Release() is dangerous business and our general suggestion is to avoid it as much as possible. Besides that it will not allow you to detach an entity instance and will the cache (L1 cache) will be destroyed when the context instance is disposed anyway.

    I hope that this will make the whole picture a bit more clear and will help you organize your code to fit better your needs. If you need any further assistance do not hesitate to contact us.

    Regards,
    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  11. Greg
    Greg avatar
    11 posts
    Member since:
    Aug 2014

    Posted 26 Sep 2014 in reply to Viktor Zhivkov Link to this post

    Viktor Zhivkov said:Hello Greg,

    Assuming that the code snippet you have posted is the body of a single method, I would say that this is a wrong way to use Telerik Data Access persistence.
    The moment you call context.Add(item) on any new entity instance it will be attached to the context and managed by it. As you already know trying to do the same using another context instance results in exception. This is caused by the fact that we are injecting state-tracking code in every persistent type that is included in your data model. This state information is tied closely to the context instance and the only way to break that link is through the CreateDetachedCopy() method. Even using the Detach API you will get a copy of the object and not the original one because it may be part of a larger graph that is still managed by the original context instance and having gaps in our knowledge for object states would results in unpredictable operations during context.SaveChanges().
    So getting back to your scenario - if you want to create two similar Item instances you will have to call the constructor twice and have two separate objects. 
    The scenario where you may want to load an object using one context instance and then update it through another is similar - you have to either use the same context instance for both operations or you will have to detach the object from the first one and then attach it back to the second context and then persist changes.

    Calling context.Cache.Release() is dangerous business and our general suggestion is to avoid it as much as possible. Besides that it will not allow you to detach an entity instance and will the cache (L1 cache) will be destroyed when the context instance is disposed anyway.

    I hope that this will make the whole picture a bit more clear and will help you organize your code to fit better your needs. If you need any further assistance do not hesitate to contact us.

    Regards,
    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     


    That's the problem I am seeing. This is in one method because it is a sample test case but I am disposing the context. Shouldn't the object be able to get attached to a different context if the context is disposed? If not then judging by your response it seems like there is no easy way to load an object with one context, dispose it, and attach it to a new context for updating. The only option at this point would be for me to re-architect my application which is not possible at this point. Thanks for the help.
  12. Viktor Zhivkov
    Admin
    Viktor Zhivkov avatar
    291 posts

    Posted 01 Oct 2014 Link to this post

    Hi Greg,

    The way to manipulate an entity instance using more than one context is to use the CreateDetachedCopy API.
    The minimal change to your code snippet may be:
    01.var item = new Item();
    02.item.Name = "Item";
    03. 
    04.using (var context = new DbContext(ConnectionString))
    05.{
    06.   try
    07.   {
    08.     context.Add(item);
    09.     context.SaveChanges();
    10.   }
    11.   finally
    12.   {
    13.      item = context.CreateDetachedCopy(item);
    14.   }
    15.}
    16. 
    17.using (var context = new DbContext(ConnectionString))
    18.{
    19.   item = context.AttachCopy(item); // attach instead of Add()!
    20.   // your original code
    21.}

    The other option is to merge your using blocks into a single one and that way reuse the first context instance.

    Regards,
    Viktor Zhivkov
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
Back to Top
DevCraft banner