Print Page | Close Window

Business Object Server Caching

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce Classic
Forum Discription: For .NET 2.0
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1525
Printed Date: 23-Apr-2025 at 6:07pm


Topic: Business Object Server Caching
Posted By: dobbo
Subject: Business Object Server Caching
Date Posted: 19-Oct-2009 at 7:25am
We are attempting to create an application using the BOS to save objects in the DB and then use Workflow to retrieve the content to send on to a third party.
 
On the whole, this works. However the problem I have is that if there is a discrepancy with the data I have sent to the third party and amend it in our front end to resend the data, the transmitted data still contains the old values. The records in the DB are definitely being updated through the BOS but the workflow, also using the BOS seems to be accessing cached values. It doesn't seem to matter if I use RefetchEntity on the PersistenceManager with a MergeStrategy of OverwriteChanges or just GetEntity with a QueryStategy of DataSourceOnly.
 
So the question is, how can I make the Workflow only see the current records in the DB and not any cached values?
 
Or should we just use the BOS to save the data and have the Wokflows use a 'local' PM to access the data? We currently use InvokeServerMethod to initiate the workflow processes so if we changed to use a local PM how would we start the processes on the server?
 
Does anyone have any ideas?
 
We are currently using Devforce Classic Version 3.6.7.1
 
Thanks
 
John



Replies:
Posted By: kimj
Date Posted: 20-Oct-2009 at 5:54pm
I'm afraid I'm not really clear on the architecture here.
 
For the record, the BOS itself is "essentially" stateless.  I say "essentially" because it does hold static metadata and data can be loaded into the server-side PM.DefaultManager, but for most purposes you can consider it stateless.  This means that queried data from fetch requests is not usually retained in any server-side cache, and save requests update the data store but do not update any server-side cache.  The BOS itself, when asked to fetch, always fetches from only the data store. 
 
DevForce will construct a new and empty server-side PersistenceManager as needed to pass to various server-side interface/event implementations, such as the target method in an InvokeServerMethod call.
 
So, the first question about why cached data is passed to workflow I really can't answer.  Is the workflow created and passed -- "something", what exactly? -- before the amended data is saved? 
 
Can you provide a little more information on both the current and proposed interaction between your workflow and the BOS?
 
 


Posted By: dobbo
Date Posted: 21-Oct-2009 at 12:34am
Thank you for your response.
 
Let me try to explain our architecture a little clearer and what we are experiencing.
 
Our software involves sending data via EDI messages to a third party. So to achieve this we have a front end application that the user will use to enter data which is persisted to the SQL database via BOS. We then call InvokeServerMethod to request our workflow engine to send the EDI message to said third party with a workflow process that gets instantiated new each time.
 
We pass a parameter object to the workflow which contains simply the business object type, it's ID and the primary key name. The workflow then gets a persistence manager and requests the data from the BOS by passing an RDBQuery constructed from the type, id and primary key name to the PM to fetch the records.
 
The problem is that this will work first time, we can use TraceViewer and/or SQL Server Profiler to see the calls being made to the DB. If the response to the EDI message suggests that there is a problem with the data we sent, then the user will amend the content and send it again in the same way described above. This will create a new workflow.
 
The problem is that both the TraceViewer and Sql Server Profiler do not show any request to the DB to refetch the data. The EDI message goes out with the original content even though the DB has changed. The EDI message is not cached anywhere and gets instantiated anew every time.
 
HTH
 
John


Posted By: kimj
Date Posted: 21-Oct-2009 at 10:34am
Thanks for the explanation.  A few questions --
 
You say the "workflow then gets a persistence manager ..." - how is this done?  I'd guess that you're reusing or sharing a PM among workflows.  If you issue an RdbQuery from a PM which uses a BOS and you're not seeing calls to the database, then that PM either thinks it's not connected and is using cache, or it's using cache because it thinks the query can be fulfilled from cache -- the BOS is never hit in the amended content workflow case.  Can you provide a snippet showing how the workflow is using the PM?
 
 
 
 
 
 
 


Posted By: dobbo
Date Posted: 22-Oct-2009 at 12:21am
We have a factory class that provides the PM. This is what the factory class looks like in it's entirety.
 

    /// <summary>
    /// Factory for creating a new Persistence Manager
    /// </summary>
    public class PersistenceManagerFactory
    {
        /// <summary>
        /// Create a new instance of the <see cref="PersistenceManager"/>
        /// </summary>
        /// <returns>The new instance of the <see cref="PersistenceManager"/></returns>
        public PersistenceManager CreatePersistenceManager()
        {
            PersistenceManager persistenceManager = PersistenceManager.DefaultManager;
            ILoginCredential loginCredential = new LoginCredential(string.Empty, string.Empty, string.Empty);
            persistenceManager.Login(loginCredential);
            return persistenceManager;
        }
    }
 
All workflows then do something like;
 

         protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            PersistenceManagerFactory factory = new PersistenceManagerFactory();
            PersistenceManager persistenceManager = factory.CreatePersistenceManager();
            
            Type typeToGet = Type.GetType(Summary.BusinessObjectType);
            RdbQuery query = new RdbQuery(typeToGet, Summary.PrimaryKeyName, EntityQueryOp.EQ, Summary.Id);
 
            RetrievedBusinessObject = persistenceManager.GetEntity(query) as ASMBusinessObject;
 
            if (RetrievedBusinessObject.IsNullEntity)
                RetrievedBusinessObject = null;
            return ActivityExecutionStatus.Closed;
        }
 
In the example above, the 'Summary' property is a simple class that stores the type, primarykey name and the primarykey value. It looks like
 

    [DataContract]
    public class BusinessObjectSummary
    {
        [DataMember]
        public string BusinessObjectType { get; private set; }
        [DataMember]
        public Guid Id { get; private set; }
        [DataMember]
        public string PrimaryKeyName { get; private set; }
        public static BusinessObjectSummary Create(ASMBusinessObject businessObject)
        {
            BusinessObjectSummary newObject = new BusinessObjectSummary();
            newObject.Populate(businessObject);
            return newObject;
        }
        private void Populate(ASMBusinessObject businessObject)
        {
            if (businessObject == null)
                throw new ArgumentNullException("businessObject");
            BusinessObjectType = businessObject.GetType().AssemblyQualifiedName;
            Id = (Guid)businessObject[businessObject.PrimaryKeyName];
            PrimaryKeyName = businessObject.PrimaryKeyName;
        }
    }
 
Thanks,
 
John


Posted By: kimj
Date Posted: 22-Oct-2009 at 8:55am
PersistenceManager.DefaultManager is actually a static singleton instance of the PM for the AppDomain.  There are two potential problems here -- 1) multi-threading problems, because the PM is not thread-safe, and 2) the GetEntity call in your Execute method will go against cache if the query has been seen before.

In your CreatePersistenceManager method, construct a new PM:

   PersistenceManager persistenceManager = new PersistenceManager();

The Login is also optional here, since you're using null credentials.

If you find that it's a little slow for the new PM to connect to the BOS, you can use the PM copy constructor to pass an existing connected/logged in PM.  If the PM.DefaultManager is in that state you can use it, otherwise you could keep a static connected/logged in PM for this purpose. The copy constructor does not copy cache, it copies other "state" information from the source PM.

   PersistenceManager persistenceManager = new PersistenceManager(_connectedPM);



Print Page | Close Window