views:

445

answers:

3

[Problem]
Having created a shared library that is to be deployed on a server machine and client machine how do I communicate between the client-server with the classes provided by the library?

Transferring the information via webservices does not seem to work as the serialised object returned by the web service is a webservice class which does not convert to the shared library.

Am I using the webservices incorrectly? Is there a better way?

[Example]

MyLibrary.cs and SubLibrary.cs is in a shared assembly that is to be used by the client app.

MyLibrary.cs

public class MyLibrary
{
    private SubLibrary sublib = new SubLibrary();

    public class MyLibrary()
    {
    }

    public string GetValue()
    {
        return sublib.GetValue();
    }
}

SubLibrary.cs

public class SubLibrary
{
    private string str = "Hello World";

    public SubLibrary()
    {
    }

    public string GetValue()
    {
        return str;
    }
}

WebService.asmx.cs

[WebMethod]
public MyLibrary GetInfo()
{
    return new MyLibrary();
}

Client App

private void GetInfo_Click(object sender, System.EventArgs e)
{
  WS.WebService services = new WS.WebService();

  MyLibrary info = services.GetInfo();  // This of course doesn't convert.

  MessageBox.Show(info.GetValue());
}
+3  A: 

In short this is a pain in 1.1 (and partly 2.0 too). Assembly-sharing (or type-sharing) only really started being a feature in WCF (.NET 3.0). The proxies generated in 1.1 will never be directly compatible with the "actual" classes; you can share the same proxies between multiple services ("sharetypes" ?), but not with independent source files, IIRC.

And since it is C# 1.2 you have neither partial classes nor extension methods to cheat with to translate them.

Options (that I can see):

  • write a static utility method to laboriously translate between the two object models
  • ditto but using XmlSerializer (less code, but not as quick)
  • live with it
  • upgrade to WCF (quite a significant change)
Marc Gravell
+1 @Marc: Thanks for the insight and alternative options. It is true that it is a big pain in 1.1.
Gavin Chin
@Marc: If you were on .NET 3.5, how would you use either partial classes or extension methods to convert the objects?
Dustin Venegas
With partial classes you could add static conversion operators (explicit/implicit). With extension method you could add `ConvertToFoo()` methods etc.
Marc Gravell
+1  A: 

With a Web Service, the type of every object that you send across the wire MUST be serializable. You can make SubLibrary serializable by implementing the IXmlSerializable interface.

public class SubLibrary : IXmlSerializable 
{
    private string str = "Hello World";

    public SubLibrary()
    {
    }

    public string GetValue()
    {
        return str;
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        //...
    }

    public void WriteXml(XmlWriter writer)
    {
        //...
    }
}

One caveat though: Since you're passing both MyLibrary and SubLibrary through the WS, you need to make sure they are both serializable. Furthermore, since you're passing MyLibrary and it contains an instance of a SubLibrary, you have to maintain the relationship between MyLibrary and SubLibrary during serialization, and that will be a problem. Unless you have some other reason for wanting to encapsulate an instance of SubLibrary inside a MyLibrary that I'm not aware of, I'd get rid of that middle layer, and have your Web Service just return an instance of SubLibrary. So you would just have:

[WebMethod]
public SubLibrary GetInfo()
{

    return new Sublibrary();
}

And in the client app:

private void GetInfo_Click(object sender, System.EventArgs e)
{
  WS.WebService services = new WS.WebService();

  SubLibrary info = services.GetInfo();  // This of course doesn't convert.

  MessageBox.Show(info.GetValue());
}

A good way to thing of passing complex objects via Web Services is: you're serializing all of the data members of an instance into XML, sending it over a wire, and then deserializing it back into real data at the other end. Don't think of it as sending the actual class (with the non-data members) over the wire.

HiredMind
Using `IXmlSerializable` tends to add more confusion to web-services, since the proxies won't have the code...
Marc Gravell
He's talking about using the same classes on either side of the wire, so that shouldn't be a problem. If the OP is designing web services for public consumption, I definitely agree with your point.
HiredMind
A: 
Tuzo
-1: He _wants_ to use the classes in the common library.
John Saunders
I understand that. I was *trying* to make 3 points: what the typical approach would be (since there is no elegant solution), that I had done this before by modifying the proxy and deleting the generated entities and adding a using for my common library (but that that isn't a good idea, IMO), and that the sample code doesn't expose any properties (it uses methods which are not serialized). Of course, I may have failed. :)
Tuzo
NOTE: The implementation I've been told to use is the same as you've said Tuzo with the modification of the generated entities. Personally I prefer Marc's approach of creating an adapter as such. Better to localise the pain in a layered approach than to resort to manually edit generated code. Also, we've found that manually editing the generated entities the original design and code has to be tweaked to allow web service serialisation. This polutes the design by having to expose classes methods that don't need to be.
Gavin Chin