diff --git a/README.md b/README.md index 646dcab3..258932e0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/doc/Caching.md b/doc/Caching.md index 708155c3..2872db7e 100644 --- a/doc/Caching.md +++ b/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(); - -/// -/// The default caching provider (See ). -/// This implementation does a very simple in-memory caching. -/// It can handle when the same template is used with multiple model-types. -/// -public class DefaultCachingProvider : ICachingProvider -{ - private readonly ConcurrentDictionary> _cache = - new ConcurrentDictionary>(); - - private readonly TypeLoader _loader; - private readonly ConcurrentBag _assemblies = new ConcurrentBag(); - - /// - /// Initializes a new instance of the class. - /// - public DefaultCachingProvider() - { - _loader = new TypeLoader(AppDomain.CurrentDomain, _assemblies); - } - - /// - /// The manages . See - /// - public TypeLoader TypeLoader - { - get - { - return _loader; - } - } - - /// - /// Get the key used within a dictionary for a modelType. - /// - 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(); - 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; - }); - } - - /// - /// Caches a template. See . - /// - /// - /// - 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]); - } - } - } - - /// - /// Try to retrieve a template from the cache. See . - /// - /// - /// - /// - /// - public bool TryRetrieveTemplate(ITemplateKey templateKey, Type modelType, out ICompiledTemplate compiledTemplate) - { - compiledTemplate = null; - var uniqueKey = templateKey.GetUniqueKeyString(); - var modelTypeKey = GetModelTypeKey(modelType); - ConcurrentDictionary dict; - if (!_cache.TryGetValue(uniqueKey, out dict)) - { - return false; - } - return dict.TryGetValue(modelTypeKey, out compiledTemplate); - } - - /// - /// Dispose the instance. - /// - public void Dispose() - { - _loader.Dispose(); - } -} -``` - +Moved to [TemplateManager.html](http://antaris.github.io/RazorEngine/TemplateManager.html) \ No newline at end of file diff --git a/doc/TemplateManager.md b/doc/TemplateManager.md index 36e10a16..13dc0ba4 100644 --- a/doc/TemplateManager.md +++ b/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 @@ -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 - `` + + > 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 + > `` ## Available CachingProviders @@ -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(); @@ -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(); + +/// +/// The default caching provider (See ). +/// This implementation does a very simple in-memory caching. +/// It can handle when the same template is used with multiple model-types. +/// +public class DefaultCachingProvider : ICachingProvider +{ + private readonly ConcurrentDictionary> _cache = + new ConcurrentDictionary>(); + + private readonly TypeLoader _loader; + private readonly ConcurrentBag _assemblies = new ConcurrentBag(); + + /// + /// Initializes a new instance of the class. + /// + public DefaultCachingProvider() + { + _loader = new TypeLoader(AppDomain.CurrentDomain, _assemblies); + } + + /// + /// The manages . See + /// + public TypeLoader TypeLoader + { + get + { + return _loader; + } + } + + /// + /// Get the key used within a dictionary for a modelType. + /// + 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(); + 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; + }); + } + + /// + /// Caches a template. See . + /// + /// + /// + 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]); + } + } + } + + /// + /// Try to retrieve a template from the cache. See . + /// + /// + /// + /// + /// + public bool TryRetrieveTemplate(ITemplateKey templateKey, Type modelType, out ICompiledTemplate compiledTemplate) + { + compiledTemplate = null; + var uniqueKey = templateKey.GetUniqueKeyString(); + var modelTypeKey = GetModelTypeKey(modelType); + ConcurrentDictionary dict; + if (!_cache.TryGetValue(uniqueKey, out dict)) + { + return false; + } + return dict.TryGetValue(modelTypeKey, out compiledTemplate); + } + + /// + /// Dispose the instance. + /// + public void Dispose() + { + _loader.Dispose(); + } +} +``` + +Contributing your implementation back to RazorEngine is highly appreciated. \ No newline at end of file diff --git a/doc/templates/template.cshtml b/doc/templates/template.cshtml index 5f2ace5d..3c50703f 100644 --- a/doc/templates/template.cshtml +++ b/doc/templates/template.cshtml @@ -61,7 +61,7 @@
  • Encoding Values in Razor
  • Reference Resolver
  • Isolation and Sandboxing
  • -
  • Caching API
  • +
  • TemplateManager and Caching
  • Development Intro
  • Upgrading Guide (3.5.0)
  • Release Notes