views:

80

answers:

2

I'm designing a small directory synchronization engine that can take different kinds of sources and handle incremental synchronization. I have defined an interface for a DirectorySource that currently looks like this:

public interface IDirectorySource
{
    IEnumerable<IDirectoryEntry> GetChanges(IList<string> attributes, IChangeToken token);
}

I do want to provide an enumerator instead of a list in order to be able to avoid unnecessary memory allocation, but my problem is that I also want to return a new IChangeToken that contains the new state information needed for the next call to GetChanges (to do incremental updates). The changetoken must be calculated after the enumeration is completed since someone else could be updating the directory between different calls.

I've thought about having a second IChangeToken-parameter that recieves the new token, but that doesn't feel quite nice (and would not be consistent since the token would be returned immediately but cannot be populated until the enumeration is completed).. And I've thought about returning something like a "IChangeSet"-interface that contains an GetEnumerator method and a GetToken-method, but the problem is still that subsequent calls to the enumerator method returns different data (and therefore have different changetokens).

How can I design an interface that makes it "impossible" for a user of my interface to use it wrong regarding my GetChanges enumerator and retrieving the associated ChangeToken?

I hope my question makes sense... ;).. I had a hard time figuring out what title to use for my question... ;)

+1  A: 

In functional programming languages there is a structure called Tuple. Here I believe Tuple is the best choice - tuple, per design, means that there is no relationships between its items. Current version of .NET Framework (3.5) does not support tuples, but the next one (4.0) does. If necessary, you can always implement Tuple by yourself - no big deal. Unfortunately, C# is missing language support for tuples, but F#, for instance, has.

So, to sum it up, make your interface like this:

Tuple<IEnumerable<IDirectoryEntry>, IChangeToken> GetChanges(IList<string> attributes, IChangeToken token);
Vitaliy Liptchinsky
Yes that would indeed work even though a "foreach"-call on the result might be a little akward. But I do have another issue (I'm going to update my question) and that is that the changetoken is not available until after the enumeration is complete.
Per
You can implement a relationship between two items in the tuple - if client tries to call any method of the IChangeToken before the enumeration - throw an exception.Since there is a relationship, even hidden, tuple structure is not appropriate anymore.
Vitaliy Liptchinsky
In your contract you want clients to follow strict and complex behavior. Methods of your interface should be called in strict order, it looks like a state. Perhaps, you should return a complex object that would maintain the state and hide all the sublte behaviors from the clients.
Vitaliy Liptchinsky
A: 

I have tried an approach using the "IChangeSet" I was thinking about in my original question. In that scenario the IDirectorySource interface looks like this:

public interface IDirectorySource
{
    IChangeSet GetChanges(IList<string> attributes, IChangeToken token);
}

And implemented a IChangeSet-interface that looks like this:

public interface IChangeSet : IEnumerable<IDirectoryEntry>
{
    IChangeToken GetToken();
}

This makes it possible to write client code that looks something like this:

IChangeSet result = source.GetChanges(attributes, token);

foreach (IDirectoryEntry entry in result) {
    // Do something with the data...
}

IChangeToken resultToken = result.GetToken();

Even though it makes it possible to call the "GetToken()"-method before enumerating (which would be wrong) I could return the old token if the function is called before the enumeration is completed.

Doesn't feel 100%, but it's the best I can come up with at this time...

Per
Hmm.. I now realise that this doesn't change much from my original. It just connects the enumerator and token a little bit more, but still allows several calls to the enumerator and then, the GetToken-call can be "out of sync"..
Per