Print Page | Close Window

Using a custom DataSourceKeyResolver and serializing EntityCacheState won't work

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2009
Forum Discription: For .NET 3.5
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1573
Printed Date: 16-Apr-2024 at 7:47am


Topic: Using a custom DataSourceKeyResolver and serializing EntityCacheState won't work
Posted By: pk55
Subject: Using a custom DataSourceKeyResolver and serializing EntityCacheState won't work
Date Posted: 30-Nov-2009 at 12:26pm

Per Kim in this thread:

http://www.ideablade.com/forum/forum_posts.asp?TID=1527&KW=formatter+error&PID=5687#5687 - http://www.ideablade.com/forum/forum_posts.asp?TID=1527&KW=formatter+error&PID=5687#5687
 
the ProbedKeyResolver on the EntityCacheState shouldn't be serialized when using InvokeServerMethod if you have a customer data source key (which we do).  She suggested setting it to NULL prior to getting the entity cache state, however, this wipes out the key resolver.
 
So why is the ProbedKeyResolver marked as a DataMember to be serialized in the first place if it can't and shouldn't be seriliazed back to the client?  If we do what Kim suggested, the entity cache state serializes back but our key resolver gets wiped out and since DevForce seems to be passing the same instance of the DataSourceResolver around, changing it to NULL as well as resetting it after grabbing the Entity Cache State affects any other connections that are active on the Business Object Server as well as the cache state we just grabbed (which then won't serialize).
 
Any thoughts?
 
 



Replies:
Posted By: pk55
Date Posted: 30-Nov-2009 at 1:54pm
Some additional info:
 
I've been able to grab the ProbedKeyResolver from the DataSourceResolver and store it in a static property (of type OBJECT) that I then make a separate RPC call to restore it on the server after the entity cache state has finished serializing back to the client.
 
This obviously is not a long-term solution; it only works during the development phase (when everyone is running off their own IIS server or Cassini server) since modifying the ProbedKeyResolver would affect all BOS connections for the one single instance of the Entity Service (please correct me if that assumption is wrong).  It seems like we'd need some kind of fix that would not require nulling out the ProbedKeyResolver once it's been set.
 
     public class DataLoaderService
    {
         public void LoadInitialDataAsync()
        {
            var manager = GlobalEntityManager.Current;
            ServerMethodDelegate myDelegate = new ServerMethodDelegate(LoadInitialData);
            manager.InvokeServerMethodAsync(myDelegate, ServerMethodCallback, null, null);
        }
 
        private void ServerMethodCallback(InvokeServerMethodEventArgs args)
        {
            var manager = GlobalEntityManager.Current;
            EntityCacheState cache = (EntityCacheState)args.Result;
            if (cache != null)
            {
                cache.Merge(manager, new RestoreStrategy(true, true, MergeStrategy.OverwriteChanges));
            }
 
            // make a second RPC call to restore the key resolver
            ServerMethodDelegate myRestoreDelegate = new ServerMethodDelegate(RestoreKeyResolver);
            manager.InvokeServerMethodAsync(myRestoreDelegate, null, null, null);
        }
 
        public static object savedResolver { get; set;}
 
        [AllowRpc]
        public static EntityCacheState LoadInitialData(IPrincipal principal, EntityManager manager, params object[] args)
        {
            if (savedResolver == null)
            {
                // grab it so it can be restored in a subsequent call
                var resolver = manager.DataSourceResolver.ProbedKeyResolver;
 
                // cast it as an object so it doesn't get wiped out when we set the ProbedKeyResolver to NULL later
                savedResolver = (object) resolver;
            }
 
            ... load up the entity manager with a bunch of entities...
 
            // wipe out the ProbedKeyResovler so the cache will serialize
            manager.DataSourceResolver.ProbedKeyResolver = null;
            var cache = manager.CacheStateManager.GetCacheState();
            return cache;
 
        }
 
        [AllowRpc]
        public static string RestoreKeyResolver(IPrincipal principal, EntityManager manager, params object[] args)
        {
            if (savedResolver != null)
            {
                manager.DataSourceResolver.ProbedKeyResolver = (IDataSourceKeyResolver)savedResolver;
            }
            return string.Empty;
        }
}
 


Posted By: WardBell
Date Posted: 30-Nov-2009 at 7:00pm
Phil - I see the problem. I fear Kim's recommendation may not have been a good one for the reason you give: the act of setting the ProbedKeyResolver to null wipes it out for all consumers of the DataSourceResolver, not just for this particular EntityManager inside your server method.
 
I've poked around and there is no obvious answer.
 
I'm not sure why you used a static to hold the savedResolver - I would think you could just swap out and swap in at the point needed as in:
 
   var resolver = manager.DataSourceResolver.ProbedKeyResolver;
   manager.DataSourceResolver.ProbedKeyResolver= null;
   var cache = manager.CacheStateManager.GetCacheState();
   manager.DataSourceResolver.ProbedKeyResolver= resolver;
   return cache;
 
But I wouldn't trust this because there is a race condition, a brief moment when the DataSourceResolver.ProbedKeyResolver is null, which could cause hard to find bugs elsewhere. It's also ugly as sin.
 
I looked at "manager.DataSourceResolver = null;"  This would only affect the EntityManager in your server method which will evaporate anyway when the method exits. Unfortunately that will cause your client app to bomb when you merge in the EntityClientState (ECS) on the client; apparently we need a non-null DataSourceResolver during the merge.
 
How about "manager.DataSourceResolver = new DataSourceResolver;"?  Sorry, the constructor is internal; you're not supposed to create these yourself.
 
How about "cache.DataSourceResolver.ProbedKeyResolver=null;"? Nope; the EntityCacheState.DataSourceResolver is internal and inaccessible.
 
You are definitely trapped. The only decent way out (before we find a fix) is to make your ProbedKeyResolver serialize across the wire AND make sure that a structurally matching type of that name exists on the client. You might try that while I go work on a proper fix.
 
You ask "So why is the ProbedKeyResolver marked as a DataMember to be serialized in the first place if it can't and shouldn't be serialazed back to the client?"
 
That's a two part question. Why we mark it for serialization I do not know. Maybe it is supposed to be serialized across some other boundary, just not the server/client boundary. But you can make your DataSourceKeyResolver serializable and, in your case, it looks like you will have to do so.
 
Remember that you can't just make it serializable; you have to deploy it on the client as well.
 
Let me know if this works (or if it doesn't).


Posted By: WardBell
Date Posted: 30-Nov-2009 at 7:11pm
Phil - I now realize why you had to play the double-server-method call game and couldn't do the swapping I suggested. Silly of me. Of course restoring the DataSourceResolver.ProbedKeyResolver BEFORE the cache has been serialized would accomplish nothing.
 
Your "fix" practically guarantees that there will be a race condition ... as you knew.
 
Let me know if you are successful serializing your DataSourceKeyResolver.
 
If not, I have a trick up my sleave that I do not want to pull out. Hint: it involves tricking the BOS into creating a dummy DataSourceResolver that you will never actually use; this one can have its ProbedKeyResolver zapped and be substituted into manager.DataSourceResolver before you extract the ECS. It's ok to fool the EntityManager in your server-side method because only that method instance will ever see or use that manager.
 
Absurd, I know. We're grasping until I can get a better fix in.


Posted By: pk55
Date Posted: 01-Dec-2009 at 9:26am
We tried to reset the key after grabbing the cache but the cache itself has a pointer to the same DataSourceResolver in the server-side EM (not a copy of it) and so resetting the EM version also resets the cache's version which is why I had to delay resetting it until AFTER the cache had serialized back to the client.  Need to use a static (which I've since changed to just be of type IDataSourceKeyResolver instead of object) to allow the server-side to remember what the resolver was prior to nulling it out.
 
We also thought about creating the client-side version to serialize but that's also not a long-term solution as I thought there are things in the resolver that we don't want to expose to the client for security reasons.  If that's not true, then maybe just serializing it would be okay.
 
For the moment (until we get a fix from IdeaBlade) we can live with the resetting being done in two separate calls as we're still in development and until we have multiple connections against a single BOS, it's not going to cause problems.
 
The question we have is why does the Entity Cache State even hold the DataSourceKeyResolver?  It seems like that shouldn't really be part of the cache but at a higher level.
 
--phil


Posted By: pk55
Date Posted: 01-Dec-2009 at 9:30am
I didn't see your second post before I posted my previous reply (got to learn to scroll all the way down).
 
What's your opinion about whether exposing the key resolve to the client is a security concern?  If it isn't, we can try to serialize it.  Otherwise, I'd be up for the other trick.


Posted By: WardBell
Date Posted: 01-Dec-2009 at 10:11am
I share your dislike for having to serialize and transmit the key resolver. I still recommend this course.
 
I too considered the security risks involved in having that resolver on the client. As long as you know that the class will be there, you can mitigate the risk without great difficulty.
 
Write your key resolver so that it is serializable but with NO serializable state. If there is anything in the class that must not appear on the client (what could that be? Certainly don't want to bake connection strings into your code), hide that in a helper class which the key resolver can acquire on the server ... but which the clent cannot reach. The reference to the instance of this helper class will be held in a variable of the server-side key resolver instance but, because you have not included it in the Data Contract, there will be no attempt to serialize it (which would fail) while serializing the key resolver for the client.
 
A PITA. Absolutely.  I have filed a defect report. I cannot provide a timetable for a fix. Frankly, you need a rare combination of circumstances to encounter this problem and the workaround is annoying but not onerous.
 
We "need" the DataSourceResolver (but not the ProbedKeyResolver) on the client when you merge the ECS into the target client EntityManager. That DataSourceResolver holds information about EntityKeys that may be needed by the target manager during the merge. In YOUR scenario, there is no EntityKey information that the target manager lacks; that is not always true in the general case.


Posted By: stephenmcd1
Date Posted: 01-Dec-2009 at 10:19am

Luckily, our (I work with Phil) custom resolver doesn't have much logic in - it mostly just defers processing to other classes that do the heavly lifting.  And as you mentioned, the client-side version only needs to match structurely with the one on the server.  In our case, the server version doesn't have any instance members (other than the GetKey method of course), so I was able to make a simple version that just returns null in just a few lines of code.  It's an easy solution and doesn't expose any senstive data.  Of course, now I'm stuck with this "Data" level class in my Silverlight client, but it's a temporary solution for now.



Posted By: pk55
Date Posted: 01-Dec-2009 at 10:34am

As long as Stephen has the serialized version working, getting a real fix soon isn't as critical to us but I do think that a fix might be needed for other DevForce customers.

Thanks for the help Ward.


Posted By: stephenmcd1
Date Posted: 20-Jun-2011 at 4:21pm
As an update, this is no longer an issue as of 6.1.1.0.  The ProbedKeyResolver no longer needs to be serialized so it doesn't need to exist on the client.  From the release notes:

Fixed issue with non-serializable instances of the EntityManager.DataSourceResolver.ProbedKeyResolver. The ProbedKeyResolver property no longer needs to return a serializable IDataSourceKeyResolver. [D1684]



Print Page | Close Window