Unified · Split
| @@ -221,11 +221,14 @@ · 220 unmodified lines | |||
| 221 | <Compile Include="Hosting\WebPageController.cs" /> | 221 | <Compile Include="Hosting\WebPageController.cs" /> |
| 222 | <Compile Include="IgnoreServicesSecurityModule.cs" /> | 222 | <Compile Include="IgnoreServicesSecurityModule.cs" /> |
| 223 | <Compile Include="Model\DataContext.cs" /> | 223 | <Compile Include="Model\DataContext.cs" /> |
| 224 | <Compile Include="Model\IRazorView.cs" /> | 224 | <Compile Include="Model\IRazorEngine.cs" /> |
| 225 | <Compile Include="Model\IRecordOperationsHub.cs" /> | 225 | <Compile Include="Model\IRecordOperationsHub.cs" /> |
| 226 | <Compile Include="Model\Label.cs" /> | 226 | <Compile Include="Model\Label.cs" /> |
| 227 | <Compile Include="Model\IDLabel.cs" /> | 227 | <Compile Include="Model\IDLabel.cs" /> |
| 228 | <Compile Include="Model\LanguageConstraint.cs" /> | ||
| 228 | <Compile Include="Model\NamespaceDoc.cs" /> | 229 | <Compile Include="Model\NamespaceDoc.cs" /> |
| 230 | <Compile Include="Model\RazorEngine.cs" /> | ||
| 231 | <Compile Include="Model\RazorViewResolutionMode.cs" /> | ||
| 229 | <Compile Include="Model\RecordOperationsCache.cs" /> | 232 | <Compile Include="Model\RecordOperationsCache.cs" /> |
| 230 | <Compile Include="NamespaceDoc.cs" /> | 233 | <Compile Include="NamespaceDoc.cs" /> |
| 231 | <Compile Include="Properties\AssemblyInfo.cs" /> | 234 | <Compile Include="Properties\AssemblyInfo.cs" /> |
| @@ -32,6 +32,7 @@ using System.Text; · 31 unmodified lines | |||
| 32 | using System.Threading.Tasks; | 32 | using System.Threading.Tasks; |
| 33 | using System.Web; | 33 | using System.Web; |
| 34 | using System.Web.Http; | 34 | using System.Web.Http; |
| 35 | using GSF.Data; | ||
| 35 | using GSF.IO; | 36 | using GSF.IO; |
| 36 | using GSF.IO.Checksums; | 37 | using GSF.IO.Checksums; |
| 37 | using GSF.Reflection; | 38 | using GSF.Reflection; |
| @@ -58,15 +59,42 @@ namespace GSF.Web.Hosting · 20 unmodified lines | |||
| 58 | /// </summary> | 59 | /// </summary> |
| 59 | public event EventHandler<string> StatusMessage; | 60 | public event EventHandler<string> StatusMessage; |
| 60 | 61 | ||
| 62 | // Fields | ||
| 63 | private readonly string m_webRootPath; | ||
| 64 | private readonly IRazorEngine m_razorEngineCS; | ||
| 65 | private readonly IRazorEngine m_razorEngineVB; | ||
| 66 | private readonly ConcurrentDictionary<string, uint> m_etagCache; | ||
| 67 | private readonly SafeFileWatcher m_fileWatcher; | ||
| 68 | private bool m_disposed; | ||
| 69 | |||
| 61 | #endregion | 70 | #endregion |
| 62 | 71 | ||
| 63 | #region [ Constructors ] | 72 | #region [ Constructors ] |
| 64 | 73 | ||
| 65 | /// <summary> | 74 | /// <summary> |
| 66 | /// Creates a new <see cref="WebPageController"/>. | 75 | /// Creates a new <see cref="WebPageController"/>. |
| 67 | /// </summary> | 76 | /// </summary> |
| 68 | public WebPageController() | 77 | /// <param name="webRootPath">Root path for web page; defaults to template path for <paramref name="razorEngineCS"/>.</param> |
| 78 | /// <param name="razorEngineCS">Razor engine instance for .cshtml templates; uses default instance if not provided.</param> | ||
| 79 | /// <param name="razorEngineVB">Razor engine instance for .vbhtml templates; uses default instance if not provided.</param> | ||
| 80 | public WebPageController(string webRootPath = null, IRazorEngine razorEngineCS = null, IRazorEngine razorEngineVB = null) | ||
| 69 | { | 81 | { |
| 82 | bool releaseMode = !AssemblyInfo.EntryAssembly.Debuggable; | ||
| 83 | m_razorEngineCS = razorEngineCS ?? (releaseMode ? RazorEngine<CSharp>.Default : RazorEngine<CSharpDebug>.Default as IRazorEngine); | ||
| 84 | m_razorEngineVB = razorEngineVB ?? (releaseMode ? RazorEngine<VisualBasic>.Default : RazorEngine<VisualBasicDebug>.Default as IRazorEngine); | ||
| 85 | m_webRootPath = FilePath.AddPathSuffix(webRootPath ?? m_razorEngineCS.TemplatePath); | ||
| 86 | m_etagCache = new ConcurrentDictionary<string, uint>(StringComparer.InvariantCultureIgnoreCase); | ||
| 87 | |||
| 88 | m_fileWatcher = new SafeFileWatcher(m_webRootPath) | ||
| 89 | { | ||
| 90 | IncludeSubdirectories = true, | ||
| 91 | EnableRaisingEvents = true | ||
| 92 | }; | ||
| 93 | |||
| 94 | m_fileWatcher.Changed += m_fileWatcher_FileChange; | ||
| 95 | m_fileWatcher.Deleted += m_fileWatcher_FileChange; | ||
| 96 | m_fileWatcher.Renamed += m_fileWatcher_FileChange; | ||
| 97 | |||
| 70 | DefaultWebPage = "index.html"; | 98 | DefaultWebPage = "index.html"; |
| 71 | ClientCacheEnabled = true; | 99 | ClientCacheEnabled = true; |
| 72 | } | 100 | } |
| @@ -74,7 +102,7 @@ namespace GSF.Web.Hosting · 1 unmodified line | |||
| 74 | #endregion | 102 | #endregion |
| 75 | 103 | ||
| 76 | #region [ Properties ] | 104 | #region [ Properties ] |
| 77 | 105 | ||
| 78 | /// <summary> | 106 | /// <summary> |
| 79 | /// Gets or sets default web page to use for this <see cref="WebPageController"/>. | 107 | /// Gets or sets default web page to use for this <see cref="WebPageController"/>. |
| 80 | /// </summary> | 108 | /// </summary> |
| @@ -85,7 +113,7 @@ namespace GSF.Web.Hosting · 4 unmodified lines | |||
| 85 | } | 113 | } |
| 86 | 114 | ||
| 87 | /// <summary> | 115 | /// <summary> |
| 88 | /// Gets or sets the Razor model instance for this <see cref="WebPageController"/>. | 116 | /// Gets or sets the <see cref="RazorView"/> model instance for this <see cref="WebPageController"/>, if any. |
| 89 | /// </summary> | 117 | /// </summary> |
| 90 | public object RazorModel | 118 | public object RazorModel |
| 91 | { | 119 | { |
| @@ -94,7 +122,7 @@ namespace GSF.Web.Hosting · 2 unmodified lines | |||
| 94 | } | 122 | } |
| 95 | 123 | ||
| 96 | /// <summary> | 124 | /// <summary> |
| 97 | /// Gets or sets the Razor model <see cref="Type"/> for this <see cref="WebPageController"/>. | 125 | /// Gets or sets the <see cref="RazorView"/> model <see cref="Type"/> for this <see cref="WebPageController"/>, if any. |
| 98 | /// </summary> | 126 | /// </summary> |
| 99 | public Type RazorModelType | 127 | public Type RazorModelType |
| 100 | { | 128 | { |
| @@ -102,6 +130,15 @@ namespace GSF.Web.Hosting · 1 unmodified line | |||
| 102 | set; | 130 | set; |
| 103 | } | 131 | } |
| 104 | 132 | ||
| 133 | /// <summary> | ||
| 134 | /// Gets or sets data connection to provide to <see cref="RazorView"/> instances in this <see cref="WebPageController"/>, if any. | ||
| 135 | /// </summary> | ||
| 136 | public AdoDataConnection RazorConnection | ||
| 137 | { | ||
| 138 | get; | ||
| 139 | set; | ||
| 140 | } | ||
| 141 | |||
| 105 | /// <summary> | 142 | /// <summary> |
| 106 | /// Gets or sets flag that determines if cache control is enabled for browser clients; default to <c>true</c>. | 143 | /// Gets or sets flag that determines if cache control is enabled for browser clients; default to <c>true</c>. |
| 107 | /// </summary> | 144 | /// </summary> |
| @@ -115,6 +152,27 @@ namespace GSF.Web.Hosting · 7 unmodified lines | |||
| 115 | 152 | ||
| 116 | #region [ Methods ] | 153 | #region [ Methods ] |
| 117 | 154 | ||
| 155 | /// <summary> | ||
| 156 | /// Releases the unmanaged resources used by the <see cref="WebPageController"/> object and optionally releases the managed resources. | ||
| 157 | /// </summary> | ||
| 158 | /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> | ||
| 159 | protected override void Dispose(bool disposing) | ||
| 160 | { | ||
| 161 | if (!m_disposed) | ||
| 162 | { | ||
| 163 | try | ||
| 164 | { | ||
| 165 | if (disposing) | ||
| 166 | m_fileWatcher?.Dispose(); | ||
| 167 | } | ||
| 168 | finally | ||
| 169 | { | ||
| 170 | m_disposed = true; // Prevent duplicate dispose. | ||
| 171 | base.Dispose(disposing); // Call base class Dispose(). | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 118 | /// <summary> | 176 | /// <summary> |
| 119 | /// Default page request handler. | 177 | /// Default page request handler. |
| 120 | /// </summary> | 178 | /// </summary> |
| @@ -157,22 +215,22 @@ namespace GSF.Web.Hosting · 36 unmodified lines | |||
| 157 | switch (fileExtension) | 215 | switch (fileExtension) |
| 158 | { | 216 | { |
| 159 | case ".cshtml": | 217 | case ".cshtml": |
| 160 | content = await s_razorViewCS(pageName, RazorModel, RazorModelType, OnExecutionException).ExecuteAsync(Request, postData); | 218 | content = await new RazorView(m_razorEngineCS, pageName, RazorModel, RazorModelType, RazorConnection, OnExecutionException).ExecuteAsync(Request, postData); |
| 161 | response.Content = new StringContent(content, Encoding.UTF8, "text/html"); | 219 | response.Content = new StringContent(content, Encoding.UTF8, "text/html"); |
| 162 | break; | 220 | break; |
| 163 | case ".vbhtml": | 221 | case ".vbhtml": |
| 164 | content = await s_razorViewVB(pageName, RazorModel, RazorModelType, OnExecutionException).ExecuteAsync(Request, postData); | 222 | content = await new RazorView(m_razorEngineVB, pageName, RazorModel, RazorModelType, RazorConnection, OnExecutionException).ExecuteAsync(Request, postData); |
| 165 | response.Content = new StringContent(content, Encoding.UTF8, "text/html"); | 223 | response.Content = new StringContent(content, Encoding.UTF8, "text/html"); |
| 166 | break; | 224 | break; |
| 167 | default: | 225 | default: |
| 168 | string fileName = FilePath.GetAbsolutePath($"{FilePath.AddPathSuffix(s_templatePath)}{pageName.Replace('/', Path.DirectorySeparatorChar)}"); | 226 | string fileName = FilePath.GetAbsolutePath($"{m_webRootPath}{pageName.Replace('/', Path.DirectorySeparatorChar)}"); |
| 169 | 227 | ||
| 170 | if (File.Exists(fileName)) | 228 | if (File.Exists(fileName)) |
| 171 | { | 229 | { |
| 172 | FileStream fileData = null; | 230 | FileStream fileData = null; |
| 173 | uint responseHash = 0; | 231 | uint responseHash = 0; |
| 174 | 232 | ||
| 175 | if (ClientCacheEnabled && !s_etagCache.TryGetValue(fileName, out responseHash)) | 233 | if (ClientCacheEnabled && !m_etagCache.TryGetValue(fileName, out responseHash)) |
| 176 | { | 234 | { |
| 177 | // Calculate check-sum for file | 235 | // Calculate check-sum for file |
| 178 | await Task.Run(() => | 236 | await Task.Run(() => |
| @@ -191,7 +249,7 @@ namespace GSF.Web.Hosting · 12 unmodified lines | |||
| 191 | } | 249 | } |
| 192 | 250 | ||
| 193 | responseHash = calculatedHash.Value; | 251 | responseHash = calculatedHash.Value; |
| 194 | s_etagCache.TryAdd(fileName, responseHash); | 252 | m_etagCache.TryAdd(fileName, responseHash); |
| 195 | fileData.Seek(0, SeekOrigin.Begin); | 253 | fileData.Seek(0, SeekOrigin.Begin); |
| 196 | 254 | ||
| 197 | OnStatusMessage($"Cache [{responseHash}] added for file \"{fileName}\""); | 255 | OnStatusMessage($"Cache [{responseHash}] added for file \"{fileName}\""); |
| @@ -200,7 +258,7 @@ namespace GSF.Web.Hosting · 2 unmodified lines | |||
| 200 | 258 | ||
| 201 | if (PublishResponseContent(response, responseHash)) | 259 | if (PublishResponseContent(response, responseHash)) |
| 202 | { | 260 | { |
| 203 | if ((object)fileData == null) | 261 | if (fileData == null) |
| 204 | fileData = File.OpenRead(fileName); | 262 | fileData = File.OpenRead(fileName); |
| 205 | 263 | ||
| 206 | response.Content = await Task.Run(() => new StreamContent(fileData)); | 264 | response.Content = await Task.Run(() => new StreamContent(fileData)); |
| @@ -259,6 +317,14 @@ namespace GSF.Web.Hosting · 52 unmodified lines | |||
| 259 | StatusMessage?.Invoke(this, message); | 317 | StatusMessage?.Invoke(this, message); |
| 260 | } | 318 | } |
| 261 | 319 | ||
| 320 | private void m_fileWatcher_FileChange(object sender, FileSystemEventArgs e) | ||
| 321 | { | ||
| 322 | uint responseHash; | ||
| 323 | |||
| 324 | if (m_etagCache.TryRemove(e.FullPath, out responseHash)) | ||
| 325 | OnStatusMessage($"Cache [{responseHash}] cleared for file \"{e.FullPath}\""); | ||
| 326 | } | ||
| 327 | |||
| 262 | #region [ Sub-folder Handlers ] | 328 | #region [ Sub-folder Handlers ] |
| 263 | 329 | ||
| 264 | /// <summary> | 330 | /// <summary> |
| @@ -409,89 +475,5 @@ namespace GSF.Web.Hosting · 144 unmodified lines | |||
| 409 | #endregion | 475 | #endregion |
| 410 | 476 | ||
| 411 | #endregion | 477 | #endregion |
| 412 | |||
| 413 | #region [ Static ] | ||
| 414 | |||
| 415 | // Static Events | ||
| 416 | |||
| 417 | /// <summary> | ||
| 418 | /// Exposes any execution exceptions that occur during static <see cref="WebPageController"/> initialization. | ||
| 419 | /// </summary> | ||
| 420 | public static event EventHandler<Exception> InitializationExceptions; | ||
| 421 | |||
| 422 | /// <summary> | ||
| 423 | /// Exposes any status messages that get reported from static <see cref="WebPageController"/> initialization. | ||
| 424 | /// </summary> | ||
| 425 | public static event EventHandler<string> InitializationMessages; | ||
| 426 | |||
| 427 | // Static Fields | ||
| 428 | private static readonly string s_templatePath; | ||
| 429 | private static readonly Func<string, object, Type, Action<Exception>, IRazorView> s_razorViewCS; | ||
| 430 | private static readonly Func<string, object, Type, Action<Exception>, IRazorView> s_razorViewVB; | ||
| 431 | private static readonly ConcurrentDictionary<string, uint> s_etagCache; | ||
| 432 | private static readonly FileSystemWatcher s_fileWatcher; | ||
| 433 | |||
| 434 | // Static Constructor | ||
| 435 | static WebPageController() | ||
| 436 | { | ||
| 437 | bool isDebugMode = AssemblyInfo.EntryAssembly.Debuggable; | ||
| 438 | |||
| 439 | if (isDebugMode) | ||
| 440 | { | ||
| 441 | s_templatePath = RazorView<CSharpDebug>.TemplatePath; | ||
| 442 | s_razorViewCS = (templateName, model, modelType, exceptionHandler) => new RazorView<CSharpDebug>(templateName, model, modelType, exceptionHandler); | ||
| 443 | s_razorViewVB = (templateName, model, modelType, exceptionHandler) => new RazorView<VisualBasicDebug>(templateName, model, modelType, exceptionHandler); | ||
| 444 | } | ||
| 445 | else | ||
| 446 | { | ||
| 447 | s_templatePath = RazorView<CSharp>.TemplatePath; | ||
| 448 | s_razorViewCS = (templateName, model, modelType, exceptionHandler) => new RazorView<CSharp>(templateName, model, modelType, exceptionHandler); | ||
| 449 | s_razorViewVB = (templateName, model, modelType, exceptionHandler) => new RazorView<VisualBasic>(templateName, model, modelType, exceptionHandler); | ||
| 450 | } | ||
| 451 | |||
| 452 | s_etagCache = new ConcurrentDictionary<string, uint>(StringComparer.InvariantCultureIgnoreCase); | ||
| 453 | |||
| 454 | s_fileWatcher = new FileSystemWatcher(s_templatePath) | ||
| 455 | { | ||
| 456 | IncludeSubdirectories = true, | ||
| 457 | EnableRaisingEvents = true | ||
| 458 | }; | ||
| 459 | |||
| 460 | s_fileWatcher.Changed += s_fileWatcher_FileChange; | ||
| 461 | s_fileWatcher.Deleted += s_fileWatcher_FileChange; | ||
| 462 | s_fileWatcher.Renamed += s_fileWatcher_FileChange; | ||
| 463 | |||
| 464 | |||
| 465 | if (isDebugMode) | ||
| 466 | { | ||
| 467 | RazorView<CSharpDebug>.PreCompile(OnInitializationExceptions); | ||
| 468 | RazorView<VisualBasicDebug>.PreCompile(OnInitializationExceptions); | ||
| 469 | } | ||
| 470 | else | ||
| 471 | { | ||
| 472 | RazorView<CSharp>.PreCompile(OnInitializationExceptions); | ||
| 473 | RazorView<VisualBasic>.PreCompile(OnInitializationExceptions); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | private static void s_fileWatcher_FileChange(object sender, FileSystemEventArgs e) | ||
| 478 | { | ||
| 479 | uint responseHash; | ||
| 480 | |||
| 481 | if (s_etagCache.TryRemove(e.FullPath, out responseHash)) | ||
| 482 | OnInitializationMessages($"Cache [{responseHash}] cleared for file \"{e.FullPath}\""); | ||
| 483 | } | ||
| 484 | |||
| 485 | private static void OnInitializationExceptions(Exception exception) | ||
| 486 | { | ||
| 487 | InitializationExceptions?.Invoke(null, exception); | ||
| 488 | } | ||
| 489 | |||
| 490 | private static void OnInitializationMessages(string message) | ||
| 491 | { | ||
| 492 | InitializationMessages?.Invoke(null, message); | ||
| 493 | } | ||
| 494 | |||
| 495 | #endregion | ||
| 496 | } | 478 | } |
| 497 | } | 479 | } |
| @@ -41,42 +41,19 @@ using RazorEngine.Templating; · 40 unmodified lines | |||
| 41 | namespace GSF.Web.Model | 41 | namespace GSF.Web.Model |
| 42 | { | 42 | { |
| 43 | /// <summary> | 43 | /// <summary> |
| 44 | /// Defines a default data context object based on embedded resources. | 44 | /// Defines a data context for Razor views. |
| 45 | /// </summary> | 45 | /// </summary> |
| 46 | public class DataContext : DataContext<CSharpEmbeddedResource> | 46 | /// <remarks> |
| 47 | { | 47 | /// This class is used by views to render HTML form input templates, paged view model configurations, |
| 48 | /// <summary> | 48 | /// provide access to modeled table operations and general database access. |
| 49 | /// Creates a new <see cref="DataContext"/>. | 49 | /// </remarks> |
| 50 | /// </summary> | 50 | public class DataContext : IDisposable |
| 51 | /// <param name="connection"><see cref="AdoDataConnection"/> to use; defaults to a new connection.</param> | ||
| 52 | /// <param name="disposeConnection">Set to <c>true</c> to dispose the provided <paramref name="connection"/>.</param> | ||
| 53 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> | ||
| 54 | public DataContext(AdoDataConnection connection = null, bool disposeConnection = false, Action<Exception> exceptionHandler = null) : | ||
| 55 | base(connection, disposeConnection, exceptionHandler) | ||
| 56 | { | ||
| 57 | } | ||
| 58 | |||
| 59 | /// <summary> | ||
| 60 | /// Creates a new <see cref="DataContext"/> using the specified <paramref name="settingsCategory"/>. | ||
| 61 | /// </summary> | ||
| 62 | /// <param name="settingsCategory">Setting category that contains the connection settings.</param> | ||
| 63 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> | ||
| 64 | public DataContext(string settingsCategory, Action<Exception> exceptionHandler = null) : | ||
| 65 | base(settingsCategory, exceptionHandler) | ||
| 66 | { | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | /// <summary> | ||
| 71 | /// Defines a data context for modeled tables. | ||
| 72 | /// </summary> | ||
| 73 | /// <typeparam name="TLanguage"><see cref="LanguageConstraint"/> type to use for Razor views.</typeparam> | ||
| 74 | public class DataContext<TLanguage> : IDisposable where TLanguage : LanguageConstraint, new() | ||
| 75 | { | 51 | { |
| 76 | #region [ Members ] | 52 | #region [ Members ] |
| 77 | 53 | ||
| 78 | // Fields | 54 | // Fields |
| 79 | private AdoDataConnection m_connection; | 55 | private AdoDataConnection m_connection; |
| 56 | private readonly IRazorEngine m_razorEngine; | ||
| 80 | private readonly Dictionary<Type, object> m_tableOperations; | 57 | private readonly Dictionary<Type, object> m_tableOperations; |
| 81 | private readonly Action<Exception> m_exceptionHandler; | 58 | private readonly Action<Exception> m_exceptionHandler; |
| 82 | private readonly Dictionary<string, Tuple<string, string>> m_fieldValidationParameters; | 59 | private readonly Dictionary<string, Tuple<string, string>> m_fieldValidationParameters; |
| @@ -98,14 +75,16 @@ namespace GSF.Web.Model · 15 unmodified lines | |||
| 98 | #region [ Constructors ] | 75 | #region [ Constructors ] |
| 99 | 76 | ||
| 100 | /// <summary> | 77 | /// <summary> |
| 101 | /// Creates a new <see cref="DataContext{TLanguage}"/>. | 78 | /// Creates a new <see cref="DataContext"/>. |
| 102 | /// </summary> | 79 | /// </summary> |
| 103 | /// <param name="connection"><see cref="AdoDataConnection"/> to use; defaults to a new connection.</param> | 80 | /// <param name="connection"><see cref="AdoDataConnection"/> to use; defaults to a new connection.</param> |
| 104 | /// <param name="disposeConnection">Set to <c>true</c> to dispose the provided <paramref name="connection"/>.</param> | 81 | /// <param name="disposeConnection">Set to <c>true</c> to dispose the provided <paramref name="connection"/>.</param> |
| 82 | /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> | ||
| 105 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> | 83 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> |
| 106 | public DataContext(AdoDataConnection connection = null, bool disposeConnection = false, Action<Exception> exceptionHandler = null) | 84 | public DataContext(AdoDataConnection connection = null, bool disposeConnection = false, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) |
| 107 | { | 85 | { |
| 108 | m_connection = connection; | 86 | m_connection = connection; |
| 87 | m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; | ||
| 109 | m_tableOperations = new Dictionary<Type, object>(); | 88 | m_tableOperations = new Dictionary<Type, object>(); |
| 110 | m_exceptionHandler = exceptionHandler; | 89 | m_exceptionHandler = exceptionHandler; |
| 111 | m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); | 90 | m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); |
| @@ -117,12 +96,14 @@ namespace GSF.Web.Model · 5 unmodified lines | |||
| 117 | } | 96 | } |
| 118 | 97 | ||
| 119 | /// <summary> | 98 | /// <summary> |
| 120 | /// Creates a new <see cref="DataContext{TLanguage}"/> using the specified <paramref name="settingsCategory"/>. | 99 | /// Creates a new <see cref="DataContext"/> using the specified <paramref name="settingsCategory"/>. |
| 121 | /// </summary> | 100 | /// </summary> |
| 122 | /// <param name="settingsCategory">Setting category that contains the connection settings.</param> | 101 | /// <param name="settingsCategory">Setting category that contains the connection settings.</param> |
| 102 | /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> | ||
| 123 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> | 103 | /// <param name="exceptionHandler">Delegate to handle exceptions.</param> |
| 124 | public DataContext(string settingsCategory, Action<Exception> exceptionHandler = null) | 104 | public DataContext(string settingsCategory, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) |
| 125 | { | 105 | { |
| 106 | m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; | ||
| 126 | m_tableOperations = new Dictionary<Type, object>(); | 107 | m_tableOperations = new Dictionary<Type, object>(); |
| 127 | m_exceptionHandler = exceptionHandler; | 108 | m_exceptionHandler = exceptionHandler; |
| 128 | m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); | 109 | m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); |
| @@ -137,7 +118,12 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 137 | #region [ Properties ] | 118 | #region [ Properties ] |
| 138 | 119 | ||
| 139 | /// <summary> | 120 | /// <summary> |
| 140 | /// Gets the <see cref="AdoDataConnection"/> for this <see cref="DataContext{TLanguage}"/>. | 121 | /// Gets reference to <see cref="IRazorEngine"/> used by this <see cref="DataContext"/>. |
| 122 | /// </summary> | ||
| 123 | public IRazorEngine RazorEngine => m_razorEngine; | ||
| 124 | |||
| 125 | /// <summary> | ||
| 126 | /// Gets the <see cref="AdoDataConnection"/> for this <see cref="DataContext"/>, creating a new one if needed. | ||
| 141 | /// </summary> | 127 | /// </summary> |
| 142 | public AdoDataConnection Connection => m_connection ?? (m_connection = new AdoDataConnection(m_settingsCategory)); | 128 | public AdoDataConnection Connection => m_connection ?? (m_connection = new AdoDataConnection(m_settingsCategory)); |
| 143 | 129 | ||
| @@ -178,7 +164,7 @@ namespace GSF.Web.Model · 34 unmodified lines | |||
| 178 | { | 164 | { |
| 179 | get | 165 | get |
| 180 | { | 166 | { |
| 181 | return m_addDateFieldTemplate ?? (m_addDateFieldTemplate = $"{RazorView<TLanguage>.TemplatePath}AddDateField.cshtml"); | 167 | return m_addDateFieldTemplate ?? (m_addDateFieldTemplate = $"{m_razorEngine.TemplatePath}AddDateField.cshtml"); |
| 182 | } | 168 | } |
| 183 | set | 169 | set |
| 184 | { | 170 | { |
| @@ -193,7 +179,7 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 193 | { | 179 | { |
| 194 | get | 180 | get |
| 195 | { | 181 | { |
| 196 | return m_addInputFieldTemplate ?? (m_addInputFieldTemplate = $"{RazorView<TLanguage>.TemplatePath}AddInputField.cshtml"); | 182 | return m_addInputFieldTemplate ?? (m_addInputFieldTemplate = $"{m_razorEngine.TemplatePath}AddInputField.cshtml"); |
| 197 | } | 183 | } |
| 198 | set | 184 | set |
| 199 | { | 185 | { |
| @@ -208,7 +194,7 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 208 | { | 194 | { |
| 209 | get | 195 | get |
| 210 | { | 196 | { |
| 211 | return m_addTextAreaFieldTemplate ?? (m_addTextAreaFieldTemplate = $"{RazorView<TLanguage>.TemplatePath}AddTextAreaField.cshtml"); | 197 | return m_addTextAreaFieldTemplate ?? (m_addTextAreaFieldTemplate = $"{m_razorEngine.TemplatePath}AddTextAreaField.cshtml"); |
| 212 | } | 198 | } |
| 213 | set | 199 | set |
| 214 | { | 200 | { |
| @@ -223,7 +209,7 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 223 | { | 209 | { |
| 224 | get | 210 | get |
| 225 | { | 211 | { |
| 226 | return m_addSelectFieldTemplate ?? (m_addSelectFieldTemplate = $"{RazorView<TLanguage>.TemplatePath}AddSelectField.cshtml"); | 212 | return m_addSelectFieldTemplate ?? (m_addSelectFieldTemplate = $"{m_razorEngine.TemplatePath}AddSelectField.cshtml"); |
| 227 | } | 213 | } |
| 228 | set | 214 | set |
| 229 | { | 215 | { |
| @@ -238,7 +224,7 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 238 | { | 224 | { |
| 239 | get | 225 | get |
| 240 | { | 226 | { |
| 241 | return m_addCheckBoxFieldTemplate ?? (m_addCheckBoxFieldTemplate = $"{RazorView<TLanguage>.TemplatePath}AddCheckBoxField.cshtml"); | 227 | return m_addCheckBoxFieldTemplate ?? (m_addCheckBoxFieldTemplate = $"{m_razorEngine.TemplatePath}AddCheckBoxField.cshtml"); |
| 242 | } | 228 | } |
| 243 | set | 229 | set |
| 244 | { | 230 | { |
| @@ -251,7 +237,7 @@ namespace GSF.Web.Model · 6 unmodified lines | |||
| 251 | #region [ Methods ] | 237 | #region [ Methods ] |
| 252 | 238 | ||
| 253 | /// <summary> | 239 | /// <summary> |
| 254 | /// Releases all the resources used by the <see cref="DataContext{TLanguage}"/> object. | 240 | /// Releases all the resources used by the <see cref="DataContext"/> object. |
| 255 | /// </summary> | 241 | /// </summary> |
| 256 | public void Dispose() | 242 | public void Dispose() |
| 257 | { | 243 | { |
| @@ -260,7 +246,7 @@ namespace GSF.Web.Model · 2 unmodified lines | |||
| 260 | } | 246 | } |
| 261 | 247 | ||
| 262 | /// <summary> | 248 | /// <summary> |
| 263 | /// Releases the unmanaged resources used by the <see cref="DataContext{TLanguage}"/> object and optionally releases the managed resources. | 249 | /// Releases the unmanaged resources used by the <see cref="DataContext"/> object and optionally releases the managed resources. |
| 264 | /// </summary> | 250 | /// </summary> |
| 265 | /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> | 251 | /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> |
| 266 | protected virtual void Dispose(bool disposing) | 252 | protected virtual void Dispose(bool disposing) |
| @@ -786,7 +772,7 @@ namespace GSF.Web.Model · 519 unmodified lines | |||
| 786 | /// <returns>Generated HTML for new date based input field based on specified parameters.</returns> | 772 | /// <returns>Generated HTML for new date based input field based on specified parameters.</returns> |
| 787 | public string AddDateField(string fieldName, bool required, int maxLength = 0, string inputType = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false) | 773 | public string AddDateField(string fieldName, bool required, int maxLength = 0, string inputType = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false) |
| 788 | { | 774 | { |
| 789 | RazorView<TLanguage> addDateFieldTemplate = new RazorView<TLanguage>(AddDateFieldTemplate, m_exceptionHandler); | 775 | RazorView addDateFieldTemplate = new RazorView(m_razorEngine, AddDateFieldTemplate, m_exceptionHandler); |
| 790 | DynamicViewBag viewBag = addDateFieldTemplate.ViewBag; | 776 | DynamicViewBag viewBag = addDateFieldTemplate.ViewBag; |
| 791 | 777 | ||
| 792 | if (string.IsNullOrEmpty(fieldID)) | 778 | if (string.IsNullOrEmpty(fieldID)) |
| @@ -895,7 +881,7 @@ namespace GSF.Web.Model · 102 unmodified lines | |||
| 895 | /// <returns>Generated HTML for new input field based on specified parameters.</returns> | 881 | /// <returns>Generated HTML for new input field based on specified parameters.</returns> |
| 896 | public string AddInputField(string fieldName, bool required, int maxLength = 0, string inputType = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false, bool enableHotLinks = true) | 882 | public string AddInputField(string fieldName, bool required, int maxLength = 0, string inputType = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false, bool enableHotLinks = true) |
| 897 | { | 883 | { |
| 898 | RazorView<TLanguage> addInputFieldTemplate = new RazorView<TLanguage>(AddInputFieldTemplate, m_exceptionHandler); | 884 | RazorView addInputFieldTemplate = new RazorView(m_razorEngine, AddInputFieldTemplate, m_exceptionHandler); |
| 899 | DynamicViewBag viewBag = addInputFieldTemplate.ViewBag; | 885 | DynamicViewBag viewBag = addInputFieldTemplate.ViewBag; |
| 900 | 886 | ||
| 901 | if (string.IsNullOrEmpty(fieldID)) | 887 | if (string.IsNullOrEmpty(fieldID)) |
| @@ -998,7 +984,7 @@ namespace GSF.Web.Model · 96 unmodified lines | |||
| 998 | /// <returns>Generated HTML for new text area field based on specified parameters.</returns> | 984 | /// <returns>Generated HTML for new text area field based on specified parameters.</returns> |
| 999 | public string AddTextAreaField(string fieldName, bool required, int maxLength = 0, int rows = 2, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false, bool enableHotLinks = true) | 985 | public string AddTextAreaField(string fieldName, bool required, int maxLength = 0, int rows = 2, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string requiredDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false, bool enableHotLinks = true) |
| 1000 | { | 986 | { |
| 1001 | RazorView<TLanguage> addTextAreaTemplate = new RazorView<TLanguage>(AddTextAreaFieldTemplate, m_exceptionHandler); | 987 | RazorView addTextAreaTemplate = new RazorView(m_razorEngine, AddTextAreaFieldTemplate, m_exceptionHandler); |
| 1002 | DynamicViewBag viewBag = addTextAreaTemplate.ViewBag; | 988 | DynamicViewBag viewBag = addTextAreaTemplate.ViewBag; |
| 1003 | 989 | ||
| 1004 | if (string.IsNullOrEmpty(fieldID)) | 990 | if (string.IsNullOrEmpty(fieldID)) |
| @@ -1087,7 +1073,7 @@ namespace GSF.Web.Model · 82 unmodified lines | |||
| 1087 | /// <returns>Generated HTML for new text field based on specified parameters.</returns> | 1073 | /// <returns>Generated HTML for new text field based on specified parameters.</returns> |
| 1088 | public string AddSelectField<TOption>(string fieldName, bool required, string optionValueFieldName, string optionLabelFieldName = null, string optionSortFieldName = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string optionDataBinding = null, string toolTip = null, bool initialFocus = false, RecordRestriction restriction = null) where TOption : class, new() | 1074 | public string AddSelectField<TOption>(string fieldName, bool required, string optionValueFieldName, string optionLabelFieldName = null, string optionSortFieldName = null, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string optionDataBinding = null, string toolTip = null, bool initialFocus = false, RecordRestriction restriction = null) where TOption : class, new() |
| 1089 | { | 1075 | { |
| 1090 | RazorView<TLanguage> addSelectFieldTemplate = new RazorView<TLanguage>(AddSelectFieldTemplate, m_exceptionHandler); | 1076 | RazorView addSelectFieldTemplate = new RazorView(m_razorEngine, AddSelectFieldTemplate, m_exceptionHandler); |
| 1091 | DynamicViewBag viewBag = addSelectFieldTemplate.ViewBag; | 1077 | DynamicViewBag viewBag = addSelectFieldTemplate.ViewBag; |
| 1092 | TableOperations<TOption> optionTableOperations = Table<TOption>(); | 1078 | TableOperations<TOption> optionTableOperations = Table<TOption>(); |
| 1093 | Dictionary<string, string> options = new Dictionary<string, string>(); | 1079 | Dictionary<string, string> options = new Dictionary<string, string>(); |
| @@ -1169,7 +1155,7 @@ namespace GSF.Web.Model · 75 unmodified lines | |||
| 1169 | /// <returns>Generated HTML for new check box field based on specified parameters.</returns> | 1155 | /// <returns>Generated HTML for new check box field based on specified parameters.</returns> |
| 1170 | public string AddCheckBoxField(string fieldName, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false) | 1156 | public string AddCheckBoxField(string fieldName, string fieldLabel = null, string fieldID = null, string groupDataBinding = null, string labelDataBinding = null, string customDataBinding = null, string dependencyFieldName = null, string toolTip = null, bool initialFocus = false) |
| 1171 | { | 1157 | { |
| 1172 | RazorView<TLanguage> addCheckBoxFieldTemplate = new RazorView<TLanguage>(AddCheckBoxFieldTemplate, m_exceptionHandler); | 1158 | RazorView addCheckBoxFieldTemplate = new RazorView(m_razorEngine, AddCheckBoxFieldTemplate, m_exceptionHandler); |
| 1173 | DynamicViewBag viewBag = addCheckBoxFieldTemplate.ViewBag; | 1159 | DynamicViewBag viewBag = addCheckBoxFieldTemplate.ViewBag; |
| 1174 | 1160 | ||
| 1175 | if (string.IsNullOrEmpty(fieldID)) | 1161 | if (string.IsNullOrEmpty(fieldID)) |
| @@ -0,0 +1,49 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // IRazorEngine.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this | ||
| 9 | // file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://opensource.org/licenses/MIT | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 05/05/2016 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // | ||
| 22 | //****************************************************************************************************** | ||
| 23 | |||
| 24 | using System; | ||
| 25 | using System.Threading.Tasks; | ||
| 26 | using RazorEngine.Templating; | ||
| 27 | |||
| 28 | namespace GSF.Web.Model | ||
| 29 | { | ||
| 30 | /// <summary> | ||
| 31 | /// Defines an interface for <see cref="RazorEngine{TLanguage}"/> implementations. | ||
| 32 | /// </summary> | ||
| 33 | public interface IRazorEngine : IRazorEngineService | ||
| 34 | { | ||
| 35 | /// <summary> | ||
| 36 | /// Gets the template path defined for this <see cref="IRazorEngine"/>. | ||
| 37 | /// </summary> | ||
| 38 | string TemplatePath | ||
| 39 | { | ||
| 40 | get; | ||
| 41 | } | ||
| 42 | |||
| 43 | /// <summary> | ||
| 44 | /// Kicks off a task to pre-compile Razor templates. | ||
| 45 | /// </summary> | ||
| 46 | /// <param name="exceptionHandler">Exception handler used to report issues, if any.</param> | ||
| 47 | Task PreCompile(Action<Exception> exceptionHandler = null); | ||
| 48 | } | ||
| 49 | } | ||
| @@ -1,112 +0,0 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // IRazorView.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this | ||
| 9 | // file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://opensource.org/licenses/MIT | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 05/05/2016 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // | ||
| 22 | //****************************************************************************************************** | ||
| 23 | |||
| 24 | using System; | ||
| 25 | using System.Collections.Generic; | ||
| 26 | using System.Net.Http; | ||
| 27 | using System.Threading.Tasks; | ||
| 28 | |||
| 29 | namespace GSF.Web.Model | ||
| 30 | { | ||
| 31 | /// <summary> | ||
| 32 | /// Defines an interface for <see cref="RazorView{TLanguage}"/> implementations. | ||
| 33 | /// </summary> | ||
| 34 | public interface IRazorView | ||
| 35 | { | ||
| 36 | /// <summary> | ||
| 37 | /// Gets or sets name of template file. | ||
| 38 | /// </summary> | ||
| 39 | string TemplateName | ||
| 40 | { | ||
| 41 | get; set; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// <summary> | ||
| 45 | /// Gets or sets reference to model to use when rendering template. | ||
| 46 | /// </summary> | ||
| 47 | object Model | ||
| 48 | { | ||
| 49 | get; set; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// <summary> | ||
| 53 | /// Gets or sets type of <see cref="Model"/>. | ||
| 54 | /// </summary> | ||
| 55 | Type ModelType | ||
| 56 | { | ||
| 57 | get; set; | ||
| 58 | } | ||
| 59 | |||
| 60 | /// <summary> | ||
| 61 | /// Gets or sets delegate used to handle exceptions. | ||
| 62 | /// </summary> | ||
| 63 | Action<Exception> ExceptionHandler | ||
| 64 | { | ||
| 65 | get; set; | ||
| 66 | } | ||
| 67 | |||
| 68 | /// <summary> | ||
| 69 | /// Gets reference to view bag used when rendering template. | ||
| 70 | /// </summary> | ||
| 71 | dynamic ViewBag | ||
| 72 | { | ||
| 73 | get; | ||
| 74 | } | ||
| 75 | |||
| 76 | /// <summary> | ||
| 77 | /// Gets query string parameter specified by <paramref name="key"/>. | ||
| 78 | /// </summary> | ||
| 79 | /// <param name="key">Name of query string parameter to retrieve.</param> | ||
| 80 | /// <returns>Query string parameter specified by <paramref name="key"/>.</returns> | ||
| 81 | string this[string key] | ||
| 82 | { | ||
| 83 | get; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// <summary> | ||
| 87 | /// Gets a dictionary of query string parameters passed to rendered view. | ||
| 88 | /// </summary> | ||
| 89 | Dictionary<string, string> Parameters | ||
| 90 | { | ||
| 91 | get; | ||
| 92 | } | ||
| 93 | |||
| 94 | /// <summary> | ||
| 95 | /// Compiles and executes view template. | ||
| 96 | /// </summary> | ||
| 97 | /// <returns>Rendered result.</returns> | ||
| 98 | string Execute(); | ||
| 99 | |||
| 100 | /// <summary> | ||
| 101 | /// Compiles and executes view template for specified request message and post data. | ||
| 102 | /// </summary> | ||
| 103 | /// <returns>Rendered result.</returns> | ||
| 104 | string Execute(HttpRequestMessage requestMessage, dynamic postData); | ||
| 105 | |||
| 106 | /// <summary> | ||
| 107 | /// Asynchronously compiles and executes view template for specified request message and post data. | ||
| 108 | /// </summary> | ||
| 109 | /// <returns>Task that will provide rendered result.</returns> | ||
| 110 | Task ExecuteAsync(HttpRequestMessage requestMessage, dynamic postData); | ||
| 111 | } | ||
| 112 | } | ||
| @@ -0,0 +1,112 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // LanguageConstraint.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright � 2016, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this | ||
| 9 | // file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://opensource.org/licenses/MIT | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 05/05/2016 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // | ||
| 22 | //****************************************************************************************************** | ||
| 23 | |||
| 24 | using RazorEngine; | ||
| 25 | |||
| 26 | namespace GSF.Web.Model | ||
| 27 | { | ||
| 28 | /// <summary> | ||
| 29 | /// Defines a language constraint for a <see cref="RazorEngine{TLanguage}"/>. | ||
| 30 | /// </summary> | ||
| 31 | public abstract class LanguageConstraint | ||
| 32 | { | ||
| 33 | /// <summary> | ||
| 34 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation. | ||
| 35 | /// </summary> | ||
| 36 | public abstract Language TargetLanguage | ||
| 37 | { | ||
| 38 | get; | ||
| 39 | } | ||
| 40 | |||
| 41 | /// <summary> | ||
| 42 | /// Gets resolution mode for <see cref="RazorEngine{TLanguage}"/> views. | ||
| 43 | /// </summary> | ||
| 44 | public virtual RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.ResolvePath; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// <summary> | ||
| 48 | /// Defines a C# based language constraint. | ||
| 49 | /// </summary> | ||
| 50 | public class CSharp : LanguageConstraint | ||
| 51 | { | ||
| 52 | /// <summary> | ||
| 53 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation - C#. | ||
| 54 | /// </summary> | ||
| 55 | public override Language TargetLanguage => Language.CSharp; | ||
| 56 | } | ||
| 57 | |||
| 58 | /// <summary> | ||
| 59 | /// Defines a C# based language constraint in debug mode. | ||
| 60 | /// </summary> | ||
| 61 | public class CSharpDebug : CSharp | ||
| 62 | { | ||
| 63 | /// <summary> | ||
| 64 | /// Gets resolution mode for <see cref="RazorEngine{TLanguage}"/> views - watching resolve path for debugging operations. | ||
| 65 | /// </summary> | ||
| 66 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.WatchingResolvePath; | ||
| 67 | } | ||
| 68 | |||
| 69 | /// <summary> | ||
| 70 | /// Defines a C# based language constraint for embedded resources. | ||
| 71 | /// </summary> | ||
| 72 | public class CSharpEmbeddedResource : CSharp | ||
| 73 | { | ||
| 74 | /// <summary> | ||
| 75 | /// Gets resolution mode for <see cref="RazorEngine{TLanguage}"/> views - embedded resources. | ||
| 76 | /// </summary> | ||
| 77 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.EmbeddedResource; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// <summary> | ||
| 81 | /// Defines a Visual Basic based language constraint. | ||
| 82 | /// </summary> | ||
| 83 | public class VisualBasic : LanguageConstraint | ||
| 84 | { | ||
| 85 | /// <summary> | ||
| 86 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation - Visual Basic. | ||
| 87 | /// </summary> | ||
| 88 | public override Language TargetLanguage => Language.VisualBasic; | ||
| 89 | } | ||
| 90 | |||
| 91 | /// <summary> | ||
| 92 | /// Defines a Visual Basic based language constraint in debug mode. | ||
| 93 | /// </summary> | ||
| 94 | public class VisualBasicDebug : VisualBasic | ||
| 95 | { | ||
| 96 | /// <summary> | ||
| 97 | /// Gets resolution mode for <see cref="RazorEngine{TLanguage}"/> views - watching resolve path for debugging operations. | ||
| 98 | /// </summary> | ||
| 99 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.WatchingResolvePath; | ||
| 100 | } | ||
| 101 | |||
| 102 | /// <summary> | ||
| 103 | /// Defines a Visual Basic based language constraint for embedded resources. | ||
| 104 | /// </summary> | ||
| 105 | public class VisualBasicEmbeddedResource : VisualBasic | ||
| 106 | { | ||
| 107 | /// <summary> | ||
| 108 | /// Gets resolution mode for <see cref="RazorEngine{TLanguage}"/> views - embedded resources. | ||
| 109 | /// </summary> | ||
| 110 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.EmbeddedResource; | ||
| 111 | } | ||
| 112 | } | ||
| @@ -0,0 +1,330 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // RazorEngine.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this | ||
| 9 | // file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://opensource.org/licenses/MIT | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 05/05/2016 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // | ||
| 22 | //****************************************************************************************************** | ||
| 23 | |||
| 24 | using System; | ||
| 25 | using System.Collections.Concurrent; | ||
| 26 | using System.IO; | ||
| 27 | using System.Linq; | ||
| 28 | using System.Reflection; | ||
| 29 | using System.Threading.Tasks; | ||
| 30 | using System.Web.Hosting; | ||
| 31 | using GSF.Configuration; | ||
| 32 | using GSF.IO; | ||
| 33 | using RazorEngine; | ||
| 34 | using RazorEngine.Configuration; | ||
| 35 | using RazorEngine.Templating; | ||
| 36 | |||
| 37 | namespace GSF.Web.Model | ||
| 38 | { | ||
| 39 | /// <summary> | ||
| 40 | /// Defines a Razor engine instance for the specified <typeparamref name="TLanguage"/> constraint. | ||
| 41 | /// </summary> | ||
| 42 | /// <typeparam name="TLanguage"><see cref="LanguageConstraint"/> for the Razor engine.</typeparam> | ||
| 43 | public class RazorEngine<TLanguage> : IRazorEngine where TLanguage : LanguageConstraint, new() | ||
| 44 | { | ||
| 45 | #region [ Members ] | ||
| 46 | |||
| 47 | // Fields | ||
| 48 | private readonly IRazorEngineService m_engineService; | ||
| 49 | private bool m_disposed; | ||
| 50 | |||
| 51 | #endregion | ||
| 52 | |||
| 53 | #region [ Constructors ] | ||
| 54 | |||
| 55 | /// <summary> | ||
| 56 | /// Creates a new <see cref="RazorEngine{TLanguage}"/> instance. | ||
| 57 | /// </summary> | ||
| 58 | /// <param name="templatePath">Template path for view files, if applicable.</param> | ||
| 59 | public RazorEngine(string templatePath) | ||
| 60 | { | ||
| 61 | if (string.IsNullOrEmpty(templatePath)) | ||
| 62 | throw new ArgumentNullException(nameof(templatePath)); | ||
| 63 | |||
| 64 | TLanguage languageType = new TLanguage(); | ||
| 65 | |||
| 66 | switch (languageType.ResolutionMode) | ||
| 67 | { | ||
| 68 | case RazorViewResolutionMode.ResolvePath: | ||
| 69 | case RazorViewResolutionMode.WatchingResolvePath: | ||
| 70 | if (!Directory.Exists(templatePath)) | ||
| 71 | throw new DirectoryNotFoundException(); | ||
| 72 | |||
| 73 | if (languageType.ResolutionMode == RazorViewResolutionMode.ResolvePath) | ||
| 74 | { | ||
| 75 | m_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 76 | { | ||
| 77 | Language = languageType.TargetLanguage, | ||
| 78 | TemplateManager = new ResolvePathTemplateManager(new[] { templatePath }), | ||
| 79 | Debug = false | ||
| 80 | }); | ||
| 81 | } | ||
| 82 | else | ||
| 83 | { | ||
| 84 | // The watching resolve path template manager should not be used in production since | ||
| 85 | // assemblies cannot be unloaded from an AppDomain. Every time a change to a .cshtml | ||
| 86 | // file is picked up by the watcher it is compiled and loaded into the AppDomain and | ||
| 87 | // the old one cannot be removed (.NET restriction), the net result is a memory leak | ||
| 88 | InvalidatingCachingProvider cachingProvider = new InvalidatingCachingProvider(); | ||
| 89 | |||
| 90 | m_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 91 | { | ||
| 92 | Language = languageType.TargetLanguage, | ||
| 93 | CachingProvider = cachingProvider, | ||
| 94 | TemplateManager = new WatchingResolvePathTemplateManager(new[] { templatePath }, cachingProvider), | ||
| 95 | Debug = true | ||
| 96 | }); | ||
| 97 | } | ||
| 98 | break; | ||
| 99 | case RazorViewResolutionMode.EmbeddedResource: | ||
| 100 | m_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 101 | { | ||
| 102 | Language = languageType.TargetLanguage, | ||
| 103 | TemplateManager = new DelegateTemplateManager(name => | ||
| 104 | { | ||
| 105 | string resourceName = name; | ||
| 106 | |||
| 107 | if (!resourceName.Contains(".")) | ||
| 108 | resourceName = templatePath + name; | ||
| 109 | |||
| 110 | Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); | ||
| 111 | |||
| 112 | if ((object)stream == null) | ||
| 113 | return ""; | ||
| 114 | |||
| 115 | using (StreamReader reader = new StreamReader(stream)) | ||
| 116 | return reader.ReadToEnd(); | ||
| 117 | }), | ||
| 118 | Debug = false | ||
| 119 | }); | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | |||
| 123 | TemplatePath = templatePath; | ||
| 124 | } | ||
| 125 | |||
| 126 | #endregion | ||
| 127 | |||
| 128 | #region [ Properties ] | ||
| 129 | |||
| 130 | /// <summary> | ||
| 131 | /// Gets the template path defined for this <see cref="RazorEngine{TLanguage}"/>. | ||
| 132 | /// </summary> | ||
| 133 | public string TemplatePath | ||
| 134 | { | ||
| 135 | get; | ||
| 136 | } | ||
| 137 | |||
| 138 | #endregion | ||
| 139 | |||
| 140 | #region [ Methods ] | ||
| 141 | |||
| 142 | /// <summary> | ||
| 143 | /// Releases all the resources used by the <see cref="RazorEngine{TLanguage}"/> object. | ||
| 144 | /// </summary> | ||
| 145 | public void Dispose() | ||
| 146 | { | ||
| 147 | Dispose(true); | ||
| 148 | GC.SuppressFinalize(this); | ||
| 149 | } | ||
| 150 | |||
| 151 | /// <summary> | ||
| 152 | /// Releases the unmanaged resources used by the <see cref="RazorEngine{TLanguage}"/> object and optionally releases the managed resources. | ||
| 153 | /// </summary> | ||
| 154 | /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> | ||
| 155 | protected virtual void Dispose(bool disposing) | ||
| 156 | { | ||
| 157 | if (!m_disposed) | ||
| 158 | { | ||
| 159 | try | ||
| 160 | { | ||
| 161 | if (disposing) | ||
| 162 | m_engineService?.Dispose(); | ||
| 163 | } | ||
| 164 | finally | ||
| 165 | { | ||
| 166 | m_disposed = true; // Prevent duplicate dispose. | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | /// <summary> | ||
| 172 | /// Gets a given key from the <see cref="ITemplateManager" /> implementation. See <see cref="ITemplateManager.GetKey(String,ResolveType,ITemplateKey)" />. | ||
| 173 | /// </summary> | ||
| 174 | /// <returns></returns> | ||
| 175 | public ITemplateKey GetKey(string name, ResolveType resolveType = ResolveType.Global, ITemplateKey context = null) | ||
| 176 | { | ||
| 177 | return m_engineService.GetKey(name, resolveType, context); | ||
| 178 | } | ||
| 179 | |||
| 180 | /// <summary>Checks if a given template is already cached.</summary> | ||
| 181 | /// <returns></returns> | ||
| 182 | public bool IsTemplateCached(ITemplateKey key, Type modelType) | ||
| 183 | { | ||
| 184 | return m_engineService.IsTemplateCached(key, modelType); | ||
| 185 | } | ||
| 186 | |||
| 187 | /// <summary> | ||
| 188 | /// Adds a given template to the template manager as dynamic template. | ||
| 189 | /// </summary> | ||
| 190 | public void AddTemplate(ITemplateKey key, ITemplateSource templateSource) | ||
| 191 | { | ||
| 192 | m_engineService.AddTemplate(key, templateSource); | ||
| 193 | } | ||
| 194 | |||
| 195 | /// <summary>Compiles the specified template and caches it.</summary> | ||
| 196 | public void Compile(ITemplateKey key, Type modelType = null) | ||
| 197 | { | ||
| 198 | m_engineService.Compile(key, modelType); | ||
| 199 | } | ||
| 200 | |||
| 201 | /// <summary> | ||
| 202 | /// Runs the given cached template. When the cache does not contain the template it will be compiled and cached beforehand. | ||
| 203 | /// </summary> | ||
| 204 | public void RunCompile(ITemplateKey key, TextWriter writer, Type modelType = null, object model = null, DynamicViewBag viewBag = null) | ||
| 205 | { | ||
| 206 | m_engineService.RunCompile(key, writer, modelType, model, viewBag); | ||
| 207 | } | ||
| 208 | |||
| 209 | /// <summary>Runs the given cached template.</summary> | ||
| 210 | public void Run(ITemplateKey key, TextWriter writer, Type modelType = null, object model = null, DynamicViewBag viewBag = null) | ||
| 211 | { | ||
| 212 | m_engineService.Run(key, writer, modelType, model, viewBag); | ||
| 213 | } | ||
| 214 | |||
| 215 | /// <summary> | ||
| 216 | /// Kicks off a task to pre-compile Razor templates. | ||
| 217 | /// </summary> | ||
| 218 | /// <param name="exceptionHandler">Exception handler used to report issues, if any.</param> | ||
| 219 | public Task PreCompile(Action<Exception> exceptionHandler = null) | ||
| 220 | { | ||
| 221 | return Task.Run(() => | ||
| 222 | { | ||
| 223 | TLanguage languageType = new TLanguage(); | ||
| 224 | |||
| 225 | if (languageType.ResolutionMode == RazorViewResolutionMode.EmbeddedResource) | ||
| 226 | { | ||
| 227 | foreach (string fileName in Assembly.GetExecutingAssembly().GetManifestResourceNames().Where(fileName => fileName.StartsWith(TemplatePath))) | ||
| 228 | { | ||
| 229 | try | ||
| 230 | { | ||
| 231 | m_engineService.Compile(fileName.Substring(TemplatePath.Length)); | ||
| 232 | } | ||
| 233 | catch (Exception ex) | ||
| 234 | { | ||
| 235 | if ((object)exceptionHandler != null) | ||
| 236 | exceptionHandler(new InvalidOperationException($"Failed to pre-compile razor template \"{fileName}\": {ex.Message}", ex)); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | else | ||
| 241 | { | ||
| 242 | string webRootFolder = FilePath.AddPathSuffix(TemplatePath); | ||
| 243 | string[] razorFiles = FilePath.GetFileList($"{webRootFolder}*.{(languageType.TargetLanguage == Language.CSharp ? "cs" : "vb")}html"); | ||
| 244 | |||
| 245 | foreach (string fileName in razorFiles) | ||
| 246 | { | ||
| 247 | try | ||
| 248 | { | ||
| 249 | m_engineService.Compile(FilePath.GetFileName(fileName)); | ||
| 250 | } | ||
| 251 | catch (Exception ex) | ||
| 252 | { | ||
| 253 | if ((object)exceptionHandler != null) | ||
| 254 | exceptionHandler(new InvalidOperationException($"Failed to pre-compile razor template \"{fileName}\": {ex.Message}", ex)); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | }); | ||
| 259 | } | ||
| 260 | |||
| 261 | #endregion | ||
| 262 | |||
| 263 | #region [ Static ] | ||
| 264 | |||
| 265 | // Static Fields | ||
| 266 | private static RazorEngine<TLanguage> s_defaultEngine; | ||
| 267 | private static readonly ConcurrentDictionary<string, RazorEngine<TLanguage>> s_configuredEngine; | ||
| 268 | |||
| 269 | // Static Properties | ||
| 270 | |||
| 271 | /// <summary> | ||
| 272 | /// Gets default configured razor engine instance. | ||
| 273 | /// </summary> | ||
| 274 | public static RazorEngine<TLanguage> Default => s_defaultEngine ?? (s_defaultEngine = GetConfiguredEngine()); | ||
| 275 | |||
| 276 | // Static Constructor | ||
| 277 | static RazorEngine() | ||
| 278 | { | ||
| 279 | s_configuredEngine = new ConcurrentDictionary<string, RazorEngine<TLanguage>>(StringComparer.OrdinalIgnoreCase); | ||
| 280 | } | ||
| 281 | |||
| 282 | // Static Methods | ||
| 283 | |||
| 284 | /// <summary> | ||
| 285 | /// Gets a shared <see cref="RazorEngine{TLanguage}"/> instance created based on configured template path defined in the specified <paramref name="settingsCategory"/>. | ||
| 286 | /// </summary> | ||
| 287 | /// <param name="settingsCategory">Settings category to use for template path settings; defaults to "systemSettings".</param> | ||
| 288 | /// <returns>Shared <see cref="RazorEngine{TLanguage}"/> instance created based on configured template path.</returns> | ||
| 289 | public static RazorEngine<TLanguage> GetConfiguredEngine(string settingsCategory = null) | ||
| 290 | { | ||
| 291 | if (string.IsNullOrWhiteSpace(settingsCategory)) | ||
| 292 | settingsCategory = "systemSettings"; | ||
| 293 | |||
| 294 | return s_configuredEngine.GetOrAdd(settingsCategory, category => | ||
| 295 | { | ||
| 296 | string templatePath = null; | ||
| 297 | |||
| 298 | // Get configured template path | ||
| 299 | CategorizedSettingsElementCollection systemSettings = ConfigurationFile.Current.Settings[category]; | ||
| 300 | |||
| 301 | TLanguage languageType = new TLanguage(); | ||
| 302 | |||
| 303 | switch (languageType.ResolutionMode) | ||
| 304 | { | ||
| 305 | case RazorViewResolutionMode.ResolvePath: | ||
| 306 | case RazorViewResolutionMode.WatchingResolvePath: | ||
| 307 | if (Common.GetApplicationType() == ApplicationType.Web) | ||
| 308 | { | ||
| 309 | systemSettings.Add("TemplatePath", "~/Views/Shared/Templates/", "Path for data context based razor field templates."); | ||
| 310 | templatePath = HostingEnvironment.MapPath(systemSettings["TemplatePath"].Value).EnsureEnd('/'); | ||
| 311 | } | ||
| 312 | else | ||
| 313 | { | ||
| 314 | systemSettings.Add("TemplatePath", "wwwroot/Templates/", "Path for data context based razor field templates."); | ||
| 315 | templatePath = FilePath.GetAbsolutePath(systemSettings["TemplatePath"].Value); | ||
| 316 | } | ||
| 317 | break; | ||
| 318 | case RazorViewResolutionMode.EmbeddedResource: | ||
| 319 | systemSettings.Add("EmbeddedTemplatePath", "GSF.Web.Model.Views.", "Embedded name space path for data context based razor field templates."); | ||
| 320 | templatePath = systemSettings["EmbeddedTemplatePath"].Value.EnsureEnd('.'); | ||
| 321 | break; | ||
| 322 | } | ||
| 323 | |||
| 324 | return new RazorEngine<TLanguage>(templatePath); | ||
| 325 | }); | ||
| 326 | } | ||
| 327 | |||
| 328 | #endregion | ||
| 329 | } | ||
| 330 | } | ||
| @@ -23,175 +23,59 @@ · 22 unmodified lines | |||
| 23 | 23 | ||
| 24 | using System; | 24 | using System; |
| 25 | using System.Collections.Generic; | 25 | using System.Collections.Generic; |
| 26 | using System.IO; | ||
| 27 | using System.Linq; | ||
| 28 | using System.Net.Http; | 26 | using System.Net.Http; |
| 29 | using System.Reflection; | ||
| 30 | using System.Threading.Tasks; | 27 | using System.Threading.Tasks; |
| 31 | using System.Web; | 28 | using System.Web; |
| 32 | using System.Web.Hosting; | ||
| 33 | using GSF.Collections; | 29 | using GSF.Collections; |
| 34 | using GSF.Configuration; | 30 | using GSF.Data; |
| 35 | using GSF.IO; | ||
| 36 | using RazorEngine; | ||
| 37 | using RazorEngine.Configuration; | ||
| 38 | using RazorEngine.Templating; | 31 | using RazorEngine.Templating; |
| 39 | 32 | ||
| 40 | // ReSharper disable StaticMemberInGenericType | 33 | // ReSharper disable StaticMemberInGenericType |
| 41 | namespace GSF.Web.Model | 34 | namespace GSF.Web.Model |
| 42 | { | 35 | { |
| 43 | /// <summary> | ||
| 44 | /// Defines resolutions modes for Razor view template files. | ||
| 45 | /// </summary> | ||
| 46 | public enum RazorViewResolutionMode | ||
| 47 | { | ||
| 48 | /// <summary> | ||
| 49 | /// Resolves template files from a path. | ||
| 50 | /// </summary> | ||
| 51 | ResolvePath, | ||
| 52 | /// <summary> | ||
| 53 | /// Resolves template files from a path and watches for changes. | ||
| 54 | /// </summary> | ||
| 55 | /// <remarks> | ||
| 56 | /// This should only be used in debug mode. | ||
| 57 | /// </remarks> | ||
| 58 | WatchingResolvePath, | ||
| 59 | /// <summary> | ||
| 60 | /// Resolves template files from an embedded resource. | ||
| 61 | /// </summary> | ||
| 62 | EmbeddedResource | ||
| 63 | } | ||
| 64 | |||
| 65 | /// <summary> | ||
| 66 | /// Defines a language constraint for a <see cref="RazorView{TLanguage}"/>. | ||
| 67 | /// </summary> | ||
| 68 | public abstract class LanguageConstraint | ||
| 69 | { | ||
| 70 | /// <summary> | ||
| 71 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation. | ||
| 72 | /// </summary> | ||
| 73 | public abstract Language TargetLanguage | ||
| 74 | { | ||
| 75 | get; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// <summary> | ||
| 79 | /// Gets resolution mode of <see cref="RazorView{TLanguage}"/>. | ||
| 80 | /// </summary> | ||
| 81 | public virtual RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.ResolvePath; | ||
| 82 | } | ||
| 83 | |||
| 84 | /// <summary> | ||
| 85 | /// Defines a C# based language constraint. | ||
| 86 | /// </summary> | ||
| 87 | public class CSharp : LanguageConstraint | ||
| 88 | { | ||
| 89 | /// <summary> | ||
| 90 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation - C#. | ||
| 91 | /// </summary> | ||
| 92 | public override Language TargetLanguage => Language.CSharp; | ||
| 93 | } | ||
| 94 | |||
| 95 | /// <summary> | ||
| 96 | /// Defines a C# based language constraint in debug mode. | ||
| 97 | /// </summary> | ||
| 98 | public class CSharpDebug : CSharp | ||
| 99 | { | ||
| 100 | /// <summary> | ||
| 101 | /// Gets resolution mode of <see cref="RazorView{TLanguage}"/> - watching resolve path for debugging operations. | ||
| 102 | /// </summary> | ||
| 103 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.WatchingResolvePath; | ||
| 104 | } | ||
| 105 | |||
| 106 | /// <summary> | ||
| 107 | /// Defines a C# based language constraint for embedded resources. | ||
| 108 | /// </summary> | ||
| 109 | public class CSharpEmbeddedResource : CSharp | ||
| 110 | { | ||
| 111 | /// <summary> | ||
| 112 | /// Gets resolution mode of <see cref="RazorView{TLanguage}"/> - embedded resources. | ||
| 113 | /// </summary> | ||
| 114 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.EmbeddedResource; | ||
| 115 | } | ||
| 116 | |||
| 117 | /// <summary> | ||
| 118 | /// Defines a Visual Basic based language constraint. | ||
| 119 | /// </summary> | ||
| 120 | public class VisualBasic : LanguageConstraint | ||
| 121 | { | ||
| 122 | /// <summary> | ||
| 123 | /// Gets target language for the <see cref="LanguageConstraint"/> implementation - Visual Basic. | ||
| 124 | /// </summary> | ||
| 125 | public override Language TargetLanguage => Language.VisualBasic; | ||
| 126 | } | ||
| 127 | |||
| 128 | /// <summary> | ||
| 129 | /// Defines a Visual Basic based language constraint in debug mode. | ||
| 130 | /// </summary> | ||
| 131 | public class VisualBasicDebug : VisualBasic | ||
| 132 | { | ||
| 133 | /// <summary> | ||
| 134 | /// Gets resolution mode of <see cref="RazorView{TLanguage}"/> - watching resolve path for debugging operations. | ||
| 135 | /// </summary> | ||
| 136 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.WatchingResolvePath; | ||
| 137 | } | ||
| 138 | |||
| 139 | /// <summary> | ||
| 140 | /// Defines a Visual Basic based language constraint for embedded resources. | ||
| 141 | /// </summary> | ||
| 142 | public class VisualBasicEmbeddedResource : VisualBasic | ||
| 143 | { | ||
| 144 | /// <summary> | ||
| 145 | /// Gets resolution mode of <see cref="RazorView{TLanguage}"/> - embedded resources. | ||
| 146 | /// </summary> | ||
| 147 | public override RazorViewResolutionMode ResolutionMode => RazorViewResolutionMode.EmbeddedResource; | ||
| 148 | } | ||
| 149 | |||
| 150 | /// <summary> | 36 | /// <summary> |
| 151 | /// Defines a view class for data context based Razor template implementations. | 37 | /// Defines a view class for data context based Razor template implementations. |
| 152 | /// </summary> | 38 | /// </summary> |
| 153 | /// <typeparam name="TLanguage"><see cref="LanguageConstraint"/> type to use for Razor views.</typeparam> | 39 | public class RazorView |
| 154 | public class RazorView<TLanguage> : IRazorView where TLanguage : LanguageConstraint, new() | ||
| 155 | { | 40 | { |
| 156 | #region [ Members ] | 41 | #region [ Members ] |
| 157 | 42 | ||
| 158 | // Fields | 43 | // Fields |
| 44 | private readonly IRazorEngine m_razorEngine; | ||
| 45 | |||
| 159 | private readonly DynamicViewBag m_viewBag = new DynamicViewBag(); | 46 | private readonly DynamicViewBag m_viewBag = new DynamicViewBag(); |
| 160 | private Dictionary<string, string> m_parameters; | 47 | private Dictionary<string, string> m_parameters; |
| 161 | 48 | ||
| 162 | #endregion | 49 | #endregion |
| 163 | 50 | ||
| 164 | #region [ Constructors ] | 51 | #region [ Constructors ] |
| 165 | |||
| 166 | /// <summary> | ||
| 167 | /// Creates a new <see cref="RazorView{TLanguage}"/>. | ||
| 168 | /// </summary> | ||
| 169 | public RazorView() | ||
| 170 | { | ||
| 171 | } | ||
| 172 | |||
| 173 | /// <summary> | 52 | /// <summary> |
| 174 | /// Creates a new <see cref="RazorView{TLanguage}"/> with the specified exception handler. | 53 | /// Creates a new <see cref="RazorView"/>. |
| 175 | /// </summary> | 54 | /// </summary> |
| 55 | /// <param name="razorEngine"><see cref="IRazorEngine"/> instance to use.</param> | ||
| 176 | /// <param name="templateName">Name of template file, typically a .cshtml or .vbhtml file.</param> | 56 | /// <param name="templateName">Name of template file, typically a .cshtml or .vbhtml file.</param> |
| 177 | /// <param name="exceptionHandler">Delegate to handle exceptions, if any.</param> | 57 | /// <param name="exceptionHandler">Delegate to handle exceptions, if any.</param> |
| 178 | public RazorView(string templateName, Action<Exception> exceptionHandler = null) | 58 | public RazorView(IRazorEngine razorEngine, string templateName, Action<Exception> exceptionHandler = null) : this(razorEngine, templateName, null, null, null, exceptionHandler) |
| 179 | { | 59 | { |
| 180 | TemplateName = templateName; | ||
| 181 | ExceptionHandler = exceptionHandler; | ||
| 182 | } | 60 | } |
| 183 | 61 | ||
| 184 | /// <summary> | 62 | /// <summary> |
| 185 | /// Creates a new <see cref="RazorView{TLanguage}"/> with the specified parameters. | 63 | /// Creates a new <see cref="RazorView"/>. |
| 186 | /// </summary> | 64 | /// </summary> |
| 65 | /// <param name="razorEngine"><see cref="IRazorEngine"/> instance to use.</param> | ||
| 187 | /// <param name="templateName">Name of template file, typically a .cshtml or .vbhtml file.</param> | 66 | /// <param name="templateName">Name of template file, typically a .cshtml or .vbhtml file.</param> |
| 188 | /// <param name="model">Reference to model to use when rendering template.</param> | 67 | /// <param name="model">Reference to model to use when rendering template.</param> |
| 189 | /// <param name="modelType">Type of <paramref name="model"/>.</param> | 68 | /// <param name="modelType">Type of <paramref name="model"/>.</param> |
| 69 | /// <param name="connection"><see cref="AdoDataConnection"/> to use, if any.</param> | ||
| 190 | /// <param name="exceptionHandler">Delegate to handle exceptions, if any.</param> | 70 | /// <param name="exceptionHandler">Delegate to handle exceptions, if any.</param> |
| 191 | public RazorView(string templateName, object model, Type modelType, Action<Exception> exceptionHandler = null) : this(templateName, exceptionHandler) | 71 | public RazorView(IRazorEngine razorEngine, string templateName, object model = null, Type modelType = null, AdoDataConnection connection = null, Action<Exception> exceptionHandler = null) |
| 192 | { | 72 | { |
| 73 | m_razorEngine = razorEngine; | ||
| 74 | TemplateName = templateName; | ||
| 193 | Model = model; | 75 | Model = model; |
| 194 | ModelType = modelType; | 76 | ModelType = modelType; |
| 77 | Connection = connection; | ||
| 78 | ExceptionHandler = exceptionHandler; | ||
| 195 | } | 79 | } |
| 196 | 80 | ||
| 197 | #endregion | 81 | #endregion |
| @@ -222,14 +106,6 @@ namespace GSF.Web.Model · 24 unmodified lines | |||
| 222 | get; set; | 106 | get; set; |
| 223 | } | 107 | } |
| 224 | 108 | ||
| 225 | /// <summary> | ||
| 226 | /// Gets or sets delegate used to handle exceptions. | ||
| 227 | /// </summary> | ||
| 228 | public Action<Exception> ExceptionHandler | ||
| 229 | { | ||
| 230 | get; set; | ||
| 231 | } | ||
| 232 | |||
| 233 | /// <summary> | 109 | /// <summary> |
| 234 | /// Gets reference to view bag used when rendering template. | 110 | /// Gets reference to view bag used when rendering template. |
| 235 | /// </summary> | 111 | /// </summary> |
| @@ -259,6 +135,43 @@ namespace GSF.Web.Model · 23 unmodified lines | |||
| 259 | } | 135 | } |
| 260 | } | 136 | } |
| 261 | 137 | ||
| 138 | /// <summary> | ||
| 139 | /// Gets reference to <see cref="IRazorEngine"/> used by this <see cref="RazorView"/>. | ||
| 140 | /// </summary> | ||
| 141 | public IRazorEngine RazorEngine => m_razorEngine; | ||
| 142 | |||
| 143 | /// <summary> | ||
| 144 | /// Gets or sets data connection to provide to <see cref="DataContext"/>, if any. | ||
| 145 | /// </summary> | ||
| 146 | public AdoDataConnection Connection | ||
| 147 | { | ||
| 148 | get; | ||
| 149 | set; | ||
| 150 | } | ||
| 151 | |||
| 152 | /// <summary> | ||
| 153 | /// Gets or sets delegate used to handle exceptions. | ||
| 154 | /// </summary> | ||
| 155 | public Action<Exception> ExceptionHandler | ||
| 156 | { | ||
| 157 | get; set; | ||
| 158 | } | ||
| 159 | |||
| 160 | /// <summary> | ||
| 161 | /// Gets or sets <see cref="IRazorEngine"/> to use for the <see cref="DataContext"/> provided to the view; defaults to <c>null</c>. | ||
| 162 | /// </summary> | ||
| 163 | /// <remarks> | ||
| 164 | /// This should typically not be changed. When value is <c>null</c>, the data context will use the default | ||
| 165 | /// <see cref="RazorEngine{TLanguage}"/> instance for <see cref="CSharpEmbeddedResource"/> which is used | ||
| 166 | /// to generate template based HTML input fields for a view. The default HTML input templates are defined | ||
| 167 | /// as embedded resources in GSF.Web. | ||
| 168 | /// </remarks> | ||
| 169 | public IRazorEngine DataContextRazorEngine | ||
| 170 | { | ||
| 171 | get; | ||
| 172 | set; | ||
| 173 | } | ||
| 174 | |||
| 262 | #endregion | 175 | #endregion |
| 263 | 176 | ||
| 264 | #region [ Methods ] | 177 | #region [ Methods ] |
| @@ -269,10 +182,10 @@ namespace GSF.Web.Model · 4 unmodified lines | |||
| 269 | /// <returns>Rendered result.</returns> | 182 | /// <returns>Rendered result.</returns> |
| 270 | public string Execute() | 183 | public string Execute() |
| 271 | { | 184 | { |
| 272 | using (DataContext<TLanguage> dataContext = new DataContext<TLanguage>(exceptionHandler: ExceptionHandler)) | 185 | using (DataContext dataContext = new DataContext(Connection, razorEngine: DataContextRazorEngine, exceptionHandler: ExceptionHandler)) |
| 273 | { | 186 | { |
| 274 | m_viewBag.AddValue("DataContext", dataContext); | 187 | m_viewBag.AddValue("DataContext", dataContext); |
| 275 | return s_engineService.RunCompile(TemplateName, ModelType, Model, m_viewBag); | 188 | return m_razorEngine.RunCompile(TemplateName, ModelType, Model, m_viewBag); |
| 276 | } | 189 | } |
| 277 | } | 190 | } |
| 278 | 191 | ||
| @@ -282,7 +195,7 @@ namespace GSF.Web.Model · 3 unmodified lines | |||
| 282 | /// <returns>Rendered result.</returns> | 195 | /// <returns>Rendered result.</returns> |
| 283 | public string Execute(HttpRequestMessage requestMessage, dynamic postData) | 196 | public string Execute(HttpRequestMessage requestMessage, dynamic postData) |
| 284 | { | 197 | { |
| 285 | using (DataContext<TLanguage> dataContext = new DataContext<TLanguage>(exceptionHandler: ExceptionHandler)) | 198 | using (DataContext dataContext = new DataContext(Connection, razorEngine: DataContextRazorEngine, exceptionHandler: ExceptionHandler)) |
| 286 | { | 199 | { |
| 287 | m_viewBag.AddValue("DataContext", dataContext); | 200 | m_viewBag.AddValue("DataContext", dataContext); |
| 288 | m_viewBag.AddValue("Request", requestMessage); | 201 | m_viewBag.AddValue("Request", requestMessage); |
| @@ -297,7 +210,7 @@ namespace GSF.Web.Model · 8 unmodified lines | |||
| 297 | m_viewBag.AddValue("PostData", postData); | 210 | m_viewBag.AddValue("PostData", postData); |
| 298 | } | 211 | } |
| 299 | 212 | ||
| 300 | return s_engineService.RunCompile(TemplateName, ModelType, Model, m_viewBag); | 213 | return m_razorEngine.RunCompile(TemplateName, ModelType, Model, m_viewBag); |
| 301 | } | 214 | } |
| 302 | } | 215 | } |
| 303 | 216 | ||
| @@ -311,141 +224,5 @@ namespace GSF.Web.Model · 7 unmodified lines | |||
| 311 | } | 224 | } |
| 312 | 225 | ||
| 313 | #endregion | 226 | #endregion |
| 314 | |||
| 315 | #region [ Static ] | ||
| 316 | |||
| 317 | // Static Fields | ||
| 318 | private static readonly IRazorEngineService s_engineService; | ||
| 319 | |||
| 320 | /// <summary> | ||
| 321 | /// Defines default settings path for template files. | ||
| 322 | /// </summary> | ||
| 323 | public static readonly string TemplatePath; | ||
| 324 | |||
| 325 | // Static Constructor | ||
| 326 | static RazorView() | ||
| 327 | { | ||
| 328 | // The current thinking for this class is that there will only be one set of Razor views per application instance | ||
| 329 | TLanguage languageType = new TLanguage(); | ||
| 330 | |||
| 331 | // Get configured template path | ||
| 332 | CategorizedSettingsElementCollection systemSettings = ConfigurationFile.Current.Settings["systemSettings"]; | ||
| 333 | |||
| 334 | switch (languageType.ResolutionMode) | ||
| 335 | { | ||
| 336 | case RazorViewResolutionMode.ResolvePath: | ||
| 337 | case RazorViewResolutionMode.WatchingResolvePath: | ||
| 338 | if (Common.GetApplicationType() == ApplicationType.Web) | ||
| 339 | { | ||
| 340 | systemSettings.Add("TemplatePath", "~/Views/Shared/Templates/", "Path for data context based razor field templates."); | ||
| 341 | TemplatePath = HostingEnvironment.MapPath(systemSettings["TemplatePath"].Value).EnsureEnd('/'); | ||
| 342 | } | ||
| 343 | else | ||
| 344 | { | ||
| 345 | systemSettings.Add("TemplatePath", "wwwroot/Templates/", "Path for data context based razor field templates."); | ||
| 346 | TemplatePath = FilePath.GetAbsolutePath(systemSettings["TemplatePath"].Value); | ||
| 347 | } | ||
| 348 | |||
| 349 | if (languageType.ResolutionMode == RazorViewResolutionMode.ResolvePath) | ||
| 350 | { | ||
| 351 | s_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 352 | { | ||
| 353 | Language = languageType.TargetLanguage, | ||
| 354 | TemplateManager = new ResolvePathTemplateManager(new[] { TemplatePath }), | ||
| 355 | Debug = false | ||
| 356 | }); | ||
| 357 | } | ||
| 358 | else | ||
| 359 | { | ||
| 360 | // The watching resolve path template manager should not be used in production since | ||
| 361 | // assemblies cannot be unloaded from an AppDomain. Every time a change to a .cshtml | ||
| 362 | // file is picked up by the watcher it is compiled and loaded into the AppDomain and | ||
| 363 | // the old one cannot be removed (.NET restriction), the net result is a memory leak | ||
| 364 | InvalidatingCachingProvider cachingProvider = new InvalidatingCachingProvider(); | ||
| 365 | |||
| 366 | s_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 367 | { | ||
| 368 | Language = languageType.TargetLanguage, | ||
| 369 | CachingProvider = cachingProvider, | ||
| 370 | TemplateManager = new WatchingResolvePathTemplateManager(new[] { TemplatePath }, cachingProvider), | ||
| 371 | Debug = true | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | break; | ||
| 375 | case RazorViewResolutionMode.EmbeddedResource: | ||
| 376 | systemSettings.Add("EmbeddedTemplatePath", "GSF.Web.Model.Views.", "Embedded name space path for data context based razor field templates."); | ||
| 377 | TemplatePath = systemSettings["EmbeddedTemplatePath"].Value.EnsureEnd('.'); | ||
| 378 | |||
| 379 | s_engineService = RazorEngineService.Create(new TemplateServiceConfiguration | ||
| 380 | { | ||
| 381 | Language = languageType.TargetLanguage, | ||
| 382 | TemplateManager = new DelegateTemplateManager(name => | ||
| 383 | { | ||
| 384 | string resourceName = name; | ||
| 385 | |||
| 386 | if (!resourceName.Contains(".")) | ||
| 387 | resourceName = TemplatePath + name; | ||
| 388 | |||
| 389 | Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); | ||
| 390 | |||
| 391 | if ((object)stream == null) | ||
| 392 | return ""; | ||
| 393 | |||
| 394 | using (StreamReader reader = new StreamReader(stream)) | ||
| 395 | return reader.ReadToEnd(); | ||
| 396 | }), | ||
| 397 | Debug = false | ||
| 398 | }); | ||
| 399 | break; | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | /// <summary> | ||
| 404 | /// Kicks off a task to pre-compile Razor templates. | ||
| 405 | /// </summary> | ||
| 406 | /// <param name="exceptionHandler">Exception handler used to report issues, if any.</param> | ||
| 407 | public static Task PreCompile(Action<Exception> exceptionHandler = null) | ||
| 408 | { | ||
| 409 | return Task.Run(() => | ||
| 410 | { | ||
| 411 | TLanguage languageType = new TLanguage(); | ||
| 412 | |||
| 413 | if (languageType.ResolutionMode == RazorViewResolutionMode.EmbeddedResource) | ||
| 414 | { | ||
| 415 | foreach (string fileName in Assembly.GetExecutingAssembly().GetManifestResourceNames().Where(fileName => fileName.StartsWith(TemplatePath))) | ||
| 416 | { | ||
| 417 | try | ||
| 418 | { | ||
| 419 | s_engineService.Compile(fileName.Substring(TemplatePath.Length)); | ||
| 420 | } | ||
| 421 | catch (Exception ex) | ||
| 422 | { | ||
| 423 | if ((object)exceptionHandler != null) | ||
| 424 | exceptionHandler(new InvalidOperationException($"Failed to pre-compile razor template \"{fileName}\": {ex.Message}", ex)); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | } | ||
| 428 | else | ||
| 429 | { | ||
| 430 | string webRootFolder = FilePath.AddPathSuffix(TemplatePath); | ||
| 431 | string[] razorFiles = FilePath.GetFileList($"{webRootFolder}*.{(languageType.TargetLanguage == Language.CSharp ? "cs" : "vb")}html"); | ||
| 432 | |||
| 433 | foreach (string fileName in razorFiles) | ||
| 434 | { | ||
| 435 | try | ||
| 436 | { | ||
| 437 | s_engineService.Compile(FilePath.GetFileName(fileName)); | ||
| 438 | } | ||
| 439 | catch (Exception ex) | ||
| 440 | { | ||
| 441 | if ((object)exceptionHandler != null) | ||
| 442 | exceptionHandler(new InvalidOperationException($"Failed to pre-compile razor template \"{fileName}\": {ex.Message}", ex)); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
| 446 | }); | ||
| 447 | } | ||
| 448 | |||
| 449 | #endregion | ||
| 450 | } | 227 | } |
| 451 | } | 228 | } |
| @@ -0,0 +1,47 @@ | |||
| 1 | //****************************************************************************************************** | ||
| 2 | // RazorViewResolutionMode.cs - Gbtc | ||
| 3 | // | ||
| 4 | // Copyright � 2016, Grid Protection Alliance. All Rights Reserved. | ||
| 5 | // | ||
| 6 | // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See | ||
| 7 | // the NOTICE file distributed with this work for additional information regarding copyright ownership. | ||
| 8 | // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this | ||
| 9 | // file except in compliance with the License. You may obtain a copy of the License at: | ||
| 10 | // | ||
| 11 | // http://opensource.org/licenses/MIT | ||
| 12 | // | ||
| 13 | // Unless agreed to in writing, the subject software distributed under the License is distributed on an | ||
| 14 | // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the | ||
| 15 | // License for the specific language governing permissions and limitations. | ||
| 16 | // | ||
| 17 | // Code Modification History: | ||
| 18 | // ---------------------------------------------------------------------------------------------------- | ||
| 19 | // 05/05/2016 - J. Ritchie Carroll | ||
| 20 | // Generated original version of source code. | ||
| 21 | // | ||
| 22 | //****************************************************************************************************** | ||
| 23 | |||
| 24 | namespace GSF.Web.Model | ||
| 25 | { | ||
| 26 | /// <summary> | ||
| 27 | /// Defines resolutions modes for Razor view template files. | ||
| 28 | /// </summary> | ||
| 29 | public enum RazorViewResolutionMode | ||
| 30 | { | ||
| 31 | /// <summary> | ||
| 32 | /// Resolves template files from a path. | ||
| 33 | /// </summary> | ||
| 34 | ResolvePath, | ||
| 35 | /// <summary> | ||
| 36 | /// Resolves template files from a path and watches for changes. | ||
| 37 | /// </summary> | ||
| 38 | /// <remarks> | ||
| 39 | /// This should only be used in debug mode. | ||
| 40 | /// </remarks> | ||
| 41 | WatchingResolvePath, | ||
| 42 | /// <summary> | ||
| 43 | /// Resolves template files from an embedded resource. | ||
| 44 | /// </summary> | ||
| 45 | EmbeddedResource | ||
| 46 | } | ||
| 47 | } | ||