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.