Moving C1 to Azure (File I/O)

by ingvar 17. november 2010 12:33

This blog post focus on file I/O when moving a complex solution to the Azure platform.

Composite C1 is a state of the art ASP.NET 4.0 CMS and is currently not 100% Azure ready. There is a lot of things to consider when moving a really complex product like Composite C1 to the Azure platform. For a full description on all the problems we havae moving C1 to Azure and all the people that attended a three day workshop, see this very descriptive blog post by Marcus Wendt.

Brainstorm and one-to-one implementation

First we sat down a listed all the classes we knew did file I/O. Some of the classes that we found were StreamReader, StreamWriter, TextReader and XmlReader. We also found some methods:XElement.Load, XElement.Save, XDocument.Load and XDocument.Save.

After we completed this list, we created one-to-one implementations of the found classes. These classes had the all same methods as the original classes and contained one private field of the original class type. The methods mapped one-to-one to the original class. Here is a simple example:


[Serializable]
public class C1StreamWriter : TextWriter, IDisposable
{
    private StreamWriter streamWriter;

    public C1StreamWriter(string path)
    {
        streamWriter = new StreamWriter(path);
    }


    public override void Write(char value)
    {
        streamWriter.Write(value);
    }

    /* And all the other constructors and methods */
}

Then we did a search'n'replace on the whole solution to insert our classes instead of the originals. We also created extension methods to replace the found methods. Next task was to see if we had found ALL the classes/methods that did file I/O. Which proved to be a rather had task.

IntelliTrace

To see if we had found all the classes, we used the new feature in Visual Studio 2010, IntelliTrace. We enabled IntelliTrace with only the File events marked and then started C1 in debug mode. This quickly showed that we had missed several classes and methods. One-to-one implementations of these newly found classes were made and IntelliTrace was fired up again.

After some time doing this, it became rather time consuming and error prone looking through several thousands stack traces of file I/O events. So I started looking at the IntelliTrace API and developed a tool, that we could use to filter the events in an intelligent way. See my blog post on how to get started with the IntelliTrace API.

FxCop

After doing all this work on finding all file I/O’s it would be nice to have a way of ensuring that no C1 developer by mistake used one of the, now forbidden, classes or methods. Also, it would be nice to have something to give all developer using C1, so they could do their developing knowing that their code also would be Azure ready. FxCop and custom rules to the rescue! See my blog post on how to make custom FxCop rules.

Findings

So which classes and methods did we find? And how hard was it to exchange them with our own classes and methods. To answer these questions I will group them into 4 groups: Static classes, non-static classes, methods and configuration classes. In the following I'll describe in more detail about the findings in each group.

Static classes

The found classes in this group were:

  • System.IO.File
  • System.IO.Directory

These were really easy to create our own implementation off. Just create a static class and do a one-to-one mapping of all methods.

Non.static classes

The found classes in this group were:

  • System.IO.StreamReader (Disposable)
  • System.IO.StreamWriter (Disposable)
  • System.IO.FileStream (Disposable)
  • System.IO.FileSystemWatcher
  • System.IO.FileInfo
  • System.IO.DirectoryInfo

These were also really easy to create our own implementation off. One important thing to remember here is to implement the dispose method the right way. Otherwise there will be problems with writing to unclosed files etc. Here is an example of doing it the right way:

~FileStream()
{
    Dispose(false);
}

protected overwrite void Dispose(bool disposing)
{
    if (disposing)
    {
        originalClass.Dispose();
    }
}

Methods

The found methods in this group were:

  • System.Xml.Linq.XDocument.Load (Local and remote)
  • System.Xml.Linq.XDocument.Save
  • System.Xml.Linq.XElement.Load (Local and remote)
  • System.Xml.Linq.XElement.Save
  • System.Xml.XmlReader.Create
  • System.Xml.XmlWriter.Create
  • System.Xml.XmlSchemaSet.Add
  • System.Xml.Xsl.XslCompiledTransform.Load

Here we simply created new static methods, some of them, extension methods and used a stream approach rather than using a path/uri approach. So instead of passing a path-string to XmlReader.Create we passed our own implementation of System.IO.FileStream to the XmlReader.Create method.


Special care had to be taken when making new methods for XDocument and XElement because their string version of the Load method can also fetch a file over the network. Here we had to look at the inputUri string and see if it was a local file (Use our own FileStream implementation) or remote (Use WebRequest to fetch the file).

Configuration classes

Found classes in this group were:

  • System.Configuration.Configuration
  • System.Configuration.ExeConfigurationFileMap (The Load method)

These classes/methods was the hardest ones. There was no way of replacing their file I/O functionality in a nice way, like we did with the other classes. So in this case we had to accept some local file I/O. But what about the Azure platform and shared configuration file across multiple instances etc? One way of solving this is to have some hooks on when a configuration file is loaded and saved. In this way we could 'fetch' a configuration file on load and 'store' it on save. Here is the full implementation of a new Configuration class that solves this:

public class C1Configuration
{
    Configuration _configuration;

    public static C1Configuration Load(string path)
    {            
        ExeConfigurationFileMap map = new ExeConfigurationFileMap();
        map.ExeConfigFilename = path;
        Configuration configuration =
            ConfigurationManager.
            OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);            

        return new C1Configuration(configuration);
    }

    protected Configuration(Configuration configuration)
    {
        _configuration = configuration;
    }

    public ConfigurationSectionCollection Sections
    {
        get
        {
            return _configuration.Sections;
        }
    }

    public ConfigurationSection GetSection(string sectionName)
    {
        return _configuration.GetSection(sectionName);
    }

    public void Save()
    {
        _configuration.Save();
    }
}

API and plugin architecture

Next step was to create an API for C1 developer to use when doing file I/O. And a plugin architecture so that we could make C1 run on a local IIS, on the Azure platform or possible other platforms. The API is for most parts the same as the API for the original classes and methods, so this was just simple make-it-so work. C1 uses Microsoft Enterprise library (Object build) as plugin architecture. So this work was also pretty straight forward. At the moment we are not done with this work but when we are done, I'll post at link to the API.

Final thoughts

At the moment we have not created a Azure implementation of our file I/O plugin. This implementation will use the blob storage for keeping the files. So in the near future I'll post how it went with the Azure implementation.

Also still missing is the ASP.NET/Webserver file I/O part. This can be resolved by using Virtualizing Access to Content. Another solution could be: Having the website files locally and do some kind of synchronization if files are added/updated/deleted. This synchronization is possible through our new file I/O abstraction layer and can be implemented in the Azure implementation. This synchronization could also be used to solve the System.Configuration.Configuration problem.

Stay tuned for details regarding the Azure implementation and other cool stuff!

Tags:

.NET | C# | C1

Comments (2) -

Mark Groves
Mark Groves United States
18-11-2010 00:47:18 #

Very cool.  You may have been able to create a Layer diagram to ensure the team does not take a dependency on those classes/methods.  It may be faster then writing a FXCop rule.  

ingvar
ingvar Denmark
18-11-2010 08:20:10 #

@Mark Groves: Thanks for the input! Ill have a close look at Layer diagram in the near future.

About the author

Martin Ingvar Kofoed Jensen

Architect and Senior Developer at Composite on the open source project Composite C1 - C#/4.0, LINQ, Azure, Parallel and much more!

Follow me on Twitter

Read more about me here.

Read press and buzz about my work and me here.

Stack Overflow

Month List