Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Reflection Studio - Part 1 - Introduction: Architecture and Design

0.00/5 (No votes)
22 Sep 2010 2  
Reflection Studio is a developer application for assembly, database, performance, and code generation, written in C# under WPF 4.0.

Introduction

In my last project, I experienced that there is no software to test performance on mobiles for NET applications. The ones working on WinForms or ASP.NET sites have a high performance cost because they hook everything they can. So I decided to try and create a new one based on injection... but Reflection Studio quickly becomes a bit more. It's a programmer tool around all my work with Reflection, database, code generation, WPF, and so on.

Reflection Studio is hosted on http://reflectionstudio.codeplex.com/. It is completely written in C# under the .NET/WPF platform. I recently moved to Visual Studio 2010 and NET 4. Please have a look at the CodePlex project because it is huge to describe everything in detail. Given below is a screenshot of the application:

ReflectionStudio

Contents

As the subject is quite big, I will (try to) write this article in several parts:

Part 1 - Architecture and Design

1.1 The Modules

Reflection Studio contains several core modules:

  • Assembly parsing as a base for performance injection and to make diagrams
  • Performance injection to get traces back from the execution of any NET assemblies, etc.
  • Code generation from templates and database, etc.
  • Database schema discovering, query, quality control, diagrams, etc.

1.2 Solution and Assemblies

As a short introduction to the project, below is the assembly diagram, with the project assemblies in blue:

The external ones:

  • Moco.Cecil from Mono project - for assembly injection
  • WPFToolkit from CodePlex - for more controls
  • Fluent from CodePlex - excellent ribbon control library
  • AvalonDock from CodePlex - get Visual Studio like interface with docking, panels, toolbox, and tabbed documents
  • ICSharpCode.AvalonEdit - a highlighted syntax editor in WPF
  • WPFVisifire.Charts from CodePlex - for chart creation
  • ...

Internally, we have these main assemblies:

  • ReflectionStudio
    • The main UI with everything specific to it
  • ReflectionStudio.Controls
    • All generic UI related elements
  • ReflectionStudio.Core
    • All business/core and helpers not related to UI or controls
  • ReflectionStudio.Core.Database.xxxxProvider
    • Plug-in assembly for SQL schema discovery and more
  • ReflectionStudio.Spy
    • The runtime that makes performance traces
  • ReflectionStudio.Tools
    • It's a utility for testing skin, colors, and controls

Class Dependencies

The whole class dependency diagram generated by Visual Studio is quiet unreadable, but it shows the starting complexity of the project:

To resume, the architecture is quiet simple. I avoid complex patterns, and classes/functions are always constructed on the same principle: simple as possible, with exception and trace management. You will see in detail in the other part of this article some "touchy" points like the database providers, assembly parser, injection, or the code generation engine.

1.3 Tracing and Events

The idea was to develop a tool that suits the developer and me during development, but also a good way to update the interface and share events between all components. I don't like to write basic trace in my code, because it's useful when a problem that is not easy occurs but is hard to exploit... so I decided my traces need to be visible in the user interface. It is based on the following model.

1.3.1 Model: EventDispatcher and Tracer

trace event model

1.3.2 Tracer Class

Allows to trace all development steps through classical functions like in the Microsoft trace or debug system: Error, Info, Verbose. I generally use a template like the following, and it's very useful when writing code and testing, as you can see the trace directly in the UI:

public bool Open( string fileName )
{
    Tracer.Verbose("ProjectService:Open", "START");

    try
    {
        Current = new ProjectEntity(fileName);
        return LoadProject();
    }
    catch (Exception err)
    {
        Tracer.Error("ProjectService.Open", err);
        return false;
    }
    finally
    {
        Tracer.Verbose("ProjectService:Open", "END");
    }
}

The main function of the Tracer class creates a message info, sends it to the Microsoft Trace system (if configured), and then sends it as a message (if it matches the workspace settings).

private static void Send(MessageEventType typ, string from, string message)
{
    MessageInfo info = new MessageInfo(typ, from, message);

    //trace is always used for all levels and can be de-activated in config
    Trace.TraceInformation(info.ToString());

    //send message only if trace level is under from level toolbox and settings
    if (info.Type <= WorkspaceService.Instance.Entity.LogLevel)
        EventDispatcher.Instance.RaiseMessage(info);
}

1.3.3 Configuration

I use a function like the next one to configure the Microsoft trace system. This allows me to delete the log on each start and to activate it depending on the software settings - call it in AppLoad:

private void TraceConfiguration()
{
    try
    {
        //delete the old log file
        string logPath = Path.Combine(PathHelper.ApplicationPath, 
                                      "ReflectionStudio.exe.log");
        if (File.Exists(logPath))
            File.Delete(logPath);

        if (ReflectionStudio.Properties.Settings.Default.UseTraceListener)
        {
            //configure the trace
            System.Diagnostics.Trace.AutoFlush = true;
            System.Diagnostics.Trace.IndentSize = 2;

            //configure the text listener
            System.Diagnostics.TraceListenerCollection listeners = 
                               System.Diagnostics.Trace.Listeners;
            listeners.Add(new 
              System.Diagnostics.TextWriterTraceListener(logPath, "LOG"));
        }
    }
    catch (Exception error)
    {
        Tracer.Error("Reflection Studio.TraceConfiguration", error);
    }
}

1.3.4 EventDispatcher Class

This one serves in the background to the Tracer class with the basic messaging system. It can raise three types of events (all of them send a message as the base information):

  1. Message: Pure text information, completed with Type, Where, and When, ....
  2. Status: With start or stop, allows to update the progress in the status bar
  3. Project: More customized and business oriented, allows to signal events like opened, closed, saved...

Here is an example of usage to fire project and status event by using the EventDispatcher class, but also used by the Tracer in try/catch management.

private bool LoadProject()
{
    try
    {
        EventDispatcher.Instance.RaiseStatus(
           Resources.CORE_LOADING, StatusEventType.StartProgress);

        EventDispatcher.Instance.RaiseProject(Current, ProjectEventType.Opening);

        Current = new ProjectDAC().Load(Current.ProjectFilePath);

        if (Current != null)
        {
            //refresh by assembly parsing
            Refresh();

            EventDispatcher.Instance.RaiseProject(Current, ProjectEventType.Opened);
            return true;
        }
        else
        {
            EventDispatcher.Instance.RaiseStatus(Resources.CORE_LOADING_ERROR, 
                                                 StatusEventType.StopProgress);
            return false;
        }
    }
    catch (Exception err)
    {
        Tracer.Error("ProjectService.LoadProject", err);
        return false;
    }
    finally
    {
        EventDispatcher.Instance.RaiseStatus(Resources.CORE_PROJECT_LOADED, 
                                 StatusEventType.StopProgress);
    }
}

1.3.5 The Log Toolbox

To complete all this, I had to write a control to display this in the UI. I do not invent the wheel and take Visual Studio as a good sample. So I create a LogToolBox user control plugged in an Avalon content. It was nice to have a converter to change the message type into an error icon!

Log

1.3.6 Connecting

And for all this to work, you just have to plug the event handlers all together like I do in the main window creation:

//catch events
EventDispatcher.Instance.OnProjectChange += 
   new EventHandler<ProjectEventArgs>(OnProjectChange);
EventDispatcher.Instance.OnStatusChange += 
   new EventHandler<StatusEventArgs>(this.MainStatusBar.OnStatusChange);

//forward also all changes to the log toolbox
EventDispatcher.Instance.OnMessage += 
  new EventHandler<MessageEventArgs>(this.LogToolbox.OnMessage);

1.4 Helpers

1.4.1 Serializing

The core assembly contains a helper class to serialize or deserialize any type of object to binary or XML format. It is as simple as:

//Deserialize
entity = (WorkspaceEntity)SerializeHelper.Deserialize(WorkspaceFile, 
          typeof(WorkspaceEntity), false);

//Serialize
result = SerializeHelper.Serialize(WorkspaceFile, entity, false);

1.4.2 Various Helpers

The core assembly also contains:

  • UrlSave and AsyncUrlSave to save the response of any HTTP GET
  • ByteHelper and CecilHelper
  • ResourceHelper: Read embedded resources
  • ProcessHelper: Launch navigator for a web URI
  • WorkerBase to manage a BackgroundWorker for multithreading

1.5 Documentation

All the code need to be documented in English as I activate the XML documentation. This allows Sandcastle and Sandcastle Help File Builder to generate the technical project help file in the new 2010 Help Viewer format. This is available in the source, and can be generated and installed by building RSHelp.shfbproj in the solution folder <ReflectionStudio\Documents\Help>. As it is too big, it will not be included in the source code or release.

Help

Additionally, I try to keep up to date the user manual and technical documentation for each release. You can find them in the <Help> folder as a doc or xps document, or through the application in the main ribbon tab that displays the help document illustrated below:

Help

1.6 Installation

The setup and related project have been created since beta 0.2. But we can have lot of changes; please do not forget to remove any previous installation, and we do not guarantee the project reversibility between versions before first release.

Everything is installed in the Reflection Studio folder. There are no specific needs and no modifications needed on the computer.

Conclusion / Feedback

See you in the next article of this series. Do not hesitate to give me feedback about it either on CodePlex or CodeProject. As the team is growing, I hope that we are getting faster, and do not hesitate to join us!

Article / Software History

  1. Initial release - Version BETA 0.2
    • Initial version that contains "nearly" everything, and is available for download on Part 1 or CodePlex
  2. Version BETA 0.3
    • Update on skin/color management and the Database module
    • Part 4 - Database module - is published

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here