Re: A tricky one...
- From: "Shailen Sukul" <shane@xxxxxxxxxxxxx>
- Date: Fri, 8 Dec 2006 13:16:49 +1100
I am sorry I lost interest after the first 100 mundane lines. You have to
remember that people on this forum do not have the time to read a point-less
narration. Please be concise and short and you might get more responses.
--
With Regards
Shailen Sukul
..Net Architect
(MCPD: Ent Apps, MCSD.Net MCSD MCAD)
Ashlen Consulting Services
http://www.ashlen.net.au
"GoogleEyeJoe" <crgsmrt@xxxxxxxxxxx> wrote in message
news:1165011542.989306.204980@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Dear all,
This is my first attempt at writing a distributed application in .NET
using the C# language and I've now run into a hurdle that looks to be
a little to high for me to leap over. It is for that reason that I'm
hoping some kind soul may offer me a springboard from which to jump.
Please let me apologise in advance for my 'wordiness' - I am the
least concise, and most verbose, person I know. J
This is what I want to do: Save an object's data to a SQL Server 2000
without writing code that I'll regret later.
I have the following physical system architecture:
A. - 1 server;
B. - Many client machines.
The application I've wrote so far is broken up into the following
tiers:
=========================================================
1. Client Hosting Windows Forms application (sits on one of the many
client machines (item 'B' in the list above))
2. Client object library that is hosted by the above Windows Forms
application (point 1)
=========================================================
3. A Server Hosting Windows Forms application (sits on the 1 server
machine (item 'A' in the list above)
4. Server object library that is hosted by the above (server) Windows
Forms application (point 3).
=========================================================
5. An object library (called ExcelciaBusiness.dll - containing the
declarations of the business objects) that is shared by both the client
and server object libraries - so that both have a reference to the
business object types - a copy held in two physical locations...
=========================================================
A general understanding of how the application works.
A Windows Forms application (ExcelciaHost.exe - item 1 in the list
above) hosts a client assembly (called in this case,
'ExcelciaClient.dll - item 2 in the list above). The hosting
windows application communicates with this assembly for all business
related requests. For example, if the Windows Forms application wants
to get a new business object (declaration of which is stored in the
shared object library - item 5 in the list above) it has to ask the
client assembly (item 2 in list above) to give it one.
Example call
(Inside a button click event (in a Windows form) for
example...)
{
//Code that obtains an instance of a comment
object (for an existing comment), places some test data into the
comment's narrative, and then displays the comment's narrative to
the user by means of a MessageBox.
Comment comment_ = _client.GetComment (commentId);
comment_.narrative = "this is a test comment";
MessageBox.Show(comment_.narrative);
}
When the client receives the request to GetComment(commentId) it,
behind the scenes, then forwards the request (by means of .NET
Remoting) to a remote server assembly (ExcelciaServer.dll - item 4 in
the list above) which is hosted by another Windows Forms application
(ExcelciaServerHost.exe - item 3 in the list above) on a remote machine
(item 'A' in the physical layout above).
So, inside the _client object the following code is executed. Example
does not include exception handling and authentication for purposes of
clarity.
public Comment GetComment(string commentId)
{
return _server.GetComment(commentId,
_ticket.ticketNumber);
}
Note: _ticket.number is the ticket number issued to the client (by the
server) when an authenticated connection between the client and the
server was initially made - when the user first logged in. It
represents a user's pass-number that has to be supplied to the server
every time the client makes a request. The server uses this information
to authenticate the client (and apply necessary security permissions)
before responding to the request. If the request from the client is
authorised the server then returns an instance of the requested object
to the client. The client then passes this object back to the Windows
Forms hosting application. So, from the perspective of the user of the
_client object, the object is returned simply from the client, the user
remains totally shielded from what actually takes place from the client
point onwards (i.e. the user is totally unaware that the client is
forwarding on a request to another object to obtain the data required).
That's the basic crooks of how everything works. The windows forms
application asks the client for an object, the client then forwards on
the request to a remote server (in a different physical location to the
client), the server handles the request (if authenticated) and then
returns the object back to the client, which then returns it back to
the windows forms application.
I hope I explained myself well, sorry if not.
Now, this is my predicament. I want to save an object, but I'm a
little stuck... This is how I've gone about it - apologies if its
rubbish, but this is my first attempt J...
When, for example, a comment is to be saved the user of the Comment
class instance (object) simply has to invoke the 'Save()' method.
One thing I forgot to mention earlier was that before an object is
passed back to the Window Forms application from the _client object,
the client first passes a reference (to itself) into the business
object by means of a Client property. This is to allow the business
object to invoke the client's SaveObject(myObject objectInstance)
method - which I'll come to shortly. So, I suppose the code
written above (GetComment) should be written a little more like this:
public Comment GetComment(string commentId)
{
Comment comment_ = _server.GetComment(commentId,
_ticket.ticketNumber);
comment.Client = this;
return comment_;
}
Remember how I said that the there exists...
'5. An object library (called ExcelciaBusiness.dll - containing the
declarations of the business objects) that is shared by both the client
and server object libraries so that both have a reference to the
business object types.'
....well, this is where the declaration of the Comment class is
contained. The property of the Comment class that is named 'Client'
is marked as 'internal' to the assembly in which it resides. This
is to hide it from the outside world (in this case, hiding it from the
Windows Forms Hosting application (item 1 in the list above)) because
this is something that the outside world should not have access to.
However, I still need the _client object to access it as it needs
business object type information (class declaration) for early binding
compilation. To allow the _client object to see the 'internal'
property, 'Client', of the 'Comment' object, I made the
internal items (methods, properties, etc.) of the object library (point
5) visible to both the assemblies: 'ExcelciaClient.dll (item 2 in the
list above) and ExcelciaServer.dll (item 4 in the list above). I did
this by adding the following lines to he assemble ExcelciaBusiness.dll:
[assembly: InternalsVisibleTo("ExcelciaClient")]
[assembly: InternalsVisibleTo("ExcelciaServer
I made it available to the ExcelciaServer.dll because the server
(object on the remote machine) like the _client object, also needs to
access information from the business objects that should not be visible
to the general outside world - which I'll come to on page 9678 of
this book... J ...it seems to be turning into one, I told you I'm
verbose...please stick with me though, I really need help (in more ways
than one J).
Ok, getting back to my earlier point, when the user of the business
object wants to save the object he/she simply has to invoke the Save()
method. Then, what should happen is that the business object should use
it's internal reference to the _client object to invoke it's
SaveObject method - passing itself as a parameter to the method.
E.g. The Commet's Save Method...
public MyObject Save()
{
//validate object prior to this call...
return _client.SaveObject(this);
}
I also forgot to mention that my objects derive from a base class,
MyObject - all saveable objects derive from this base class (as I
need to invoke a Persist method later on that should be overridden by
all derived classes).
So, what should now happen is that the object should be passed (by
value) across the network (by means of .NET Remoting) to the server
object (hosted in a Windows Forms Application) on the server machine.
Inside the _client's SaveObject method...
public MyObject Save(MyObject objectInstance)
{
objectInstance.Client = null; << prevent the client from being
serializ(s)ed for transmission across network
objectInstance = _server.SaveObject(_ticket.Number, objectInstance);
//à this then sends the object to the server along with the client's
ticketnumber (for authorisation by the server)
//Once the object returns from the server re-issue the client reference
objectInstance.Client = this;
//Then return it to the caller of this function...
return objectInstance;
}
Waiting (to be invoked) inside the server class instance is the method
(from line 2 of the above Save method)...
//Inside the server class on the remote machine:
public MyObject Save(Guid ticketNumber, MyObject objectInstance)...
this is where my problem (or one of them) lies...
My original plan looked something like this...
public MyObject Save(Guid ticketNumber, MyObject objectInstance)
{
//Use ticketNumber to get user id permissions, etc. and authorise
application to proceed...
objectInstance.server = this; <<I'll come to this
in a mo'
objectInstance.Persist(); <<- tells the object to save itself...
objectInstance.server = null; << ready to be passed back across the
network...
return objectInstance;
}
What would happen here is that the Comment object (in this case at
base-class level (MyObject)) would be passe a reference to the Server
object. The Server object has information that the Comment object
needs, but currently does not have...
That information (in a simple case) is the reference to a database
connection (I'm not using a DAL yet - I've not ready enough info
on the subject) and the user's Id - which is obtained by the server
by performing a lookup of the client's ticketNumber - I don't
want the client having any knowledge of physical database keys - I
see it as a small security risk. I have other situations with other
objects that are sent to the user without actual database Ids -
instead enumerations are used, which are then later translated into key
values when the business object arrives at the server (ready to be
saved).
So what I have now is the situation whereby the ExcelciaServer.dll
references the ExcelciaBusiness.dll for type information on the
business objects, and because the business objects (in this case, a
Comment class instance) need to reference the server class instance
(that resides in the ExcelciaServer.dll) so that they can invoke
methods on the server object - such as GetUserId, GetConnection (to
the database) the ExcelciaBusiness.dll needs a reference to the
ExcelciaServer.dll. This is a problem as it results in the need for a
circular reference dependency - not good... bad design??? Not sure
- not proficient enough to say... got a bad feeling though... Anyway,
to get around this problem I thought of creating an Interface for the
server object called IServerSide that the server object (inside
ExcelciaServer.dll) would implement, and the ExcelciaBusiness.dll could
reference - that way the business objects could reference an interface
to the server and the server could still reference the
ExcelciaBusiness.dll without creating a circular reference -
reference-mad am i...? The only downside to this is that the client
also needs the class definitions of the business objects; the
ExcelciaBusiness.dll therefore needs to be located on the client as
well as the server (where any man and his dog can see the server side
methods of the interface (IServerSide - which is totally
inappropriate! - as they are declared as public)). So, to get around
this I tried making the interface internal so that only the server
object and client object (both classes defined the in the assemblies
ExcelciaClient.dll ExcelciaServer.dll) could see the methods - hence,
stopping every man and his dog (external to the dlls) viewing the
server-side methods. Great I thought... Until I realised that for the
server to implement the interface (which was marked as internal - yet
visible to both ExcelciaClient.dll ExcelciaServer.dll because of the
lines
[assembly: InternalsVisibleTo("ExcelciaClient")]
[assembly: InternalsVisibleTo("ExcelciaServer")]) the methods that it
implemented from the IServerSide interface need to be declared as
public on the server - making them available to anyone whom may get
their grubby hands on the server DLL. The problem with this is that one
of the IServerSide methods is GetConnection, which returns an
SQLConnection instance - ready access to my database. Now, I don't
want the server to expose this method because it means there's
potential for anyone else who may have access to the DLL to also
reference it and gain direct access to my database - a big no, no!!!
So, what's my alternative...? Design wise, I'm not sure - as
I'm still pretty new to O.O. and.NET stuff... What I did think of
doing was altering the server instance's Save method so that, based
on the type of object being passed into the method, I would pass
various properties into the business objects before invoking the
business object's Persist() method, removing the need for the
business object to reference the server...
E.g.
Not yet looked into how to determine object type at runtime, so please
ignore my inability to type the necessary code... I hope you get the
picture...
(Save method of the Server class instance):
public MyObject Save(MyObject objectInstance)
{
if (object is a Comment)
{
objectInstance.DBConnection = GetConnection();
objectInstance.UserId =
GetUserIdFromTicket(ticketNumber);
}
else
{
if (it is something else)
{
//pass it some other parameters that it requires before saving
itself...
}
}
objectInstance.Persist();
return objectInstance;
}
My only issue with this is that I may end up with a huge switch/if
statement and I'm pretty sure that's not a good design. It is for
that reason that I'm looking for help. I am now at a crossroads and I
really, really don't know which way to turn - and whether there's
another road that I can't yet see... PS. I cannot afford to start
again as my deadline is fast approaching...
Many thanks if you managed to read until this point and congratulations
if you can understand my terrible English. If anyone can point my in
the right direction I would really, really appreciate it.
Regards,
Craig.
.
- References:
- A tricky one...
- From: GoogleEyeJoe
- A tricky one...
- Prev by Date: Re: RemotingException - Request Timed Out
- Next by Date: Re: Execution doesn't return from server after VS 2005 upgrade
- Previous by thread: Re: A tricky one...
- Next by thread: RemotingException - Request Timed Out
- Index(es):
Loading