[c#] Remoting: Probleem met member variables geremote class

Pagina: 1
Acties:
  • 281 views sinds 30-01-2008
  • Reageer

  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
We zijn er weer eens met een vraag... Ik heb zopas een client/server applicatie geschreven met sockets maar zou nu wel eens met Remoting willen experimenteren omdat dat voor bepaalde toepassingen veel interessanter is dan sockets en de hele rompslomp die daarbij komt kijken. Dankzij enkele uitstekende tutorials op codeproject duurde het natuurlijk niet lang eer ik Remoting aan de praat kreeg. Zolang ik het bij losse functies hield was er geen probleem, maar toen ik een volledige class wou remoten stootte ik op een lastig probleem. Waarschijnlijk 'by design', maar ik had toch graag geweten waarom het zo is en hoe ik het kan omzeilen.

Even een heel simpel voorbeeldje om het probleem te schetsen:

Volgende class in de serverapplicatie wordt het object dat ik via Remoting beschikbaar wil maken voor de clientapplicatie:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Object : MarshalByRefObject,MyInterface
{
    // Variabelen
    private bool bTest = false;

    // Constructor
    public Object()
    {
    }

    // Destructor
    ~Object()
    {
    }

    // Lees bTest
    public bool Lees()
    {
        return bTest;
    }

    // Zet bTest
    public void Zet(bool b)
    {
        bTest = b;
    }
}
De gebruikte interface voor de clientapplicatie is:
C#:
1
2
3
4
5
public interface MyInterface
{
    bool Lees();
    void Zet(bool b)
}
De werking is vrij straight-forward. Met functie Lees() lees ik de huidige waarde van boolean bTest uit en met Zet() kan ik de waarde ervan wijzigen. De serverapplicatie start het remotingproces met volgende code:
C#:
1
2
3
4
5
6
7
// SERVER
TcpChannel tcpChannel = new TcpChannel(4848);
ChannelServices.RegisterChannel(tcpChannel);
RemotingConfiguration.RegisterWellKnownServiceType(
    Type.GetType("Object"),
    "RemoteObject",
    WellKnownObjectMode.SingleCall);


Wat gaat er nu mis in de clientapplicatie (vereenvoudigde code)?
C#:
1
2
3
4
5
6
7
8
9
10
// CLIENT
TcpChannel tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel);
MyInterface myRemoteObject = 
    (MyInterface)Activator.GetObject(typeof(MyInterface),
    "tcp://192.168.1.10:4848/RemoteObject");
..
myRemoteObject.Lees();      // Geeft false terug
myRemoteObject.Zet(true);
myRemoteObject.Lees();      // Geeft false terug!!!
Ik had verwacht dat die laatste regel 'true' zou teruggeven. Maar blijkbaar kan de geremote class zijn member variabelen niet veranderen. Binnen een functie geen enkel probleem, maar erbuiten wil het niet werken.

Kan iemand me uitleggen waarom dit zo is, en hoe ik dit kan verhelpen? Ik zou graag de class volledig gebruiken in m'n client applicatie, maar bepaalde waarden moeten door de serverapplicatie ingevuld worden. Dat laatste is nu al mogelijk, maar ik kan de class gewoon niet deftig werkende krijgen in de clientapplicatie door bovenstaand probleem...

[ Voor 18% gewijzigd door mahi op 13-09-2005 12:00 ]

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • whoami
  • Registratie: December 2000
  • Laatst online: 15:31
Object is de 'remoted' class, de MarshalByRefObject class dus ?
Het is geen class die je in je client terugkrijgt van een remoted (marshalbyref) class ?

Is Object client activated of server activated ?

[ Voor 15% gewijzigd door whoami op 13-09-2005 11:36 ]

https://fgheysels.github.io/


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
Ik heb duidelijk niet voldoende informatie in de topic start gezet. Excuses daarvoor. Ik heb ondertussen de codevoorbeelden aangevuld zodat de situatie duidelijk zou moeten zijn.

Object is idd de MarshalByRefObject class (ik was dat cruciale stukje vergeten in m'n codevoorbeeld). Het gaat om een server activated object (Activator.GetObject).

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • whoami
  • Registratie: December 2000
  • Laatst online: 15:31
Jouw probleem wordt veroorzaakt omdat jouw 'remoted object' state bijhoudt.

Aangezien jouw remote object een single-call object is, verliest deze iedere keer z'n state. Bij een single-call object wordt er nl. iedere keer dat je een request doet naar zo'n object, een nieuw aangemaakt. (De naam zegt het eigenlijk al zelf):
SingleCall types always have one instance per client request. The next method invocation will be serviced by a different server instance, even if the previous instance has not yet been recycled by the system. SingleCall types do not participate in the lifetime lease system.
There are two modes in which server-activated objects can be activated:

* Singleton
Only one object will be created on the server to fulfill the requests of all the clients; that means the object is shared, and the state will be shared by all the clients.

* SingleCall
Such objects are created on each method call and objects are not shared among clients. State should not be maintained in such objects because such objects are destroyed after each method call.
Je kan je probleem oplossen door gebruik te gaan maken van client-activated types denk ik. Je zult dan echter wel zelf de 'liftetime' van die objecten moeten gaan verzorgen.


Wees jezelf er ook van bewust, dat, iedere keer je een method-call doet op een remote object, er dus een netwerk-call gedaan wordt. Zorg er dus voor dat je zoweinig mogelijk calls moet doen.
Liever alles in één keer ophalen / doen, dan 10x een call doen om een property oid te zetten.

[ Voor 50% gewijzigd door whoami op 13-09-2005 12:20 ]

https://fgheysels.github.io/


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
Nu ik het allemaal nog eens nalees is het inderdaad duidelijk. Ik heb SingleCall verkeerd geïnterpreteerd. Ik dacht dat dit een object voor iedere client aanmaakte, maar had duidelijk over het hoofd gezien dat per method call een nieuwe instantie werd aangemaakt. Ik verkeerde in de veronderstelling dat dit enkel gebeurde bij het initializeren van het object.

Dus ik heb inderdaad client activated remoting nodig in mijn situatie. Jammer genoeg is het aantal tutorials hierover heel wat dunner gezaaid en gaat m'n c# boek ook niet verder dan het vermelden dat dit ook bestaat naast server activated remoting... Typisch :)

Hoe dan ook, ik ben weer aan het coden geslagen en met codeproject's ".NET Remoting – Basic Maneuvers" tutorial kom ik al een heel eind verder. Maar in tegenstelling tot server activated remoting krijg ik de client activated remoting niet aan de praat...

Laten we even hetzelfde object en interface beschouwen als uit m'n topic start. De serverkant gaat er dan als volgt uit zien:
C#:
1
2
3
4
5
// SERVER
TcpChannel tcpChannel = new TcpChannel(4848);
ChannelServices.RegisterChannel(tcpChannel);
RemotingConfiguration.RegisterActivatedServiceType(
    Type.GetType("Object"));
Wat me hieraan meteen al opvalt is de afwezigheid van de object uri. Hoe moet de client dan de server vinden?

Ik denk wel dat de server correct is, maar het is de client waar ik hopeloos de mist in ga...
C#:
1
2
3
4
5
6
// CLIENT
object[] attributes = { new System.Runtime.Remoting.Activation.UrlAttribute(
    "tcp://192.168.1.10:4848/MainForm") };
ObjectHandle handle = (ObjectHandle)Activator.CreateInstance(
    "Object", "Object", attributes);
MyInterface myRemoteObject = (MyInterface)handle.Unwrap();
Dit geeft telkens onderstaande foutmelding op regel 4 uit het voorbeeld:
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll

Additional information: ?
Deze zeer uitgebreide foutmelding laat me wat radeloos. Welk bestand ontbreekt er? Kan ie de uri niet openen? Vind ie het object niet aan de server zijde? ...

Ik heb trouwens nog wat vragen over de client zijde.

De uri in de client is "tcp://192.168.1.10:4848/MainForm". MainForm is hierbij de class aan de server kant waarin de server gestart wordt. Ik heb dit gewoon naar analogie van codeproject's tutorial overgenomen. Maar klopt dit ook? Moet de uri specifiek die class bevatten? Of moet de naam iets anders zijn? Bij de server activated remoting kon je zelf een naam opgeven wat de situatie meteen veel duidelijker maakte.

De parameters van CreateInstance zijn (string assemblyName, string typeName, object[] activationAttributes). Die laatste is duidelijk bovenstaand attribuut, maar de twee eersten worden nergens duidelijk uitgelegd. Slaat assemblyName op de interface MyInterface aan de client kant, of het object Object aan de server kant? Hetzelfde voor typeName. Wanneer ik regel 4 naar hetvolgende verander
C#:
1
2
ObjectHandle handle = (ObjectHandle)Activator.CreateInstance(
    Type.GetType("MyInterface"), attributes);
dan krijg ik als foutmelding
An unhandled exception of type 'System.MissingMethodException' occurred in mscorlib.dll

Additional information: Constructor on type MyInterface not found.
Geen constructor gevonden... Maar een interface kan geen constructor bevatten.

Ik zou natuurlijk de class Object eveneens in de client kunnen onderbrengen (en MyInterface dan wijzigen in Object), maar dat lijkt me nogal idioot. Dan gaat het hele voordeel van zulk een interface verloren. Ik wil Object helemaal niet in de client compileren. Daarvoor dient de interface.

Met andere woorden, ik zit weer muurvast :P

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • whoami
  • Registratie: December 2000
  • Laatst online: 15:31
Wat is ObjectHandle ?
Lukt het niet als je 't zo doet:
code:
1
2
3
object[] url = new UrlAttribute ("tcp://.....");

MyInterface bliep = (MyInterface)Activator.CreateInstance (typeof(MyInterface),  null, url);


Heeft je object ook een default constructor ?

[ Voor 11% gewijzigd door whoami op 13-09-2005 15:15 ]

https://fgheysels.github.io/


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
ObjectHandle of System.Runtime.Remoting.ObjectHandle: The ObjectHandle class is used to pass an object (in a wrapped state) between multiple application domains without loading the metadata for the wrapped object in each AppDomain through which the ObjectHandle travels. Thus, the ObjectHandle class gives the caller control of when the Type of the remote object is loaded into a domain.

Ik gebruik dat ook maar omdat de voorbeelden dat ook doen :)
Lukt het niet als je 't zo doet:
[...]
Heeft je object ook een default constructor ?
Ik heb je voorbeeldje even aangepast naar mijn codevoorbeeld:
C#:
1
2
3
4
object[] attributes = { new System.Runtime.Remoting.Activation.UrlAttribute( 
    "tcp://192.168.1.10:4848/MainForm") };
MyInterface myRemoteObject = (MyInterface)Activator.CreateInstance (
    typeof(MyInterface), null, attributes);
Helaas werkt ook dit niet. De foutmelding is ditmaal:
An unhandled exception of type 'System.NotSupportedException' occurred in mscorlib.dll

Additional information: Activation Attributes are not supported for types not deriving from MarshalByRefObject.
Roze olifanten vliegen hier voorbij... Ahum ;). We zijn er dus nog niet.

Je laatste vraag begrijp ik niet helemaal. Of mijn object een default constructor heeft? Is diegene die er nu staat (zie Object voorbeeldcode in topic start) dan geen goede? De Constructor foutmelding lijkt volgens mij ook niet voort te vloeien uit Object, maar uit MyInterface. Maar interfaces kunnen geen constructors en dergelijke bevatten.

Heb je trouwens m'n vraag over MainForm in "tcp://192.168.1.10:4848/MainForm" gelezen (vorig bericht). Klopt dat wel?

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


Verwijderd

Een opmerking vraag: zou je de URL's naar de gelezen artikels erbij willen plaatsen? Dat kan handig zijn voor mensen met vragen over dit onderwerp en om je te helpen met je oplossing ... :)

[ Voor 6% gewijzigd door Verwijderd op 13-09-2005 15:43 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 15:31
Ik denk dat die 'MainForm' die je in de Uri specifieert niet goed is; dat moet de naam van de applicatie zijn; niet de naam van een class.
althans, dat is wat ik uitmaak uit de MSDN. Met client side activation heb ik niet echt ervaring.
Ik zou er thuis eens een boek moeten op naslaan.

https://fgheysels.github.io/


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
Verwijderd schreef op dinsdag 13 september 2005 @ 15:39:
Een opmerking vraag: zou je de URL's naar de gelezen artikels erbij willen plaatsen? Dat kan handig zijn voor mensen met vragen over dit onderwerp en om je te helpen met je oplossing ... :)
Mmmh, ik was weer eens wat lui :)

- .NET Remoting & Basic Maneuvers
- Simple but potentially useful example of .NET Remoting
- Applying Observer Pattern in .NET Remoting
- .Net Remoting - Part2 Object Activation, Lifetime And Configuration
- Distributed Computing using .NET Remoting
- A Simple But Useful Example of .NET Remoting Part 2 (net gevonden - deze geeft veel uitleg, ben ik mee bezig)
whoami schreef op dinsdag 13 september 2005 @ 15:45:
Ik denk dat die 'MainForm' die je in de Uri specifieert niet goed is; dat moet de naam van de applicatie zijn; niet de naam van een class.
Je hebt gelijk. In een code voorbeeld dat ik net vond (3de link van hierboven) wordt in de server de naam gezet door middel van
C#:
1
RemotingConfiguration.ApplicationName = "Naam";
en dien je die "Naam" dan ook weer te gebruiken in de url in de client. Maar dat brengt me voorlopig geen stap verder. Dezelfde foutmelding blijft. Maar de net gevonden tutorial gaat wel wat dieper op alles in. Hopelijk kom ik met deze tutorial wel tot een goed resultaat.

Ik hou jullie op de hoogte!

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
Net op het punt dat ik radeloos begon te worden krijg ik het handeltje alsnog aan de praat! Maar nog niet helemaal in de stijl dat ik het wens te hebben.

Het draait natuurlijk allemaal om volgende regel in de client code:
C#:
1
2
ObjectHandle handle = (ObjectHandle)Activator.CreateInstance( 
    "Object", "Object", attributes);
De parameters van CreateInstance zijn (string assemblyName, string typeName, object[] activationAttributes). De eerste parameter duidt op de assembly naam van het Object - zonder extensie (dus als de assembly test.exe of test.dll noemt, dan wordt de assembly naam gewoon test). De tweede parameter is het type van het Object in de stijl van namespace.ClassNaam. Het gaat hier wel degelijk om het object in de server en niet de interface!

Maar zelfs na dit correct aangepast te hebben bleef ik dezelfde foutmelding krijgen. Even logisch nadenken leert ons dat de client de namespace.ClassNaam helemaal niet kan kennen zonder reference. Inderdaad, hoe idioot het ook moge klinken, maar je moet de server als reference opgeven bij de client - Puur en alleen voor dit object type. Ik ben nog steeds niet 100% zeker of dit absoluut een vereiste is, maar na een nauwgezette analyse van een paar gecompileerde voorbeeldprojecten valt me op dat de clients steeds ook de server meenemen.

Maar dat is natuurlijk helemaal niet wat ik wou... Ik wil niet dat de hele serverapplicatie bij m'n clients terecht komt. Dat zou imo een beetje absurd zijn. Oplossing? Zowel de interface als het remote object in een aparte dll compileren en de server & client elks naar die dll laten refereren. Dan zit nog steeds wel het hele remote object in de client, maar toch alvast niet meer de hele serverapplicatie. Waarom het me niet lukt zonder het remote object in de client is me niet geheel duidelijk. Bij server activated remoting werkt het namelijk wel zonder dit te doen. Enkel de interface is daar voldoende voor de client.

Maar dit schept een nieuw probleem... Nu is het remote object een los onderdeel geworden. Dus eigenlijk gaat de client via de server het object benaderen, maar het object is geen onderdeel van de server meer. Dus er is niet echt meer sprake van een server/client applicatie want buiten het object serven heeft de server geen controle meer over het object. En mijn idee was net dat clients via het object informatie van de server zouden kunnen opvragen... Ik kan natuurlijk de server referencen in het object, maar dan kom ik in een eindeloos straatje...

Ik zit dus gewoon vast aan het feit dat ik niet weet hoe ik het object uit de client weghoud en enkel de interface gebruik in de client. Ideaal is dat het object gewoon in de server blijft zoals bij server activated remoting, maar dat lukt me niet...

Remoting experts hier?

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • whoami
  • Registratie: December 2000
  • Laatst online: 15:31
Zoals ik al eerder zei, heb ik met client activation remoting niet echt ervaring; echter, misschien vind je wel iets op de site van Ingo Rammer

https://fgheysels.github.io/


  • Rhapsody
  • Registratie: Oktober 2002
  • Laatst online: 18:24

Rhapsody

In Metal We Trust

Voorzichtige kick..

Ik heb hetzelfde probleem hier.
Het ziet er inderdaad naar uit dat wanneer er gebruik gemaakt wordt van CAO's dat de client dan al een reference moet hebben naar het remote object zelf en niet naar de client.

Wat een mogelijke oplossing kan zijn, is het maken van een extra dll die vergelijkbaar is met de interface van het remote object. Enkel de methoden en attributen zonder de implementaties. (in principe dus een dll die zich gedraagt als een interface terwijl het gewoon een dll is. )

Met dat in het achterhoofd krijg je dus in de nieuwe situatie:
- Server
- Client
- Remote Object A
- Interface voor Remote Object A (deze heb je in principe niet meer nodig dan als ik het goed heb)
- 'lege versie' van Remote Object A (identiek aan Remote Object A, alleen geen implementaties van methoden e.d.)

Zijn er mensen die dit toevallig al getest hebben? Op dit moment ben ik er mee bezig, maar mochten er al mensen zijn die hier al ervaring mee hebben dan is dat erg handig om mee te nemen in mijn onderzoek.

Edit:
Op http://www.dotnet247.com/247reference/msgs/12/62960.aspx is er iemand met hetzelfde probleem, en daar wordt eigenlijk ook in uitgelegd hoe of wat:
One important point to note: Using client-activation, you cannot create an
object using only an interface specification. This limitation is simply
because you cannot instantiate an interface whether or not you are using
remoting. You get around this limitation when using server-activation
because the server knows about an actual class to instantiate.

The work-around is to create an assembly based on your server assembly and
delete all the implementation details in each method, etc. You compile this
stubbed assembly and reference it in your client app so the client app knows
what the remote object looks like. The soapsuds utility will do this for
you if you are using SOAP serialization. If you are not using SOAP, you
will have to do this yourself.
Edit 2:
Voor de anderen met hetzelfde probleem. Heb het nu voor elkaar. Heb de client een referentie gegeven naar het remote object.
code:
1
lokaleReferentie = CType(Activator.CreateInstance(GetType(RemoteObject)), RemoteObject)

[ Voor 40% gewijzigd door Rhapsody op 22-09-2005 13:17 . Reden: CAO aan de gang ]

🇪🇺 pro Europa!


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
Ik heb het zelf maar opgegeven... Op de site van Ingo Rammer (waar whoami naar verwijst) wordt CAO omschreven als ten stelligste af te raden. Er kleven nogal wat nadelen aan vast. Jammer :)

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.


  • Rhapsody
  • Registratie: Oktober 2002
  • Laatst online: 18:24

Rhapsody

In Metal We Trust

mahi schreef op dinsdag 27 september 2005 @ 09:03:
Ik heb het zelf maar opgegeven... Op de site van Ingo Rammer (waar whoami naar verwijst) wordt CAO omschreven als ten stelligste af te raden. Er kleven nogal wat nadelen aan vast. Jammer :)
Ik zal eens even kijken op die site, maar er zijn gevallen waarbij CAO's gewoon noodzakelijk zijn. Dat is imho de enige manier om een prive-instantie per client te realiseren welke zo lang mogelijk blijft leven als de client wil.

edit: kun je mij een url geven waar ze dat afraden?

[ Voor 6% gewijzigd door Rhapsody op 27-09-2005 11:24 ]

🇪🇺 pro Europa!


  • mahi
  • Registratie: Juni 2001
  • Laatst online: 03-10-2025

mahi

God bless GoT

Topicstarter
.NET Remoting Use Cases and Best Practices - Zoals je in het tabelletje op die pagina kunt zien beïnvloedt CAO de schaalbaarheid in een negatieve zin. Nu is dat natuurlijk niet altijd van belang. Ook onder het puntje "How About Client-Activated Objects?" haalt Ingo wat negatieve punten aan. Zelf zou ik daar allemaal geen last van gehad hebben, maar eerlijk gezegd was ik de CAO zo beu dat ik ze maar links heb laten liggen :P. SAO is zo eenvoudig - ik snap niet waarom CAO zo'n bitch moet zijn.

Lees ook even de "The Nine Rules of Remoting" onderaan de pagina door. Ook daar hamert Ingo erop om geen CAO te gebruiken.

[ Voor 6% gewijzigd door mahi op 27-09-2005 11:52 ]

A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.

Pagina: 1