|
DAGNABIT!!
Microsoft hates me sucks today!
Without the Colour property, the service works just fine, the proxy file begins this way:
namespace Junk.Types
{
using System.Runtime.Serialization;
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Record", Namespace="http://schemas.datacontract.org/2004/07/Junk.Types")]
public partial class Record : object, System.Runtime.Serialization.IExtensibleDataObject
::snip::
With the Colour property, the proxy file is totally different:
using System.Data;
::snip::
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemas.datacontract.org/2004/07/Junk.Types")]
public partial class Record
{
private System.Data.DataSet favouriteColourField;
::snip::
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]
public System.Data.DataSet FavouriteColour
{
get
{
return this.favouriteColourField;
}
set
{
this.favouriteColourField = value;
}
}
::snip::
The Record class is no longer in the Junk.Types namespace! And other problems as well.
Maybe I should try it as a Color...
|
|
|
|
|
Hmm. I did get a DataSet at some point from WSDL.EXE too. And I didn't like it a bit.
I still have a lot to explore, I'm not giving in yet!
BTW: Keith Barrow just added quite a message to this thread, you might be interested as well.
|
|
|
|
|
Luc Pattyn wrote: DataSet ... And I didn't like it a bit.
Ditto.
Plus, when the property is a Color, it wants to serialize the Color type.
What I have now is I'm passing a string through the service, but accessing it as a Colour. It's pretty ugly... fugly, even.
Luc Pattyn wrote: Keith Barrow
If it's one the ones I already read, I already read it.
|
|
|
|
|
Luc Pattyn wrote: OK, let me provide some context. I'm preparing a proposal specification for a CodeProject Web Service, something that would provide information about members, articles, messages, etc, so several existing and future applications (such as CP Vanity) could use it to their advantage, which would be much preferred over fetching web pages and scraping HTML. The proposal would be discussed amongst interested members before Chris might commit to it and implement it.
Finally . WCF might be a better technology in the long run, you can still expose a vanilla SOAP front end from a WCF service, but WCF is designed to be extensible. IIRC WCF is much better at versioning.
Luc Pattyn wrote:
I expect the CP data model (as well as the subset we want access to) will evolve just like anything else, and I do want future old clients not to break under future new service versions.
This is tricky if the service doesn't allow for this at the outset.
Luc Pattyn wrote: I'm rather pleased with what IIS and WSDL are offering, as it works perfectly for basic types such as int, float, bool, string, as well as DateTime; it works pretty well for Size too.
You can send custom objects types too. The web services are "contract" based, if you let the service know you it returns as type of MyObject as long as this has been correctly defined it will serialize its properties properly. Some framework classes will get though as they are serializable, though obviously the instance changes
Luc Pattyn wrote: Color does not get through by default; it needs extra code at both ends.
This is a limitation of web services as a whole (a common interview question is explain the differences/conflicts between web services and Object Orientated systems - this is one of the points) even if you provide a client API that does the aforementioned jiggery-pokery , it doesn't guarantee against someone generating / creating their own client class. Conversely, it is good if your service is designed to cope with this, for example the Objective C required for an iPhone App wouldn't play with MS objects coming over the wire. This is why I suggested sending the value to the client, an iPhone app would be able to convert this to a hex value.
The "classical" way of proceeding with a web services is to convert the sever object model information into the serializable message classes (which flatten also often flatten out the object graph), send those across the wire. The client is then responsible for converting the deserialized [proxy generated] class out into the client's object model. It is possible to provide an API dll with the client classes pre-defined (it may even be desirable for .net clients, I have done this in the past) but it is a good idea to make the system interop with other technologies. I'd love an iPhone app, as would others I'd expect, not to mention Android phone users etc.
If you need a hand with this, feel free to e-mail me, I've a fair amount of WCF experience and a bit of Vanilla SOAP services stuff. I've felt the CP has lacked this feature for some time, so I'd be happy to help.
|
|
|
|
|
Hi Keith,
thanks for all the information and the insight.
I had set out to define the kind of information one would need from CodeProject, and having gotten that together to some degree, I decided to perform some mock-up experiments, as I often do while writing specs. So I created a little web service and discovered WSDL, and got a few things up and running quite easily, until I got surprised by DateTime and Size working and Color not working.
In the mean time I learned a lot, and reading your message and also PIEBALD's, I feel a hiatus in not knowing a thing about WCF, which I'll have to remedy soon.
You raised an excellent point about support for non-Windows clients. Even when they don't know about .NET or C#, we probably should not ignore them, in fact I'm inclined to fully consider them and to make their support no more difficult than necessary.
So I'm going to split my proposal efforts in two parts now; one concentrating on the functional level, to be discussed with people who already did some HTML scraping clients. The other about the underlying server-client and communication technology. I'll study and experiment some more, but I think I also may take you up on your kind offer.
Regards, and thanks again.
|
|
|
|
|
Luc Pattyn wrote: non-Windows clients ... should not ignore them
F-'em!
But seriously, WCF uses "industry standard protocols", so they shouldn't have any particular problem consuming .net Web Services. They'll have to do a bit of work regardless, there's not much you can do about that. What you can do is make things easier for .net practitioners, without unduly affecting non-.net practitioners.
|
|
|
|
|
PIEBALDconsult wrote: without unduly affecting non-.net practitioners
that sounds about right.
I guess you did read it then.
|
|
|
|
|
I have a copy of this[^].
|
|
|
|
|
So, look right away for "Color" in the index.
|
|
|
|
|
Why didn't I think of that?
It's not there -- it should be right before "Confuse a Belgian".
|
|
|
|
|
PIEBALDconsult wrote: right before "Confuse a Belgian"
Even less likely to be found in an index.
Would be an interesting title for a book though.
|
|
|
|
|
Sooo... my Colour class didn't help.
I now have a kludge working that passes the Argb as a string and parses it on the other side. Fugly fugly fugly, but here it is.
(This is the class I was using last spring.)
Using a partial class, I have only those properties that are to be serialized in Record.cs:
namespace Junk.Types
{
[System.Runtime.Serialization.DataContractAttribute()]
public sealed partial class Record
{
[System.Runtime.Serialization.DataMemberAttribute()]
public System.Guid ID { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public string LastName { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public string FirstName { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public Junk.Types.Gender Gender { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
private string colourstring { get ; set ; }
}
}
Note that the colourstring is private.
Other members of the class are in RecordPlus.cs:
namespace Junk.Types
{
using PIEBALD.Lib.LibExt.ParseColor ;
public partial class Record
{
private System.Drawing.Color? favouritecolour = null ;
public Record
(
)
{
return ;
}
public Record
(
System.Guid ID
,
string LastName
,
string FirstName
,
Junk.Types.Gender Gender
,
System.Drawing.Color FavouriteColour
)
{
this.ID = ID ;
this.LastName = LastName ;
this.FirstName = FirstName ;
this.Gender = Gender ;
this.FavouriteColour = FavouriteColour ;
return ;
}
public System.Drawing.Color FavouriteColour
{
get
{
if ( !this.favouritecolour.HasValue )
{
this.favouritecolour = this.colourstring.ParseColor() ;
}
return ( this.favouritecolour.Value ) ;
}
set
{
this.favouritecolour = value ;
this.colourstring = "#" + value.ToArgb().ToString( "X8" ) ;
return ;
}
}
public string
FullName
{
get
{
return ( System.String.Format
(
"{0}, {1}"
,
this.LastName
,
this.FirstName
) ) ;
}
}
public override string
ToString
(
)
{
return ( System.String.Format
(
"{0} {1} {2}"
,
this.FullName
,
this.Gender
,
this.colourstring
) ) ;
}
}
}
(ParseColor is an Extension Method.)
On the server side, the code uses the class as defined in these two files.
On the client side, the code uses the second file and the proxy file, which has its own version of the first file.
That's all the jiggery-pokery I've been doing, and it works fine for the types I've been using.
As for non-.net practitioners, they'd see the #FFFFFFFF and deal with it however they need to.
|
|
|
|
|
OK, thanks. I'll study that tomorrow.
One question though: Is this still part of SOAP and WSDL/svcutil plus extra client methods;
or is this part of (or built on top of) WCF?
|
|
|
|
|
The JunkClient.cs (in this case) file that svcutil generates from the WSDL contains a (partial) Record class containing only those properties that are specified in the contract. Then I supply the rest of the class via another file.
Record.cs + RecordPlus.cs == Record class
| |
| |
WSDL/svcutil |
| |
| |
V V
JunkClient.cs + RecordPlus.cs == Record class
|
|
|
|
|
so it does not need WCF, it is just SOAP, WSDL and partial classes, and .NET 2.0 can do all of those. Right?
|
|
|
|
|
It doesn't need WCF. I could try compiling it with .net 2.0 -- this is a project I started on to try out VS 2010.
I'll get back to you later, the kid wants me to read to him.
Have good night.
|
|
|
|
|
Good Night to all of you then.
|
|
|
|
|
I sit corrected:
DataMemberAttribute Class[^]
.NET Framework
Supported in: 4, 3.5, 3.0
So, no, not .net 2.0 -- unless maybe you write your own, like you can with ExtensionAttribute...
And my code as it stands uses Extension Methods and HashSet, so I would need to remove those.
modified on Sunday, September 26, 2010 12:54 AM
|
|
|
|
|
The stuff that everybody else said is confusing, so I thought I'd make it simple if anybody else is curious.
The properties A, R, G, and B on the Color struct only have getters (i.e., they don't have setters). No setters, the serializer can't set them. And the underlying property that they are based upon, Value, is private (not to mention it only has a getter), which serialization skips.
The Size struct serializes/deserializes fine because the Width and Height properties are public and have setters.
Oh, and in case you are curious why the deserialization requires a public setter... it first creates an instance of the class by calling the default constructor, then it calls the setter on each public property. That, by the way, means that serialization doesn't work by default on classes without a default constructor (I've been annoyed by that a couple times).
I think you can inherit from (or as PIEBALD might say, implement) an interface or something that allows you to implement custom serialization, but I've never had to resort to that.
|
|
|
|
|
aspdotnetdev wrote: as PIEBALD might say, implement
Correct, you're learning, grasshopper.
Much of that is true, but the question remains of why MS chose to implement Color in such a way that it won't serialize/deserialize in some meaningful way.
As I demonstrated with my last experiment, the property that gets serialized/deserialized (colourstring) doesn't have to be public (because objects serialize/deserialize themselves). MS could implement Color with serialization as I did with my Colour class.
Edit:
Allow me to amend that. This being a (WCF) Web Service, the class that is generated for the proxy includes:
[System.Runtime.Serialization.DataMemberAttribute()]
public string colourstring
{
get
{
return this.colourstringField;
}
set
{
this.colourstringField = value;
}
}
So, although the property in the class I wrote is private, the property that gets deserialized is public.
Crap, that's no good... what genius came up with that idea?
|
|
|
|
|
Aah ha ha ha!
System.ConsoleColor is an enumeration, so it passes through just fine... if you like 16-color.
Or write your own enumeration with the colours you want... how many could there be?
|
|
|
|
|
Yes, that was my initial approach, just an enum for CP colors (mainly: Orange, DarkGreen, Platinum, Gold, Silver, Bronze), but then I wanted to get the actual color components so an app could somewhat mimic the site look, even when CP itself changes some of its style parameters.
As DateTimes did just fine, I got a big bad surprise by Color acting up. You know the rest.
|
|
|
|
|
I have now rewritten my Colour class (as a struct) and it seems to work properly.
Record.cs as it now stands:
namespace Junk.Types
{
[System.Runtime.Serialization.DataContractAttribute()]
public sealed partial class Record
{
[System.Runtime.Serialization.DataMemberAttribute()]
public System.Guid ID { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public string LastName { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public string FirstName { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public Junk.Types.Gender Gender { get ; set ; }
[System.Runtime.Serialization.DataMemberAttribute()]
public Junk.Types.Colour FavouriteColour { get ; set ; }
}
}
Colour.cs:
namespace Junk.Types
{
public partial struct Colour
{
public byte A ;
public byte R ;
public byte G ;
public byte B ;
}
}
ColourPlus.cs:
namespace Junk.Types
{
using PIEBALD.Lib.LibExt.ParseArgb ;
public partial struct Colour
{
public override string
ToString
(
)
{
return ( System.String.Format
(
"#{0:X2}{1:X2}{2:X2}{3:X2}"
,
this.A
,
this.R
,
this.G
,
this.B
) ) ;
}
public static Colour
Parse
(
string Argb
)
{
return ( Colour.FromArgb ( Argb.ParseArgb() ) ) ;
}
public int
ToArgb
(
)
{
return ( this.A << 24 | this.R << 16 | this.G << 8 | this.B ) ;
}
public static Colour
FromArgb
(
int Argb
)
{
Colour result = new Colour() ;
result.B = (byte) ( Argb & 0x0FF ) ;
Argb >>= 8 ;
result.G = (byte) ( Argb & 0x0FF ) ;
Argb >>= 8 ;
result.R = (byte) ( Argb & 0x0FF ) ;
Argb >>= 8 ;
result.A = (byte) ( Argb & 0x0FF ) ;
return ( result ) ;
}
public static implicit operator System.Drawing.Color
(
Colour Colour
)
{
return ( System.Drawing.Color.FromArgb ( Colour.ToArgb() ) ) ;
}
public static implicit operator Colour
(
System.Drawing.Color Color
)
{
return ( Colour.FromArgb ( Color.ToArgb() ) ) ;
}
}
}
(ParseArgb replaces ParseColor, it returns an int.)
I haven't inspected the SOAP messages (I don't know how), but I expect they contain the four byte values; something along the lines of:
<FavouriteColour><A>256</A><R>256</R><G>256</G><B>256</B></FavouriteColour>
|
|
|
|
|
I'll experiment with this later today.
BTW: are you with Procter&Gamble? your favorite color is "whiter than white" (that is, if your code recovers from overflow when parsing the example)
|
|
|
|
|
I prefer Belgian White.
|
|
|
|
|