views:

557

answers:

4

It's 2008, and I'm still torn on this one. So I'm developing a web method that needs a complex type passed into it and returned from it. The two options I'm toying with are:

  1. Pass and return actual business objects with both data and behavior. When wsdl.exe is run, it will automatically create proxy classes that contain just the data portion, and these will be automatically converted to and from my real business objects on the server side. On the client side, they will only get to use the dumb proxy type, and they will have to map them into some real business objects as they see fit. A big drawback here is that if I "own" both the server and client side, and I want to use the same set of real business objects, I can run into certain headaches with name conflicts, etc. (Since the real objects and the proxies are named the same.)

  2. Forget trying to pass "real" business objects. Instead, just create simple DataTransfer objects which I will map back and forth to my real business objects manually. They still get copied to new proxy objects by wsdl.exe anyway, but at least I'm not tricking myself into thinking that web services can natively handle objects with business logic in them.

By the way - Does anyone know how to tell wsdl.exe to not make a copy of the object? Shouldn't we be able to just tell it, "Hey, use this existing type right over here. Don't copy it!"

Anyway, I've kinda settled on #2 for now, but I'm curious what you all think. I have a feeling there are way better ways to do this in general, and I may not even be totally accurate on all my points, so please let me know what your experiences have been.

Update: I just found out that VS 2008 has an option to reuse existing types when adding a "Service Reference", rather than creating brand new identical type in the proxy file. Sweet.

+4  A: 

I'd do a hybrid. I would use an object like this

public class TransferObject
{
    public string Type { get; set; }
    public byte[] Data { get; set; }
}

then i have a nice little utility that serializes an object then compresses it.

public static class CompressedSerializer
{
    /// <summary>
    /// Decompresses the specified compressed data.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="compressedData">The compressed data.</param>
    /// <returns></returns>
    public static T Decompress<T>(byte[] compressedData) where T : class
    {
        T result = null;
        using (MemoryStream memory = new MemoryStream())
        {
            memory.Write(compressedData, 0, compressedData.Length);
            memory.Position = 0L;

            using (GZipStream zip= new GZipStream(memory, CompressionMode.Decompress, true))
            {
                zip.Flush();
                var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                result = formatter.Deserialize(zip) as T;
            }
        }

        return result;
    }

    /// <summary>
    /// Compresses the specified data.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="data">The data.</param>
    /// <returns></returns>
    public static byte[] Compress<T>(T data)
    {
        byte[] result = null;
        using (MemoryStream memory = new MemoryStream())
        {
            using (GZipStream zip= new GZipStream(memory, CompressionMode.Compress, true))
            {
                var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                formatter.Serialize(zip, data);
            }

            result = memory.ToArray();
        }

        return result;
    }
}

Then you'd just pass the transfer object that would have the type name. So you could do something like this

[WebMethod]
public void ReceiveData(TransferObject data)
{
    Type originType = Type.GetType(data.Type);
    object item = CompressedSerializer.Decompress<object>(data.Data);
}

right now the compressed serializer uses generics to make it strongly typed, but you could make a method easily to take in a Type object to deserialize using originType above, all depends on your implementation.

hope this gives you some ideas. Oh, and to answer your other question, wsdl.exe doesn't support reusing types, WCF does though.

Darren Kopp
thanks man! I couldn't get compress working until I noticed you read memory stream **after** disposing zip stream. Apparently flushing the zip stream is not enough...
dotjoe
+1  A: 

Darren wrote: I'd do a hybrid. I would use an object like this...

Interesting idea... passing a serialized version of the object instead of the (wsdl-ed) object itself. In a way, I like its elegance, but in another way, it seems to defeat the purpose of exposing your web service to potential third parties or partners or whatever. How would they know what to pass? Would they have to rely purely on documentation? It also loses some of the "heterogeneous client" aspect, since the serialization is very .Net specific. I don't mean to be critical, I'm just wondering if what you're proposing is also meant for these types of use cases. I don't see anything wrong with using it in a closed environment though.

I should look into WCF... I've been avoiding it, but maybe it's time.

jeremcc
+1  A: 

oh, for sure, i only do this when i'm the consumer of the webservice or if you have some sort of controller that they request an object from and then you handle the serialization and sending rather than them directly consuming the web service. But really, if they are directly consuming the webservice, then they wouldn't need or necessarily have the assembly that would have the type in it in the first place, and should be using the objects that wsdl generates.

And yes, what i put forth is very .NET specific because i don't like to use anything else. The only other time i consume webservices outside of .net was in javascript, but now i only use json responses instead of xml webservice responses :)

Darren Kopp
+1  A: 

there is also an argument for separating the tiers - have a set of serializable objects that get passed to and from the web service and a translator to map and convert between that set and the business objects (which might have properties not suitable for passing over the wire)

Its the approach favoured by the web service software factory (http://www.codeplex.com/servicefactory) and means that you can change your business objects without breaking the web service interface/contract