1

In a linked question I asked about a problem with Geode.Net client casting Geode PDX types to objects (domain objects) and this time I've a problem casting objects to Geode PDX types. It seems a region can hold both pdx serialized object types and other types of object and / or internal object types. By working with byte offsets I guess.

Anyway, this link shows a way to check for object types and handle them:

// get checks Object type and handles each appropriately
Object myObject = myRegion.get(myKey);

either as PDX instances or as domain object types:

if (myObject instanceof PdxInstance) {
  // get returned PdxInstance instead of domain object    
  PdxInstance myPdxInstance = (PdxInstance)myObject;

then with GetField for a PDX Instance you can update just 1 field, very nice...

My problem is with domain objects:

else if (myObject instanceof DomainClass) {
// code to handle domain object instance  

When coming from an event listener

public void AfterCreate(EntryEvent<TKey, TVal> ev)

I first of all try to cast ev.NewValue to Pdx or then the domain object and keep getting one of the following:

When the value in the region is a PdxInstance then without the if (myObject instanceof PdxInstance) as above to prevent it the cast IPdxInstance pdx = (IPdxInstance)ev.NewValue gives:

System.InvalidCastException: Unable to cast object of type myObject to type
Apache.Geode.Client.IPdxInstance

Which is to be expected. I seem to have broken the PdxSerialisation of myObject by creating the region <key, value> as <string, Object> even though myObject extends PdxSerializable and writing keys & values to the region is going through the ToData() override.

So then to deal with myObject directly for example myObject = ev.NewValue or myObject.Field1 = ((myObject)(ev.NewValue)).Field1 or like variations is giving:

Cannot implicitly convert type 'TVal' to 'myObject' 
Cannot convert type 'TVal' to 'myObject'

Surely the event ev.NewValue to myObject cast ought to be straightforward so what am I missing? Otherwise I have to use ev.Key (which does cast without any exception) to specifically get the value again from the region in the cache by using:

IRegion<string, Object> r = cache.GetRegion<string, Object>(region);
return r[key];

So when the object is already given in the TVal type NewValue then why I just can't access it there? :-s

rupweb
  • 3,052
  • 1
  • 30
  • 57
  • 1
    Could you post the code containing the cast that throws this exception ? – Randy May Feb 03 '18 at 02:22
  • Thanks for comment. I updated the question. – rupweb Feb 03 '18 at 10:31
  • 1
    Are you using the Native Client? I ask this because your "string" object is lower case and your "AfterCreate" is Pascal-Case, which are .NET conventions. If so, your issue may not be with PDX. Extend the ReflectionBasedAutoSerializer and put a breakpoint in "ReadTransform" and you will see which field is giving you the problem: see http://gemfire-native-90.docs.pivotal.io/native/dotnet-caching-api/extending-pdx-autoserializer.html. Also, if it's trying to cast to IPdxInstance, you must have read-serialized=true. Are you sure that's what you want? – Wes Williams Feb 05 '18 at 14:59
  • @WesWilliams nice comment but it's the key that's `string` not `String` and I am having no problem casting the key. It's the value `Object` that I have a problem with. Also, for the serialization the problem is at compile time, I can't get as far as runtime. Lastly, yes whether or not to PDX seems to be an issue. For REST we need PDX but not for an internal domain object that is if the internal object isn't ever gonna be used in a REST capacity... hmm – rupweb Feb 05 '18 at 15:39

2 Answers2

1

I recommend that you remove "read-serialized=true" and remove the type checking for IPdxInstance. You can still have PDX objects in your region without read-serialized=true. You really want the deserialized domain object itself in your callback and not the PDX instance. Turning off read-serialized will accomplish that and give you back your deserialized domain object in the AfterCreate method.

Note: The above recommendation is based on the fact that you are using a .NET client and .NET is giving you a compile error. This does not occur with Java clients. It's a bit puzzling why the .NET interaction is doing this.

FYI that it's well known that a region may contain either a PDX instance or a domain object in a region where PDX is being used. This is for performance efficiency. A primary node may store the domain object in a non-PDX form to avoid serialization costs but a PDX version on a secondary server. That is why clients always need to check the type before operating on the instance.

Yes, I understood that string was your key but it was whether or not you were using the Native Client that was my key question. The lower-case "s" was a clue.

Wes Williams
  • 266
  • 1
  • 5
  • Yes, using the .Net native client. Also the PDX is set to true because it's a [REST requirement](https://geode.apache.org/docs/guide/16/rest_apps/chapter_overview.html) – rupweb Jul 04 '18 at 13:02
  • So I suppose that REST enabled regions could be PDX and the non REST regions could not be! – rupweb Jul 04 '18 at 13:06
0

Per programming your application to use PDX instances a region can hold either objects or PDX wrapped objects. For the former, casting through object is working. So I needed to write:

object o = ev.NewValue;
if (o.GetType().Name.Equals("PdxInstanceImpl")) 
{
  PdxInstance myPdx = (PdxInstance)o;
  MyObject m = (MyObject)myPdx.GetObject();

  string s = m.MyString;
  decimal d = m.MyDecimal;
}
else
{
  MyObject m = (MyObject)o;

  string s = m.MyString;
  decimal d = m.MyDecimal;
}
rupweb
  • 3,052
  • 1
  • 30
  • 57