ServiceStack/ServiceStack — pull request #624 by bchavez (2013-04-24 01:23:09+00:00)
Title: Re-implementation of Razor2 engine based on RazorGenerator

Very crude / basic implementation of new Razor2 engine.

## An attempt at a better Razor engine

### Basic Overview

At a high level, there are 3 major parts to this new Razor2 sub system.

* The `ViewManager` - Responsible for maintaining and keeping the state of Razor pages.
* The `BuildManager` - Responsible for compiling Razor pages.
* The `PageResolver` - Responsible for resolving requests to Razor pages.

### In Detail

#### The `ViewManager`

The `ViewManager` is located in `Razor2\Managers\ViewManager.cs` and is initialized by the `RazorFormat` plugin. `ViewManager` is the first manager to be initialized.

The initialization process starts off by `ScanForRazorPages()` which collects all the `.cshtml` files in the project directory and builds a model around these `.cshtml` pages.

This model of a `.cshtml` page is represented by `RazorPage`. For every `.cshtml` page found in the project directory, a `RazorPage` is instantiated, and placed into a `Pages` field of type `Dictionary`. The **key** in `Pages` is the relative path to a `.cshtml` file, (ex: `/foo/bar.cshtml` or `/default.cshtml`).

The `RazorPage` has the following structure:
```cs
public class RazorPage
{
public RazorPage()
{
this.IsValid = false;
}

public RazorPageHost PageHost { get; set; }

public bool IsValid { get; set; }

public IVirtualFile File { get; set; }

public Type PageType { get; set; }
public Type ModelType { get; set; }

public virtual HttpCompileException CompileException { get; set; }

public RenderingPage ActivateInstance()
{
return Activator.CreateInstance( this.PageType ) as RenderingPage;
}
}
```

The most important of these properties/fields are:
* `IsValid` - Used to determine if a `RazorPage` needs recompilation or not.
* `PageType` - The resultant CLR type of a `.cshtml` razor page which is output from the dynamic assembly generated by **RazorGenerator**. PageTypes are meant to be instantiated upon every request. If a page hasn't been compiled, `PageType` is `null`.
* `CompileException` - For future use - will contain the compile exception if a parallel thread performs the compilation. This property is likely to be checked before serving the razor page incase any recent edits to a razor page caused a compilation error.
* `PageHost` - The most important - is responsible compiling `.cshtml` pages (more on this later).

The responsibility of `ViewManager` is to keep page edits **in-sync** with the compiled type. It does this by using a `FileSystemWatcher` which checks for recent edits to `.cshtml` pages. If a recent edit to a razor page is made, the file `FileSystemWatcher` invalidates the `RazorPage.IsValid = false` so that upon the next request for that page, the page gets recompiled and re-synced with the contents on disk.

Using a `FileSystemWatcher` avoids the whole problem of having razor pages get out of sync with the compiled CLR type of the razor page. AFAIK, (correct me if I'm wrong) this is reinventing ASP.NET's `BuildManager` which is suppose to do exactly this job, but since @mythz wants to do parallel compilation, we'd need to write our own infrastructure to manage page state to support parallel compilation.

#### The `BuildManager`

The `BuildManager`'s sole job is to compile `.cshtml`. It does so by pulling out the `RazorPage.PageHost` property, and calling `Compile()` on it and a type is returned. The returned type is the resultant CRL type output from the RazorEngine parsing the .cshtml file.

The **RazorPageHost** is a model of the comple engine in **RazorGenerator**.

A key concept in **RazorGenerator** is a **CodeTransformer**. A **CodeTransformer** acts as a small *contributor* to modifying the output of the source code by the razor engine. For example, `Razor2\Compilation\CodeTransformers\MakeTypePartial.cs` is a transformer that transforms output of the razor engine's class to a partial type instead of a regular type.

Example (before and after) a code transformer is applied:
```cs
//before
public class MyRazorPageGeneratedByCshtml{

}
//after MakeTypePartal is applied
public partial MyRazorPageGeneratedByCshtml{

}
```

An `AggregateCodeTransformer` is exactly that, an aggregate list of possible code transformations on the output of the Razor Engine. Specifically, `RazorViewPageTransformer` is a type of `AggregateCodeTransformer` that contains a list of code transformations that include:

```cs
public class RazorViewPageTransformer : AggregateCodeTransformer{
codeTransformers = new List
{
//1. adding a GenratedClassAttriubte to
// the final C# razor class
new AddGeneratedClassAttribute(),

//2. adding a PageVirutalPath attribute to the final compiled class
//which could be used in pre-compiled views to determine the
//path location of a compiled content .cshmtl page.
new AddPageVirtualPathAttribute(),

//3. Allows you to set the import namespaces of the razor page
new SetImports( namespaces, replaceExisting: false ),

//4. Infrastucutre
new RemoveLineHiddenPragmas(),

//5. Makes resultant CLR types partial.
new MakeTypePartial(),

//6. Reads the WebConfig for the base class page type.
new WebConfigTransformer()
};
}
```

I hope that makes some kind of sense. If not, let me know and I'll be glad to explain more. Basically, code transformers modify the output of the razor engine in their *own little bit* way, and you can stack a list of transforms to be applied to the output before it's compiled. Check the `Razor2\CodeTransformers` directory for a list of all the possible code transformations that can be applied to the output of the razor engine.

#### The `PageResolver`

Last but not least, the `PageResolver` is responsible for "hooking into" service stack and translating requests into pages and ultimately executing those pages.

I tried to localize all that handling code in one central place. Nothing special here, and should be straightforward.

### Other notes

Each subsystem, `ViewManager`, `BuildManager` and `PageResolver` has their own dedicated config. One thing I disliked about ServiceStack was the endless plethora of config settings in `EndpointHostConfig` where config details of each subsystem kinda *ran into* each other almost like a big traffic jam of config stuff.

(Ex: `EndpointHostConfig.WsdlSoapActionNamespace` and `EndpointHostConfig.RazorNamespaces` dont have much in common).

In an effort to move away from that, I've logicially seperated each config into `ViewConfig`, `BuildConfig`, and `PageResolverConfig`.

#### New tests\ServiceStack.Razor2.Tests project added

**This is the project I used to test against.** `RazorRockstars.Web` was doing too much for my new razor engine so I had to create a stripped down version of a basic razor website to incrementally get things working. Please view this project on `http://localhost:8181` and feel free to get a gist of how things work.

As indicated in #593 (repeated here for convenience):
Here's what works:

* Basic standalone **Content** pages - just slap a .cshtml page in the project directory, and we'll pick it up.
* Basic **Response / View** pages - ~/Views/ResponseDTO.cshtml and ~Views/ReqeustDTO.cshtml
* Better directory / file monitoring with FileSystemWatcher (in an effort to reduce IIS process killing on every razor page edit)
* A step toward simplification - common sense maintainable code.

What doesn't work yet:
* Layout pages
* Partials / sections
* Parallel thread compilation
* Wiring up of SS specific IRedis, IDb interfaces that were bolted onto base razor ViewPages.

You might not be happy with my implementation since I've imposed a few breaking changes:

Here's what's changed:

* **Removed**: Support for Multiple Razor Extensions --
* Previously, ServiceStack supported multiple extension/base class support. For example, *.cshtml and *.rzr files. I really couldn't find a compelling reason to keep this support around. Here are my reasons:

* It adds complexity to the code base.
* You can use `@inherits` in `*.cshtml` files to inherit different base class pages. IMO, I don't think it's worth the effort and added complexity of handling multiple razor extensions and associated base classes. I think it's best if people want to use different base page they should use `@inherits`.
* Another reason for removing multiple extensions is: dealing with multiple default pages. Which one do you chose? `default.cshtml` or `default.rzr`. Again, it complicates things.
* In an effort to keep it simple and reduce *automagicness* I've decided to just deal with `default.cshtml`

Please let me know your comments, and feel free to criticize, critique, or reject this pull request.

Brian