Click or drag to resize

ISynchronizationCollectionGetChangesAsync Method

IT Hit WebDAV Classes Reference
Returns a list of changes that correspond to a synchronization request.

Namespace:  ITHit.WebDAV.Server.Synchronization
Assembly:  ITHit.WebDAV.Server (in ITHit.WebDAV.Server.dll) Version: 13.3.13068
Syntax
Task<DavChanges> GetChangesAsync(
	IList<PropertyName> propNames,
	string syncToken,
	bool deep,
	Nullable<long> limit = null
)

Parameters

propNames
Type: System.Collections.GenericIListPropertyName
List of properties requested by the client.
syncToken
Type: SystemString
The synchronization token provided by the server and returned by the client. This method must return items that changed since this token was retuned by the server. This parameter is null or empty in case of full synchronization.
deep
Type: SystemBoolean
Indicates the "scope" of the synchronization report request, false - immediate children and true - all children at any depth.
limit (Optional)
Type: SystemNullableInt64
The number of items to return. Null in case of no limit.

Return Value

Type: TaskDavChanges
List of changes that that happened since the synchronization token provided.
Examples

The code below is part of 'WebDAVServer.FileSystemSynchronization.AspNetCore' C# & VB samples provided with the SDK.

C#
public async Task<DavChanges> GetChangesAsync(IList<PropertyName> propNames, string syncToken, bool deep, long? limit = null)
{
    // In this sample we use item's USN as a sync token.
    // USN increases on every item update, move, creation and deletion. 

    DavChanges changes = new DavChanges();
    long syncId = string.IsNullOrEmpty(syncToken) ? 0 : long.Parse(syncToken);

    // Get all file system entries with usn.
    List<(IChangedItem HierarchyItem, long SyncId)> childrenList = new List<(IChangedItem HierarchyItem, long SyncId)>();
    foreach ((string Path, long SyncId) item in await GetSyncIdsAsync(syncId, deep))
    {
        IChangedItem child = (IChangedItem)await GetChildAsync(item.Path);

        if (child != null)
        {
            childrenList.Add((child, item.SyncId));
        }
    }

    // If limit==0 this is a sync-token request, no need to return any changes.
    bool isSyncTokenRequest = limit.HasValue && limit.Value == 0;
    if (isSyncTokenRequest)
    {
        changes.NewSyncToken = childrenList.Max(p => p.SyncId).ToString();
        return changes;
    }

    IEnumerable<(IChangedItem HierarchyItem, long SyncId)> children = childrenList;

    // If syncId == 0 this is a full sync request.
    // We do not want to return deleted items in this case, removing them from the list.
    if (syncId == 0)
    {
        children = children.Where(item => item.HierarchyItem.ChangeType != Change.Deleted);
    }

    // Truncate results if limit is specified.
    if (limit.HasValue)
    {
        // Order children by sync ID, so we can truncate results.
        children = children.OrderBy(p => p.SyncId);

        // Truncate results.
        children = children.Take((int)limit.Value);

        // Specify if more changes can be returned.
        changes.MoreResults = limit.Value < childrenList.Count;
    }

    // Return new sync token.
    changes.NewSyncToken = children.Count() != 0 ? children.Max(p => p.SyncId).ToString() : syncToken;

    // Return changes.
    changes.AddRange(children.Select(p => p.HierarchyItem));

    return changes;
}

private async Task<IHierarchyItem> GetChildAsync(string childPath)
{
    string childRelPath = childPath.Substring(dirInfo.FullName.Length).Replace(System.IO.Path.DirectorySeparatorChar.ToString(), "/").TrimStart('/');
    IEnumerable<string> encodedParts = childRelPath.Split('/').Select(EncodeUtil.EncodeUrlPart);
    string childRelUrl = Path.TrimEnd('/') + "/" + string.Join("/", encodedParts);

    DavHierarchyItem child = await DavFolder.GetFolderAsync(context, childRelUrl);
    if (child == null)
    {
        child = await DavFile.GetFileAsync(context, childRelUrl);
    }

    return child;
}

private async Task<IEnumerable<(string Path, long SyncId)>> GetSyncIdsAsync(long minSyncId, bool deep)
{
    // First we must read max existing USN. However, in this sample,
    // for the sake of simplicity, we just read all changes under this folder.

    SearchOption searchOptions = deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

    ConcurrentBag<(string Path, long Usn)> list = new ConcurrentBag<(string Path, long Usn)>();

    string[] decendants = Directory.GetFileSystemEntries(dirInfo.FullName, "*", searchOptions);
    ParallelOptions parallelOptions = new()
    {
        MaxDegreeOfParallelism = 1000
    };
    await Parallel.ForEachAsync(decendants, parallelOptions, async (path, token) =>
    {
        long syncId = await new FileSystemItem(path).GetUsnAsync();
        if (syncId > minSyncId)
        {
            list.Add(new(path, syncId));
        }
    });

    return list;
}
See Also