Structs must have unique names across multiple services - why?

If a server exposes two services, each defined by a separate RODL file and each located in a separate namespace in the server project, it seems that you can’t define a struct with the same name in both services. If you do, the Intf file for one of the services will throw an InvalidCastException at runtime.

I have a test case containing ServiceA and ServiceB (in NamespaceA and NamespaceB respectively), both of which contain one method, GetPerson, which returns a Person struct. The struct is defined identically in both services. If a client calls ServiceA.GetPerson() then it returns an instance of NamespaceA.Person, as expected. But on calling ServiceB.GetPerson (which should return a NamespaceB.Person) the exception is thrown at this line in GetPerson() in LibraryB_Intf.cs:

Person Result = ((Person)(@__LocalMessage.Read("Result", typeof(Person), RemObjects.SDK.StreamingFormat.Default)));

The exception message is “Unable to cast object of type ‘Server.NamespaceA.Person’ to type ‘Server.NamespaceB.Person’”. But if you hover the mouse over the ‘Person’ types in this line, the tooltip shows that the compiler knows that ‘Person’ is a NamespaceB.Person. There is no mention of a NamespaceA.Person in this line (or file, or namespace), so why is a casting attempt being made at runtime?

Stepping into the RO source, we find the answer in the ReadComplex method in BinSerializer.cs (at least in the case of binmessages). In the call to ReadComplex, the name parameter is “Result” and the type parameter is Server.NamespaceB.Person.

        public override ComplexType ReadComplex(String name, Type type)
        {
            [...]
            ComplexType lResult = null;

            String lClassName = this.ReadAnsiString();
            [...]
            lResult = (ComplexType)TypeManager.CreateInstanceOfType(lClassName);

            lResult.ReadComplex(this);
            return lResult;
        }

The call to ReadAnsiString returns “Person” so the TypeManager goes off and looks for a type called “Person” in all the loaded assemblies in the current app domain. Unfortunately, the first one it finds is Server.NamespaceA.Person. On returning, an instance of this type is created and then cast to a NamespaceB.Person in LibraryB_Intf.cs, resulting in the exception.

There’s probably a good reason why this ‘type lookup’ is done using only the short name of the type (“Person”) rather than the fully qualified name (“Server.NamespaceB.Person”), but given that the full type is being passed to ReadComplex(), could it not also be passed to TypeManager.CreateInstanceOfType? Then the assemblies could be searched for a type that matches the fully qualified name, rather than just the short name that is returned from ReadAnsiString. Unless I’m missing something, this would prevent the lookup from returning the wrong type, it would avoid the invalid cast, and we’d be able to use the same struct definition in multiple services in the same server.

At the moment, I have to use unique names for all my otherwise-identical structs and then map them to a single type defined at a higher level. Not a showstopper, but an added complication that would appear to be unnecessary.

I will email my test case to the support address.

I’m afraid this is as designed. Not all platforms supported by RO have namespaces, so each type thats marshaled need to have a unique name.

See? I told you I was missing something. :smile:

Thanks Marc.

1 Like

Has there been any change to this restriction?

We ran into a problem where this has become critical for our current use of Remobjects.

Thanks, TK

Maybe my previous post could be cleared up if someone could give me a good pattern/explanation on how to handle API versioning.

Our client application is distributed through the Apple Store which means we don’t have direct control over when client devices will be updated. Companies using our solutions install/maintain locally (on prim) our RemObjects SDK server but refuse to install multiple versions due to various reasons.

Long story short, we will have some IOS devices running our latest client app while others will be running older versions of the client app perhaps indefinitely (user can’t update because he/she owns an older IOS device), but all needing to run against a single on prim RO Server.

How is the above handled within the RO SDK? If I change a RO Complex type, older versions of the client app break as the data sent/received is unexpected. Creating a new types (type inheritance could be used) requires modifying all of our code in client apps to use the new type. This also requires all methods in the RO Interface that used the old types to be re-created with a different name. Again, this is required as there will be old versions of the client app running against this new updated server.

I have purposed to our IT department that we use a reverse proxy to route traffic to different RO Servers based on the url endpoint used by the client app. This works but again IT has requested we come up with a solution that doesn’t require this added complexity as Companies using our solutions look at this as a pore design and demand a better solution.

My solution before finding out the RO didn’t support “namespaces” was to create a copy of the RO rodl file, rename the “library” and contained services. Once renamed I was able to make any changes desired to types and methods. The RO Server would host/server multiple rodl interfaces/implementations. The only change in the client code was to use the new service name which is a single line of code. This works beautifully until the RO SDK looks up the “type” be sent over the wire and finds the wrong one since it’s not finding the complex type based on the “library” the client app is using.

Any help/insight will be greatly appreciated. Thanks, TK

1 Like

Hi.

i’m afraid we don’t currently have any automated mechanisms for versioning built into the core RO APIs. This is actually something we are looking into for an 9.x release later this year, but right now, you will have to handle version manually; i propose something along the following lines:

(a) have a API call, say on your LoginService, where client and server can exchange their expected version number and adjust their behavior accordingly.

(b) don’t change the signature of existing methods on existing services, once published. Adding new methods with a new name is fine, and your clients can negotiate which methods to call based on what server they are talking to (mostly an issue if you expect NEW clients to talk to OLD servers, so this might be a moot issue).

© don’t change the signature of structs. if you need to extend them, create descendants. Your server can decide which version of a struct it can send back by virtue of knowing the client’s API version. Similar for new clients talking to old servers if that’s a concern).

You can save yourself a whole lot of trouble ion you can assure the server is always newer than the client. (i.e., you don’t keep any old v3 servers after v4 of your client app is deployed). If thats the case, you pretty much ONLY have to worry about versioning server side. Old clients will call older APIs, and send older structs by simple virtue of not knowing about the newer ones. The server can fall back to only sending older versions (i.e. ancestors) of structs, when its knows its talking to an older client.

For 9.x, we’re looking into adding built-in mechanism for versioning on RODL level (with some interaction from user code being unavoidable, of course). Unfortunately i cannot give you a timeline yet, not can i got into details for how this will work, exactly, as it’s still in early stages.

Off course once we et past the drawing board, your feedback and experience would be very welcome.

Does that make sense and sound good?

1 Like

Thanks Mark, makes sense.

For this round of modifications I had already made changes along the lines you mentioned. Looking down the road I can see more major changes coming and the outlined approach would with time “fragment” the server code base. Looking forward to your purposed changes as any help from the RO framework will be appreciated.

Thanks, TK

1 Like

Hi Mark.

Did some form of “versioning” make it into RO SDK 9? I didn’t see mention of this in your What’s New in Remoting SDK 9 doc.

I have been moving our server and clients to RO9 and love the async pattern available for Delphi clients.

Thanks again, TK

1 Like

Hi RemObjects Talk,

Sorry to resurrect this old thread, but I seem to be having an identical issue to the OP after 6 years! We have a C# .NET client calling methods on two different Delphi services using the Remoting SDK. We’ve used the Visual Studio plugin to generate client-side code for both, and placed the _Intf.cs within the client project in separate folders and given them unique namespaces.

There is a lot of overlap between the structures and names between the two Delphi services, and we are seeing System.InvalidCastException being thrown where there are instances of a struct with the same name between both services despite them being distinguishable by namespace.

Without modifying our mature and integrated Delphi services, is there a client-side workaround for this?

Many thanks,
Dave

Hi,

You can have reduced RODL for each Delphi service.

as a result, it will allow to generate personal _Intf for each service and use personal namespaces also.

read Service Group feature (Delphi only) for more details about ServiceGroups.

example: Service1 uses unit1.mystruct and Service2 uses unit2.mystruct

if you assign group1 to Service1 and group2 to Service2, and put links to .remoteRODL files:

VS will generate reduced _Intf too. as a result, methods of Service1 and Service2 should be called w/o any issues.

Hi there,

Does this mean we would have to effectively rename mystruct to unit1.mystruct for each conflicting name, regenerate the server-side interface files and rebuild the services, or can we do this without rebuilding them?

I’m not clear either on how creating a reduced RODL for each client would help, since surely renaming the structs to include namespaces (or some other difference) would resolve the problem without grouping the services? Really we would like our client to be able to access all methods on each service without seeing a restricted set.

I was hoping for a a solution requiring only client-side changes, since the number of conflicting names will be large, and we don’t want to introduce risk or changes to the operation of other clients already using these services.

Just to be clear, the exceptions we are seeing are on the C# client side, not the Delphi server side.

Thanks,
Dave

Hi,

renaming of structs is option, but it takes a lot of efforts. try to add ServiceGroup attribute.
this is cheap change that doesn’t affect to other clients or server-side at all
however it will allow to generate reduced RODL.

animation

Hi again, and happy new year!

I updated one of our Delphi services to use a servicegroup, and I was able to see that including the service group as a query parameter in the URL filtered the XML as expected in a browser.

I then tried adding the query to the .remoteRODL file as per your suggestion below.

if you assign group1 to Service1 and group2 to Service2, and put links to .remoteRODL files:

I found that the file generated was identical, regardless of whether I included the servicegroup query parameter or not, which presumably is unexpected! Any ideas why this might be?

I’m using version 10.0.0.1555 of the Visual Studio extensions.

Hi,

Can you create a simple testcase that reproduced this issue, pls?
You can take your project and remove business logic.
You can drop it to support@ for keeping privacy

Thank you, I have just sent an email containing the test setup to support.