Skip to content

Commit

Permalink
update docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
matthid committed May 1, 2015
1 parent d5c5465 commit 19a51ac
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 148 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -120,7 +120,7 @@ The API is designed around the idea that you do not have the templates sitting a
(while you can do that as seen above).
The main interface to provide RazorEngine with templates is the `ITemplateManager` interface.
You should either pick one of the available implementations or write your own.
See [the documentation](http://antaris.github.io/RazorEngine/TemplateManager.html) for details
See [TemplateManager and Caching documentation](http://antaris.github.io/RazorEngine/TemplateManager.html) for details.

## Temporary files

Expand Down
136 changes: 1 addition & 135 deletions doc/Caching.md
@@ -1,138 +1,4 @@

# RazorEngine Caching API

RazorEngine provides a robust caching layer out of the box which should suite most use-cases.
If you use "Compile" on application startup and "Run" on every use of the application you can be
sure that caching works (as "Run" would throw if there is no cached template).
If you want "lazy"-compilation of the templates you would just use "RunCompile".

However if you need some custom features (like caching across multiple runs of the application, or cleanup of compiled templates)
you want to run your own caching implementation.
All you need to do is implement the ICachingProvider and set an instance of your implementation in the configuration.
As starting point you can use the DefaultCachingProvider (latest code is in the repository):

```csharp
config.CachingProvider = new DefaultCachingProvider();

/// <summary>
/// The default caching provider (See <see cref="ICachingProvider"/>).
/// This implementation does a very simple in-memory caching.
/// It can handle when the same template is used with multiple model-types.
/// </summary>
public class DefaultCachingProvider : ICachingProvider
{
private readonly ConcurrentDictionary<string, ConcurrentDictionary<Type, ICompiledTemplate>> _cache =
new ConcurrentDictionary<string, ConcurrentDictionary<Type, ICompiledTemplate>>();

private readonly TypeLoader _loader;
private readonly ConcurrentBag<Assembly> _assemblies = new ConcurrentBag<Assembly>();

/// <summary>
/// Initializes a new instance of the <see cref="DefaultCachingProvider"/> class.
/// </summary>
public DefaultCachingProvider()
{
_loader = new TypeLoader(AppDomain.CurrentDomain, _assemblies);
}

/// <summary>
/// The manages <see cref="TypeLoader"/>. See <see cref="ICachingProvider.TypeLoader"/>
/// </summary>
public TypeLoader TypeLoader
{
get
{
return _loader;
}
}

/// <summary>
/// Get the key used within a dictionary for a modelType.
/// </summary>
public static Type GetModelTypeKey(Type modelType)
{
if (modelType == null ||
typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(modelType))
{
return typeof(System.Dynamic.DynamicObject);
}
return modelType;
}

private void CacheTemplateHelper(ICompiledTemplate template, ITemplateKey templateKey, Type modelTypeKey)
{
var uniqueKey = templateKey.GetUniqueKeyString();
_cache.AddOrUpdate(uniqueKey, key =>
{
// new item added
_assemblies.Add(template.TemplateAssembly);
var dict = new ConcurrentDictionary<Type, ICompiledTemplate>();
dict.AddOrUpdate(modelTypeKey, template, (t, old) => template);
return dict;
}, (key, dict) =>
{
dict.AddOrUpdate(modelTypeKey, t =>
{
// new item added (template was not compiled with the given type).
_assemblies.Add(template.TemplateAssembly);
return template;
}, (t, old) =>
{
// item was already added before
return template;
});
return dict;
});
}

/// <summary>
/// Caches a template. See <see cref="ICachingProvider.CacheTemplate"/>.
/// </summary>
/// <param name="template"></param>
/// <param name="templateKey"></param>
public void CacheTemplate(ICompiledTemplate template, ITemplateKey templateKey)
{
var modelTypeKey = GetModelTypeKey(template.ModelType);
CacheTemplateHelper(template, templateKey, modelTypeKey);
var typeArgs = template.TemplateType.BaseType.GetGenericArguments();
if (typeArgs.Length > 0)
{
var alternativeKey = GetModelTypeKey(typeArgs[0]);
if (alternativeKey != modelTypeKey)
{
// could be a template with an @model directive.
CacheTemplateHelper(template, templateKey, typeArgs[0]);
}
}
}

/// <summary>
/// Try to retrieve a template from the cache. See <see cref="ICachingProvider.TryRetrieveTemplate"/>.
/// </summary>
/// <param name="templateKey"></param>
/// <param name="modelType"></param>
/// <param name="compiledTemplate"></param>
/// <returns></returns>
public bool TryRetrieveTemplate(ITemplateKey templateKey, Type modelType, out ICompiledTemplate compiledTemplate)
{
compiledTemplate = null;
var uniqueKey = templateKey.GetUniqueKeyString();
var modelTypeKey = GetModelTypeKey(modelType);
ConcurrentDictionary<Type, ICompiledTemplate> dict;
if (!_cache.TryGetValue(uniqueKey, out dict))
{
return false;
}
return dict.TryGetValue(modelTypeKey, out compiledTemplate);
}

/// <summary>
/// Dispose the instance.
/// </summary>
public void Dispose()
{
_loader.Dispose();
}
}
```

Moved to [TemplateManager.html](http://antaris.github.io/RazorEngine/TemplateManager.html)
168 changes: 157 additions & 11 deletions doc/TemplateManager.md
@@ -1,6 +1,7 @@
# `TemplateManager` and `ICachingProvider`
# ITemplateManager and ICachingProvider

This section explains how `TemplateManager` work, which ones are available by default and how to write your own.
This section explains how `ITemplateManager` and `ICachingProvider` work and play together,
which implementations are available by default and how to write your own.

## Template resolving

Expand Down Expand Up @@ -33,12 +34,13 @@ The `GetKey` step enables a `TemplateManager` to add customized data to the key
Note that this introduces a memory leak to your application, so only use this is you have an AppDomain recycle strategy in place
or for debugging purposes.

> Mono doesn't always detect changes, if you have problems report a bug to mono and try to use
`Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled");`
Related:
http://stackoverflow.com/questions/16859372/why-doesnt-the-servicestack-razor-filesystemwatcher-work-on-mono-mac-os-x
http://stackoverflow.com/questions/16519000/filesystemwatcher-under-mono-watching-subdirs
`<Insert your bug report here>`

> Mono doesn't always detect changes, if you have problems report a bug to mono and try to use
> `Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled");`
> Related:
> http://stackoverflow.com/questions/16859372/why-doesnt-the-servicestack-razor-filesystemwatcher-work-on-mono-mac-os-x
> http://stackoverflow.com/questions/16519000/filesystemwatcher-under-mono-watching-subdirs
> `<Insert your bug report here>`
## Available CachingProviders

Expand All @@ -47,7 +49,9 @@ The `GetKey` step enables a `TemplateManager` to add customized data to the key
Note that invalidating cached templates doesn't actually free the memory (loaded assemblies), so only use this
for debugging purposes or if you have an `AppDomain` recycle strategy in place.

## Writing your own
## Writing your own `ITemplateManager`

If the above implementations don't fit your needs you can roll your own:

```csharp
config.TemplateManager = new MyTemplateManager();
Expand All @@ -69,15 +73,157 @@ public class MyTemplateManager : ITemplateManager
// context or the resolveType you need your own implementation here!
// Otherwise you can just use NameOnlyTemplateKey.
return new NameOnlyTemplateKey(name, resolveType, context);
// template is specified by full path
//return new FullPathTemplateKey(name, fullPath, resolveType, context);
}

public void AddDynamic(ITemplateKey key, ITemplateSource source)
{
// You can disable dynamic templates completely, but
// then all convenience methods (Compile and RunCompile) with
// You can disable dynamic templates completely.
// This just means all convenience methods (Compile and RunCompile) with
// a TemplateSource will no longer work (they are not really needed anyway).
throw new NotImplementedException("dynamic templates are not supported!");
}
}
```

Contributing your implementation back to RazorEngine is highly appreciated.

## Writing your own `ICachingProvider`

RazorEngine provides a robust caching layer out of the box which should suite most use-cases.
If you use "Compile" on application startup and "Run" on every use of the application you can be
sure that caching works (as "Run" would throw if there is no cached template).
If you want "lazy"-compilation of the templates you would just use "RunCompile".

However if you need some custom features (like caching across multiple runs of the application)
you want to run your own caching implementation.
All you need to do is implement the `ICachingProvider` interface and set an instance of your implementation in the configuration.
As starting point you can use the DefaultCachingProvider (latest code is in the repository):

```csharp
config.CachingProvider = new DefaultCachingProvider();

/// <summary>
/// The default caching provider (See <see cref="ICachingProvider"/>).
/// This implementation does a very simple in-memory caching.
/// It can handle when the same template is used with multiple model-types.
/// </summary>
public class DefaultCachingProvider : ICachingProvider
{
private readonly ConcurrentDictionary<string, ConcurrentDictionary<Type, ICompiledTemplate>> _cache =
new ConcurrentDictionary<string, ConcurrentDictionary<Type, ICompiledTemplate>>();

private readonly TypeLoader _loader;
private readonly ConcurrentBag<Assembly> _assemblies = new ConcurrentBag<Assembly>();

/// <summary>
/// Initializes a new instance of the <see cref="DefaultCachingProvider"/> class.
/// </summary>
public DefaultCachingProvider()
{
_loader = new TypeLoader(AppDomain.CurrentDomain, _assemblies);
}

/// <summary>
/// The manages <see cref="TypeLoader"/>. See <see cref="ICachingProvider.TypeLoader"/>
/// </summary>
public TypeLoader TypeLoader
{
get
{
return _loader;
}
}

/// <summary>
/// Get the key used within a dictionary for a modelType.
/// </summary>
public static Type GetModelTypeKey(Type modelType)
{
if (modelType == null ||
typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(modelType))
{
return typeof(System.Dynamic.DynamicObject);
}
return modelType;
}

private void CacheTemplateHelper(ICompiledTemplate template, ITemplateKey templateKey, Type modelTypeKey)
{
var uniqueKey = templateKey.GetUniqueKeyString();
_cache.AddOrUpdate(uniqueKey, key =>
{
// new item added
_assemblies.Add(template.TemplateAssembly);
var dict = new ConcurrentDictionary<Type, ICompiledTemplate>();
dict.AddOrUpdate(modelTypeKey, template, (t, old) => template);
return dict;
}, (key, dict) =>
{
dict.AddOrUpdate(modelTypeKey, t =>
{
// new item added (template was not compiled with the given type).
_assemblies.Add(template.TemplateAssembly);
return template;
}, (t, old) =>
{
// item was already added before
return template;
});
return dict;
});
}

/// <summary>
/// Caches a template. See <see cref="ICachingProvider.CacheTemplate"/>.
/// </summary>
/// <param name="template"></param>
/// <param name="templateKey"></param>
public void CacheTemplate(ICompiledTemplate template, ITemplateKey templateKey)
{
var modelTypeKey = GetModelTypeKey(template.ModelType);
CacheTemplateHelper(template, templateKey, modelTypeKey);
var typeArgs = template.TemplateType.BaseType.GetGenericArguments();
if (typeArgs.Length > 0)
{
var alternativeKey = GetModelTypeKey(typeArgs[0]);
if (alternativeKey != modelTypeKey)
{
// could be a template with an @model directive.
CacheTemplateHelper(template, templateKey, typeArgs[0]);
}
}
}

/// <summary>
/// Try to retrieve a template from the cache. See <see cref="ICachingProvider.TryRetrieveTemplate"/>.
/// </summary>
/// <param name="templateKey"></param>
/// <param name="modelType"></param>
/// <param name="compiledTemplate"></param>
/// <returns></returns>
public bool TryRetrieveTemplate(ITemplateKey templateKey, Type modelType, out ICompiledTemplate compiledTemplate)
{
compiledTemplate = null;
var uniqueKey = templateKey.GetUniqueKeyString();
var modelTypeKey = GetModelTypeKey(modelType);
ConcurrentDictionary<Type, ICompiledTemplate> dict;
if (!_cache.TryGetValue(uniqueKey, out dict))
{
return false;
}
return dict.TryGetValue(modelTypeKey, out compiledTemplate);
}

/// <summary>
/// Dispose the instance.
/// </summary>
public void Dispose()
{
_loader.Dispose();
}
}
```

Contributing your implementation back to RazorEngine is highly appreciated.
2 changes: 1 addition & 1 deletion doc/templates/template.cshtml
Expand Up @@ -61,7 +61,7 @@
<li><a href="@Root/Encoding.html">Encoding Values in Razor</a></li>
<li><a href="@Root/ReferenceResolver.html">Reference Resolver</a></li>
<li><a href="@Root/Isolation.html">Isolation and Sandboxing</a></li>
<li><a href="@Root/Caching.html">Caching API</a></li>
<li><a href="@Root/TemplateManager.html">TemplateManager and Caching</a></li>
<li><a href="@Root/DevelopReadme.html">Development Intro</a></li>
<li><a href="@Root/Upgrading.html">Upgrading Guide (3.5.0)</a></li>
<li><a href="@Root/ReleaseNotes.html">Release Notes</a></li>
Expand Down

0 comments on commit 19a51ac

Please sign in to comment.