[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework.remoting

I'm confused on how to deploy remoting efficiently...

mdb

10/27/2004 5:25:00 PM

I want to write a program that provides its user interface only thru
remoting (ie you have a Windows service that does the work, and an admin
program that controls it.)

But I can't see how I can write the admin program in such a way that I
don't distribute the Windows Service assembly along with it.

What I have so far is:

- a project that defines an interface describing the commands that I
want available to the admin program ("work interface")
- a project that a class containing all the functionality to do the work
("work class")
- a project that implements the windows service doing the work ("windows
service"). The windows service simply creates an object from the class
above and allows it to do its thing.

So I have a couple choices:

1. I can make the work class a MarshalByRefObj, allowing direct access
from the admin program and to the actual functions that do the work.
This means that the admin program would require access to the work class
assembly, which is what I want to avoid.

2. I can create another separate (new class) object MarshalByRefObj,
but then I have to pass the commands up to the actual work class. If I
use a direct object reference, I'm in the same boat - the admin program
would end up needing the work class assembly. I could use events or
delegates to pass the messages up, but then the admin program will see a
bunch of events/delegates that it will never use - again not a great
idea.

See where I'm going with this?? Ultimately what I want is an assembly
that can be referenced and used by the admin program without requiring a
reference to the work assembly, and which doesn't expose a bunch of
unnecessary stuff...

What am I missing?? Thanks for all replies!

-mdb
8 Answers

Ken Kolda

10/27/2004 5:42:00 PM

0

You're on the right track with the different assemblies. You're also correct
that your work class should derive from MarshalByRefObject. Once that's
done, you should not need the work class's assembly on the client. That's
because the client will interact with the work class solely through the
"work interface" you've defined.

You asserted that you would need to have the work class assembly on the
client -- what makes you think this is the case? The only shared assembly
should be the work interface assembly assuming you've got your client coded
correctly (i.e. it never references the work class, only teh work
interface).

Ken



"mdb" <m_b_r_a_y@c_t_i_u_s_a__d0t__com> wrote in message
news:Xns958F886CB2BC7mbrayctiusacom@207.46.248.16...
> I want to write a program that provides its user interface only thru
> remoting (ie you have a Windows service that does the work, and an admin
> program that controls it.)
>
> But I can't see how I can write the admin program in such a way that I
> don't distribute the Windows Service assembly along with it.
>
> What I have so far is:
>
> - a project that defines an interface describing the commands that I
> want available to the admin program ("work interface")
> - a project that a class containing all the functionality to do the work
> ("work class")
> - a project that implements the windows service doing the work ("windows
> service"). The windows service simply creates an object from the class
> above and allows it to do its thing.
>
> So I have a couple choices:
>
> 1. I can make the work class a MarshalByRefObj, allowing direct access
> from the admin program and to the actual functions that do the work.
> This means that the admin program would require access to the work class
> assembly, which is what I want to avoid.
>
> 2. I can create another separate (new class) object MarshalByRefObj,
> but then I have to pass the commands up to the actual work class. If I
> use a direct object reference, I'm in the same boat - the admin program
> would end up needing the work class assembly. I could use events or
> delegates to pass the messages up, but then the admin program will see a
> bunch of events/delegates that it will never use - again not a great
> idea.
>
> See where I'm going with this?? Ultimately what I want is an assembly
> that can be referenced and used by the admin program without requiring a
> reference to the work assembly, and which doesn't expose a bunch of
> unnecessary stuff...
>
> What am I missing?? Thanks for all replies!
>
> -mdb


mdb

10/27/2004 6:26:00 PM

0

"Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
news:#TU#wxEvEHA.1824@TK2MSFTNGP10.phx.gbl:

>
> You asserted that you would need to have the work class assembly on
> the client -- what makes you think this is the case? The only shared
> assembly should be the work interface assembly assuming you've got
> your client coded correctly (i.e. it never references the work class,
> only teh work interface).
>

In order to create the work class object on the client, I need to
'WorkClass wc = new WorkClass()'. Also, I need to be able to do
'RemotingConfiguration.RegisterWellKnownClientType(typeof(WorkClass)...)'
so that when I 'new WorkClass()' it knows where to get it from. You can't
create instances of interfaces, so I can't 'new IWorkClass()'.

I know I must be missing something... I guess one assumption I have made
is that I have to RegisterWellKnownClientType on the actual type of the
class - is this not correct? Can I RegisterWellKnownClientType on the
interface??

-mdb

Robert Jordan

10/27/2004 6:46:00 PM

0

Hi,

> In order to create the work class object on the client, I need to
> 'WorkClass wc = new WorkClass()'. Also, I need to be able to do
> 'RemotingConfiguration.RegisterWellKnownClientType(typeof(WorkClass)...)'
> so that when I 'new WorkClass()' it knows where to get it from. You can't
> create instances of interfaces, so I can't 'new IWorkClass()'.
>
> I know I must be missing something... I guess one assumption I have made
> is that I have to RegisterWellKnownClientType on the actual type of the
> class - is this not correct? Can I RegisterWellKnownClientType on the
> interface??

You don't need to register a wellknown type. Just instantiate the
type using the shared interface:

IShared server = (IShared) RemotingServices.Connect(
typeof(IShared), "tcp://host:port/uri.rem");

bye
Rob

mdb

10/27/2004 8:57:00 PM

0

"Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
news:#TU#wxEvEHA.1824@TK2MSFTNGP10.phx.gbl:

> You asserted that you would need to have the work class assembly on
> the client -- what makes you think this is the case? The only shared
> assembly should be the work interface assembly assuming you've got
> your client coded correctly (i.e. it never references the work class,
> only teh work interface).

OK so I've seen how I don't have to distribute the server assembly to
the client... I have a project defining the remoting interface, and I
have a server project (windows service) that registers the type, and I
have a client project that connects by using Activator.GetObject(...) on
the interface type. Now I have another problem...

When I try to subscribe to an event by running the client on a box other
than the server, I get the "customErrors" issue that is well known with
..NET 1.1 (accepted). When I run the client on the same machine as the
server, I get the exception that was serialized back to the client
saying that the server couldn't find the assembly for my client!! Why
would the server need this??

I'm not doing anything particularly wierd, just trying to subscribe to
an event... its about like this...

public interface XInterface
{
event delHandler Y;
bool Initalize();
}
public delegate void delHandler();

public ServerObject : MarshalByRefObject, XInterface
{
public event delHandler Y = null;
}

public ClientObject
{
private ConnectToServer()
{
...
XInterface x = Activator.GetObject(...)
x.Initialize();
x.Y += new delHandler(this.HandleEvent);
...
}

private void HandleEvent()
{
...
}
}

the x.Initialize function call works... I see it work on my server.
When I try to subscribe to x.Y, that's where it says it can't find the
client assembly.

I have configured the TcpChannel on both the server and client with
specific port numbers (in this case, 48100 on the server and 8008 on the
client), and set the TypeFilterLevel to Full on both the server and the
client, although I'm not sure I need it on the client.

I know I can get thru this if I can just get over these wierd bumps!

-mdb

Ken Kolda

10/27/2004 9:33:00 PM

0

When you subscribe to a server event, you are essentially making the client
the server and vice versa. So, the same rules that govern when the client
needs the server assembly now govern whether the server needs the client
assembly.

When you subscribe to an event, you are passing a delegate from the client
to the server. Inside the delegate it holds two pieces of information:

1) A "target" object, which is a reference to the object instance on which
the callback lives
2) A "method" object which represent the method to be called.

So, the key point is that the delegate holds a reference to your client
object. Also, delegates are serializable types. So, when you pass the
delegate to the server, it gets serialized and deserialized. When the
deserialization takes place on the server, it goes to deserialize the
"target" object. To do that, it needs the assembly in which tha object
lives.

The problem with events is that they're not interface-driven -- the entire
target objects is referenced in the delegate. To work around this, you have
two main choices:

1) Don't use events. Instead, use straight callbacks and interfaces. For
example, instead of exposing an event named "Y", put a method on your server
called "SubscribeToY(IYListener)". Then have your client implement
IYListener and put the definition of this interface in your common assembly.
When the event is to be raised, the server invokes the method on the
IYListener interface for each subscribed object.

2) Use an event wrapper object. This is a simple object that is MarshalByRef
the whole purpose of which is to forward events to another object. For
example:

public class EventWrapper : MarshalByRefObject
{
public delHandler wrappedDelegate;
public EventWrapper(delWrapper wrappedDelegate)
{
this.wrappedDelegate = wrappedDelegate;
}
public delHandler EventHandler
{
get { return new delHandler(this.onEvent); }
}
private void onEvent()
{
wrappedDelegate();
}
}

The above class would go into your common assembly that's shared by client
and server. Now, to subscribe to a server event looks like:

XInterface x = Activator.GetObject(...)
x.Initialize();
x.Y += new EventWrapper(new delHandler(this.HandleEvent)).EventHandler;

Now, when the event gets raised on the server, the EventWrapper handles
transporting the event back to the client. It then passes the event on to
the ClientObject.

Hope that helps -
Ken


"mdb" <m_b_r_a_y@c_t_i_u_s_a__d0t__com> wrote in message
news:Xns958FAC72B961Fmbrayctiusacom@207.46.248.16...
> "Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
> news:#TU#wxEvEHA.1824@TK2MSFTNGP10.phx.gbl:
>
> > You asserted that you would need to have the work class assembly on
> > the client -- what makes you think this is the case? The only shared
> > assembly should be the work interface assembly assuming you've got
> > your client coded correctly (i.e. it never references the work class,
> > only teh work interface).
>
> OK so I've seen how I don't have to distribute the server assembly to
> the client... I have a project defining the remoting interface, and I
> have a server project (windows service) that registers the type, and I
> have a client project that connects by using Activator.GetObject(...) on
> the interface type. Now I have another problem...
>
> When I try to subscribe to an event by running the client on a box other
> than the server, I get the "customErrors" issue that is well known with
> .NET 1.1 (accepted). When I run the client on the same machine as the
> server, I get the exception that was serialized back to the client
> saying that the server couldn't find the assembly for my client!! Why
> would the server need this??
>
> I'm not doing anything particularly wierd, just trying to subscribe to
> an event... its about like this...
>
> public interface XInterface
> {
> event delHandler Y;
> bool Initalize();
> }
> public delegate void delHandler();
>
> public ServerObject : MarshalByRefObject, XInterface
> {
> public event delHandler Y = null;
> }
>
> public ClientObject
> {
> private ConnectToServer()
> {
> ...
> XInterface x = Activator.GetObject(...)
> x.Initialize();
> x.Y += new delHandler(this.HandleEvent);
> ...
> }
>
> private void HandleEvent()
> {
> ...
> }
> }
>
> the x.Initialize function call works... I see it work on my server.
> When I try to subscribe to x.Y, that's where it says it can't find the
> client assembly.
>
> I have configured the TcpChannel on both the server and client with
> specific port numbers (in this case, 48100 on the server and 8008 on the
> client), and set the TypeFilterLevel to Full on both the server and the
> client, although I'm not sure I need it on the client.
>
> I know I can get thru this if I can just get over these wierd bumps!
>
> -mdb


mdb

10/27/2004 9:51:00 PM

0

"Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
news:#CMl6yGvEHA.940@TK2MSFTNGP14.phx.gbl:

> 1) Don't use events. Instead, use straight callbacks and interfaces.
> For example, instead of exposing an event named "Y", put a method on
> your server called "SubscribeToY(IYListener)". Then have your client
> implement IYListener and put the definition of this interface in your
> common assembly. When the event is to be raised, the server invokes
> the method on the IYListener interface for each subscribed object.

I like this approach better... I'm sure I can handle from here, but I
have another question. Is there a built-in way to track clients, or do I
have to do that myself? (I've already done it manually in another project,
so I don't need details of how to do it, but it would be nice if there is a
built-in method.)

-mdb

Ken Kolda

10/27/2004 10:31:00 PM

0

Sorry -- you have to write you own. I use approach #1 as well since I think
it's a lot cleaner and you have the most control.

Ken


"mdb" <m_b_r_a_y@c_t_i_u_s_a__d0t__com> wrote in message
news:Xns958FB5996355Ambrayctiusacom@207.46.248.16...
> "Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
> news:#CMl6yGvEHA.940@TK2MSFTNGP14.phx.gbl:
>
> > 1) Don't use events. Instead, use straight callbacks and interfaces.
> > For example, instead of exposing an event named "Y", put a method on
> > your server called "SubscribeToY(IYListener)". Then have your client
> > implement IYListener and put the definition of this interface in your
> > common assembly. When the event is to be raised, the server invokes
> > the method on the IYListener interface for each subscribed object.
>
> I like this approach better... I'm sure I can handle from here, but I
> have another question. Is there a built-in way to track clients, or do I
> have to do that myself? (I've already done it manually in another
project,
> so I don't need details of how to do it, but it would be nice if there is
a
> built-in method.)
>
> -mdb


Phil Greg

11/3/2004 2:25:00 PM

0

Hi, I tried to implement the callback approach you described, and I'm having
trouble. Here's what I have (in C++):

Common:
public __gc __abstract class BaseClientEvents : public MarshalByRefObject
{
public:
void SubmissionCallback (int sender, String* message)
{
InternalSubmissionCallback (sender, message) ;
}

protected:
virtual void InternalSubmissionCallback (int sender, String* message) = 0;
};

//////////////////////////////////////////////////////////////////////
Client:
public __gc class ClientEvents : public BaseClientEvents
{
public:
void InternalSubmissionCallback (int sender, String* message)
{

}
};

public __gc class Form1 : public System::Windows::Forms::Form
{
public:
IGreeter* greet;
ClientEvents* clientEvents;

private: System::Void RegisterWithServer_Click(System::Object * sender,
System::EventArgs * e)
{
InstanceNumber->Text = String::Concat(S"I am instance number ",
__box(greet->RegisterApp(3,
__try_cast<BaseClientEvents*>(clientEvents)))->ToString());
}
};

////////////////////////////////////////////////////////
Server:
int Greeter::RegisterApp(long lHandle, BaseClientEvents* iClient)
{
/*****/
}

Now when I call the RegisterApp from the client, I keep getting this
exception:
An unhandled exception of type 'System.Runtime.Remoting.RemotingException'
occurred in mscorlib.dll
Additional information: The argument type System.MarshalByRefObject cannot
be converted into parameter type GreeterKnownObjects.BaseClientEvents.

I've tried defining BaseClientEvents as an __abstract or a plain __gc class
but I keep getting the same exception. Anything stupid I might be doing?

Thanks in advance!

Phil










"Ken Kolda" wrote:

> Sorry -- you have to write you own. I use approach #1 as well since I think
> it's a lot cleaner and you have the most control.
>
> Ken
>
>
> "mdb" <m_b_r_a_y@c_t_i_u_s_a__d0t__com> wrote in message
> news:Xns958FB5996355Ambrayctiusacom@207.46.248.16...
> > "Ken Kolda" <ken.kolda@elliemae-nospamplease.com> wrote in
> > news:#CMl6yGvEHA.940@TK2MSFTNGP14.phx.gbl:
> >
> > > 1) Don't use events. Instead, use straight callbacks and interfaces.
> > > For example, instead of exposing an event named "Y", put a method on
> > > your server called "SubscribeToY(IYListener)". Then have your client
> > > implement IYListener and put the definition of this interface in your
> > > common assembly. When the event is to be raised, the server invokes
> > > the method on the IYListener interface for each subscribed object.
> >
> > I like this approach better... I'm sure I can handle from here, but I
> > have another question. Is there a built-in way to track clients, or do I
> > have to do that myself? (I've already done it manually in another
> project,
> > so I don't need details of how to do it, but it would be nice if there is
> a
> > built-in method.)
> >
> > -mdb
>
>
>