Simon Fell > Its just code > August 2006

Wednesday, August 30, 2006

Sync Services is pretty cool (it has a couple of rough edges, but provides a ton of useful features out of the box), and i have a prototype up and running that will syncronize contacts between the Mac address book and Salesforce.com, it does bi-directional sync, handles conflicts etc. all is good. However that's all my app does, its a window with a sync button on, it currently looks a lot like iSync, and in fact everyone who's seen it so far asks the very reasonable question, why isn't it just a plug-in to iSync. That's my question too, but everything I've read so far indicates that the iSync plug-in model is not available to 3rd parties. What's Apple's plans here, will iSync become some extensible hub, or is it being downplayed into a phone sync tool, and will i end up with a ton of app's all syncing different peices of data to different services ?

Monday, August 21, 2006

I've been reading up on Apples Sync Services, I really like what I've seen so far, the ability to push field level changes, the fact that the engine will manage Id mappings for you, so far it seems really well thought out. Nice job whoever worked on it.

Thursday, August 17, 2006

The MSDN library is littered with sample code, most of it annoys the hell out me because it doesn't say what the expected outcome is from running the code, and sometimes the samples are downright misleading, take this one from CodeTypeReferenceCollection

// Creates an empty CodeTypeReferenceCollection.
CodeTypeReferenceCollection collection = new CodeTypeReferenceCollection();

// Adds a collection of CodeTypeReference objects to the collection.
CodeTypeReferenceCollection referencesCollection = new CodeTypeReferenceCollection();
referencesCollection.Add( new CodeTypeReference(typeof(bool)) );
referencesCollection.Add( new CodeTypeReference(typeof(bool)) );
collection.AddRange( referencesCollection );

// Tests for the presence of a CodeTypeReference in the 
// collection, and retrieves its index if it is found.
CodeTypeReference testReference = new CodeTypeReference(typeof(bool));
int itemIndex = -1;
if( collection.Contains( testReference ) )
    itemIndex = collection.IndexOf( testReference );

No where does it mention what value itemIndex might have when you run this. I saw this and took it to mean that the CodeTypeReference's would get compared by value rather than reference, and so find the bool CodeTypeReference even though its a different instance, but no, running the code reveals that CodeTypeReference doesn't seem to override Equals or ==, and so the collection.Contains call returns false. grrrhhh. (this makes finding out if a particular type is in the Collection something of a PIA if you didn't construct and populate the collection yourself.)

Getting no where with my woes with SchemaImporterExtension, despite the claims in the docs, it seems like its only really designed to substitute out types for existing types that are referenced in, not for more complex code generation solutions. There's a bunch of trivial examples out there, but nothing that does anything complex with it. Looks like a more workable approach will be to build a replacement wsdl.exe (which is largely a wrapper around ServiceDescriptionImporter. Its a pity that everything except Main is private in the wsdl.exe class, it always seems like the Microsoft classes always default to not being re-usable unless they really want it to, which sucks, i get to duplicate a whole bunch of boring boiler plate code just so I can get at the CodeDom before it gets serialized out to a file.

Update So I still need to wire up all the wsdl.exe options, but I got the basic's working, it sucks in the WSDL, massages my changes into the CodeDom then spits it out to a file :)

Tuesday, August 15, 2006

I've been digging into SchemaImporterExtension to see if I can fix the generated code from .NET's wsdl.exe to include setting the FooSpecified flag from the foo Setter (see this post from way back in 2004 if you need a refresher on the details). What's currently generated for an optional element (minOccurs='0' / maxOccurs='1') is this

private System.Nullable<System.DateTime> createdDateField;
    private bool createdDateFieldSpecified;

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
    public System.Nullable<System.DateTime> CreatedDate {
        get {
            return this.createdDateField;
        }
        set {
            this.createdDateField = value;
        }
    }
And what It really should of been is
private System.Nullable<System.DateTime> createdDateField;
    private bool createdDateFieldSpecified;

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
    public System.Nullable<System.DateTime> CreatedDate {
        get {
            return this.createdDateField;
        }
        set {
            this.createdDateField = value;
            this.createdDateFieldSpecified = true;
        }
    }

Its frustrating, I'm sure someone up in Redmond could fix this in a couple of hours, but I've been waiting for 2 years now (and I expect the wait will be a whole bunch longer). But with .NET 2.0, SchemaImporterExtension gives hope that I can fix this myself without having to wait. Unfortuantly the samples and docs are pretty skinny, it seems like you get a chance to replace the code generation for a particular type, but you don't seem to get any access to the default code that would be generated, this means in this case you need to duplicate all of the logic that generates the code for a ComplexType. As anyone who's done work with generating code from WSDL will tell you, this is a non trivial amount of work to get right. Anway, I plowed in and got the basics working, however have run into a couple of road blocks, how do I tell the importer to import an additional type (e.g. complexType foo has an element of type bar, so I need to import the bar type as well), and have it generate its code / call back into the extensions etc ? You get passed an instance of a XmlSchemaImporter object to your code, (but this is doc'd as "This class supports the .NET Framework infrastructure and is not intended to be used directly from your code." huh? so why is it in the methods in a public extension point?) anyway, this seems to have the hopeful method called ImportSchemaType (amongst others), however all this seems to do is generate the naming mappings between the schema type QName you pass it, and the resulting CLR type, it doesn't generate any code in the final proxy. I'm also seeing that ImportSchemaType gets called for some schemaTypes (in particular complexTypes that are used by other complexTypes), and the resulting additions to the CodeNamespace seem to get dropped.).

I put together a quick sample that re-pro's the latter problem with code getting dropped, here's the ImporterExtension, a test WSDL, parameters file for wsdl.exe and the resulting generated file, notice that even though ImportSchemaType was called for the Bar type, and we added a CodeTypeDeclaration to the mainNamespace for it, it doesn't appear in the final code.

C:\Source\dotnet\sfSchemaImporterTest>wsdl.exe /par:params.xml
Microsoft (R) Web Services Description Language Utility
[Microsoft (R) .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation. All rights reserved.
Generating type Bar
adding Bar to mainNamespace
Generating type FooResult
adding FooResult to mainNamespace
Writing file 'C:\Source\dotnet\sfSchemaImporterTest\DoFooService.cs'.

This also only seems to allow me the chance to fiddle with the schema types, is there an equvilent extension to allow me to fiddle with the web services specifics bits? (like the DoFooService class in the above sample, or the classes generated for soap headers)

Dreamforce is rapidly approaching and this year there's ADN @ Dreamforce, a conference in a conference for AppExchange developers, be the first to find out about all the cool features coming in the Winter release. I'll be there at the ever popular meet the developers session, drop by and say hi.

Monday, August 14, 2006

I was in Seattle for a couple of days, and managed to squeeze in some good coffee, following the advise on Tonx's blog we headed over to Sitka & Spruce in Eastlake (which despite its name is not east of lake Washington as I was expecting). Bronwen served up a mighty tasty macchiato, the best one I've had in a long time, the milk was done perfectly, and the espresso was very smooth with no bitterness what so ever, yummy.