Two-way STSSync Protocol Server for Outlook 2007

So I've finally done it. I made the first stab at a two-way stssync protocol server. You can get it here:

https://www.codeplex.com/stssyncprovider

I'd love to have some of you start using it and report issues you find with it so I can continue to improve it. I know there are issues already, but it should be mostly there to at least get you started.

This was largely inspired by Stephen Toub's original Custom Calendar Provider sample. I'd recommend reading that article for background on this idea.  I later updated it to work with Outlook 2007, but this was still just using the ver=1.0 sync (one-way) . Now, with the release of the SharePoint protocol documents which I announced some months back and troubleshooting logging in Outlook, I've been able to construct a stssync protocol server, which, like Toub's sample, allows for you to simply create a provider that snaps into the engine.

My original design goal was to make it so that provider developers would only have to implement a single interface which returned a dataset of records when Outlook requests them and accepted a dataset of updates when Outlook makes changes and the rest of the plumbing would be handled by the provider engine ("the engine"). I came pretty close. Here is the interface as it stands so far:

    public interface IProvider

    {

        DataSet GetEmptyDataSet(Guid ProviderID);

        DataRow GetSingleRow(Guid ProviderID, int id);

        DataRow Update(Guid ProviderID, DataRow updateRow);

        DataSet GetUpdatesSinceToken(Guid ProviderID, ChangeKey changeKey);

        ListType GetProviderType(Guid ProviderID);

        StringDictionary GetFieldMappingsForListType(Guid ProviderID, ListType listType);

    }

 

So here's the explanation for the interface.

  • GetEmptyDataSet: this is mostly used so that the engine can get a feel for what columns are in the table you're returning. You simply return an empty typed dataset or a standard dataset with a single (empty) table that contains the same columns you would later return filled.
  • GetSingleRow: Pretty easy - I give you an ID, you give me back the row associated with it.
  • Update: I give you the datarow with changes made and you pass me back the updated row (some columns may change as a result of doing the update - like Modified time, for example)
  • GetUpdatesSinceToken: I give you a changekey from which you can extract a timestamp, or just call ToString to use the changekey as a watermark for getting changes since the last sync. You return your DataSet filled with any rows that have changed since the provided watermark (changeKey).
  • GetProviderType: You return a value from the ListType enumeration. This tells the engine whether you're a contacts provider versus a calendar provider, task provider, discussion list provider, or document library provider (no doc library or attachment support is currently in the provider - it's tbi).
  • GetFieldMappingsForListType - In this method you simply return a StringDictionary filled with any field name mappings the engine can use to map the columns in your dataset to column names it's familiar with. For example, you might do something like oSDict.Add("myContactIDField","ID");

Each method is passed the ProviderID specified in the web.config for this provider. If you want to implement two different providers in the same class, you can distinguish between them using this ID, otherwise, your implementation can largely ignore this parameter - it is mostly used by the engine to get a reference to your interface.

Once you've implemented the interface, just go to the web.config file for the engine and add a new Provider element. Here's a sample:

<ProviderProxy>
  <Providers>
    <Provider Name="AdventureWorks Contacts" ID="{7765B84F-6D32-4d31-B28E-6BC615D2F187}" Type="AdventureWorksProvider.Contacts" Assembly="AdventureWorksProvider" />
  </Providers>
</ProviderProxy>

  • Name = just the display name you want to appear on the list in Outlook and on default.aspx
  • ID = this contains a Guid that is used in invoking methods on your IProvider interface.
  • Type = the Namespace.ClassName of the type of your IProvider interface.
  • Assembly = the qualified assembly name of the the assembly that contains the Type specified.

Copy your assembly to the bin directory of the engine web site and the engine will automatically find your assembly. I modified the Pre- and Post-Build events on my AdventureWorks provider sample to automatically do this to simplify the debugging process.

This is just a very rough draft. I wanted to have a sample up there that folks could start to work with and get some ideas. I'd love to hear your feedback and for you to help mold the project into something that is good over time. Please contact me with any issues you find or any suggestions (I know of some myself, already). I'll do my best to put out regular releases as the code base improves. I'd also love to hear what you do with it and what unique stores you start syncing with Outlook.

Just as a note, in this initial release, I do not support one-way sync, just two-way (seems backwards, but the goal was to get a two way sync working).

Also, I may need to change the IProvider interface definition from one release to the next as bugs/feature-completeness dictate probably in the early phases. It doesn't make sense to have an interface that's incomplete. Once I'm able to declare feature-completeness on the IProvider interface, any new enhancements or additional functionality will come in the form of additional/augmented interfaces (IProvider2, etc).