Skip to main content

Note: This post is part of a series and you can find the rest of the parts in the series index.

An important class in your implementation is your item implementation, which is used to identify what an item (be that file, directory, list item, bug etc…) is. This class needs to implement IMigrationItem. This class is more important for VC than WIT but in both cases you need to put in all the properties you need to know about. You must also make sure that all properties are serialisable by the default XML serialiser in .NET.

Power Tip: VC stands for Version Control. This refers to an adapter that works with the source control aspects of the system. WI, work items, and WIT, work item tracking, are the same thing. File attachments in WI are NOT regarded as VC and must be handled by your WI adapter.

IMigrationItem

WIT

The WIT adapter is slightly smaller than the VC one since it is just a set of properties. I do not care too much about the inherited DisplayName property here either and the Download method is just logging. The only interesting part is the SimpleDictionary<T,V> I use. SimpleDictionary<T,V> is just to store Key/Value pairs of column information from SharePoint (because you may have customised the columns in SharePoint, I cannot hard code them). The reason I use this rather the Dictionary<T,V> which .NET provides, is because that class is not able to be serialised using the default XML serialiser. I will cover SimpleDictionary<T,V> in a later post.

public class SharePointListItem : IMigrationItem
{
    public SharePointListItem()
    {
        this.Columns = new SimpleDictionary<string, object>();
    }

    public string Id { get; set; }
    public DateTime ModifiedOn { get; set; }
    public string AuthorId { get; set; }
    public SimpleDictionary<string, object> Columns { get; set; }    
    public string DisplayName { get; set; }

    public void Download(string localPath)
    {
        TraceManager.TraceInformation("WSSWIT:MI:Download - {0}", localPath);
    }
}

VC

The VC adapter is a little more complex with more properties but the key difference is the DisplayName property, which I return the filename. Even more important though it the Download method which is used to get the actual file or folder to a specific location, provided by the localpath parameter, on disk for the platform to use.

Power Tip: When you are downloading files in the IMigrationItem, you are responsible for the creation of the path too. So make sure you are creating directories and also checking what directories exist too.

string IMigrationItem.DisplayName
{
    get { return Filename; }
}

void IMigrationItem.Download(string localPath)
{
    TraceManager.TraceInformation("WSSVC:Item:Download:From {0} to {1}", this.AbsoluteURL, localPath);
    if (this.ItemType == SharePointItemType.File)
    {
        TraceManager.TraceInformation("\tType is file");
        string targetDir = Path.GetDirectoryName(localPath);
        if (!Directory.Exists(targetDir))
        {
            TraceManager.TraceInformation("\tCreating Directory for file - {0}", targetDir);
            Directory.CreateDirectory(targetDir);
        }

        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(this.AbsoluteURL);
        webRequest.Credentials = this.Credentials;
        using (Stream responseStream = webRequest.GetResponse().GetResponseStream())
        {
            using (FileStream fileStream = new FileStream(localPath, FileMode.CreateNew, FileAccess.ReadWrite))
            {
                byte[] buffer = new byte[1024];
                int bytesRead;
                do
                {
                    // Read data (up to 1k) from the stream
                    bytesRead = responseStream.Read(buffer, 0, buffer.Length);

                    // Write the data to the local file
                    fileStream.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);
            }
        }

        TraceManager.TraceInformation("\tFile downloaded successfully");
    }

    if (this.ItemType == SharePointItemType.Directory)
    {
        TraceManager.TraceInformation("\tType is directory");
        if (!Directory.Exists(localPath))
        {
            TraceManager.TraceInformation("\tCreating Directory - {0}", localPath);
            Directory.CreateDirectory(localPath);
        }
    }
}

IMigrationItemSerializer

The migration item serializer is just a way to get your item to XML using .NET serialisation. My implementation of this is the exact same for both VC and WIT.

public class SharePointWITMigrationItemSerializer : IMigrationItemSerializer
{
    public IMigrationItem LoadItem(string itemBlob, ChangeGroupManager manager)
    {
        TraceManager.TraceInformation("WSSWIT:S:LoadItem");
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }

        if (string.IsNullOrEmpty(itemBlob))
        {
            throw new ArgumentNullException("itemBlob");
        }

        XmlSerializer serializer = new XmlSerializer(typeof(SharePointListItem));

        using (StringReader itemBlobStringReader = new StringReader(itemBlob))
        {
            using (XmlReader itemBlobXmlReader = XmlReader.Create(itemBlobStringReader))
            {
                return (SharePointListItem)serializer.Deserialize(itemBlobXmlReader);
            }
        }
    }

    public string SerializeItem(IMigrationItem item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }

        TraceManager.TraceInformation("WSSWIT:S:SerializeItem - {0}", item.DisplayName);

        XmlSerializer sharePointTaskSerializer = new XmlSerializer(item.GetType());

        using (MemoryStream memoryStream = new MemoryStream())
        {
            sharePointTaskSerializer.Serialize(memoryStream, item);
            memoryStream.Seek(0, SeekOrigin.Begin);
            using (StreamReader streamReader = new StreamReader(memoryStream))
            {
                return streamReader.ReadToEnd();
            }
        }
    }
}