Razor Beyond ASP.NETの使用

そのため、 Microsoftは昨日、新しいRazorビューエンジンを含むASP.NET MVC3 RTMをリリースしました 。 おそらく既にご存知のように、RazorにはWeb固有のコンポーネントは含まれていません。つまり、他のアプリケーションで使用できます。 さて、あなたはまだこれを知らない場合-それは調べる時間です!



この投稿では、Razorをニーズのテンプレートエンジンとして使用する方法を示します。 ソースはAndrew Nurse ブログ投稿「ASP.Net外でのRazorのホスティング」でしたが、これは直接的な翻訳ではありません。







たとえば、注文に関する情報を含むテキストレターを作成します。



注文は、OrderModelとOrderItemModelの2つのクラスで記述されます。



public class OrderModel { public string FirstName { get; set; } public string LastName { get; set; } public List<OrderItemModel> Items { get; set; } } public class OrderItemModel { public string ProductName { get; set; } public Decimal Price { get; set; } public int Qty { get; set; } }
      
      







電子メールの作成に使用するテンプレートを以下に示します。 たとえば、 Andrei Taritsynのブログ投稿「Razor Syntax Quick Reference [翻訳]」で 、Razor構文に関する詳細情報を入手できます。



 , @Model.FirstName @Model.LastName! #   -  - ---------- ------ ------ ------ @for (var i = 0; i < Model.Items.Count; i++) { var item = Model.Items[i]; @String.Format( "{0} {1,-10} {2,6} {3,6} {4,6}\r\n", i + 1, @item.ProductName, @item.Price, @item.Qty, item.Price * item.Qty) } : @(((OrderModel)Model).Items.Sum(x => x.Price * x.Qty)) WBR, ACME team
      
      







テンプレートのベースとして使用されるクラスが必要になります。



 public abstract class TemplateBase { public TextWriter Output { get; set; } public dynamic Model { get; set; } public abstract void Execute(); public virtual void Write(object value) { this.Output.Write(value); } public virtual void WriteLiteral(object value) { this.Output.Write(value); } }
      
      







Outputプロパティは、テンプレート実行の結果を受け取るTextWriterを設定します。



Modelプロパティは、パラメーターをテンプレートに渡すために使用されます。



継承されたクラスのExecute()メソッドには、テンプレートコードが含まれます。



Write()およびWriteLiteral()メソッドは、それぞれ式およびテキスト文字列の評価結果を出力するために使用されます。 なぜなら 計算の結果や文字列に対して追加の処理は必要ありません;これらのメソッドのコードは同じです。



注:Execute()、Write()、およびWriteLiteral()という名前がデフォルトで使用されます。必要に応じて、RazorEngineHostクラスのインスタンスのGeneratedClassContextプロパティを使用して他の名前を指定できます。



次に、Razorのホストを作成し、C#がテンプレートで使用されることを示します(RazorはC#とVBをサポートします。必要に応じて、VBRazorCodeLanguageのインスタンスをRazorEngineHostコンストラクターに渡すことでVBを使用できます)。



 var razorHost = new RazorEngineHost(new CSharpRazorCodeLanguage());
      
      







次に、基本クラスの名前、テンプレートコードが配置される名前空間、およびテンプレートクラスの名前を指定します。



 razorHost.DefaultBaseClass = typeof(TemplateBase).FullName; razorHost.DefaultNamespace = "StandaloneRazorDemo"; razorHost.DefaultClassName = "Template";
      
      







テンプレートクラスのコードで使用できる名前空間のセットを追加します。



 razorHost.NamespaceImports.Add("System"); razorHost.NamespaceImports.Add("System.Collections.Generic"); razorHost.NamespaceImports.Add("System.Linq"); razorHost.NamespaceImports.Add("System.Text");
      
      







最後に、テンプレートエンジンを作成します。



 var razorEngine = new RazorTemplateEngine(razorHost);
      
      







次に、テンプレートの解析を試みます。



 var templateText = File.ReadAllText("template.txt"); GeneratorResults generatorResults = null; using (var reader = new StringReader(templateText)) { generatorResults = razorEngine.GenerateCode(reader); }
      
      







GeneratorResultsクラスのSuccessプロパティは、解析がどの程度成功したかを示します。 解析に問題がある場合、エラーのリストを表示します。



 if (!generatorResults.Success) { foreach (var error in generatorResults.ParserErrors) { Console.WriteLine( "Razor error: ({0}, {1}) {2}", error.Location.LineIndex + 1, error.Location.CharacterIndex + 1, error.Message); } throw new ApplicationException(); }
      
      







解析が成功した場合、GeneratedCodeプロパティにはDOMツリーが含まれており、これを使用してコードを生成できます。



 return generatorResults.GeneratedCode;
      
      







次に、テンプレートをコンパイルする必要があります。



 private static string CompileTemplate(CodeCompileUnit generatedCode) { var codeProvider = new CSharpCodeProvider(); #if DEBUG using (var writer = new StreamWriter("out.cs", false, Encoding.UTF8)) { codeProvider.GenerateCodeFromCompileUnit( generatedCode, writer, new CodeGeneratorOptions()); } #endif var outDirectory = "temp"; Directory.CreateDirectory(outDirectory); var outAssemblyName = Path.Combine(outDirectory, String.Format("{0}.dll", Guid.NewGuid().ToString("N"))); var refAssemblyNames = new List<string>(); refAssemblyNames.Add(new Uri(typeof(TemplateBase).Assembly.CodeBase).AbsolutePath); refAssemblyNames.Add("System.Core.dll"); refAssemblyNames.Add("Microsoft.CSharp.dll"); var compilerResults = codeProvider.CompileAssemblyFromDom( new CompilerParameters(refAssemblyNames.ToArray(), outAssemblyName), generatedCode); if (compilerResults.Errors.HasErrors) { var errors = compilerResults .Errors .OfType<CompilerError>() .Where(x => !x.IsWarning); foreach (var error in errors) { Console.WriteLine("Compiler error: ({0}, {1}) {2}", error.Line, error.Column, error.ErrorText); } throw new ApplicationException(); } return outAssemblyName; }
      
      







上記のメソッドは、コンパイルされたテンプレートを含むアセンブリの名前を返します。



#if DEBUG ... #endifで囲まれたコードはデバッグに使用され、すべての操作が実行された後にテンプレートがどのようになったかを確認できます。



アセンブリを読み込んで、テンプレートクラスのインスタンスを作成し、それを「実行」するだけです。



 var assembly = Assembly.LoadFrom(outAssemblyName); var type = assembly.GetType("StandaloneRazorDemo.Template", true); var template = Activator.CreateInstance(type) as TemplateBase; using (var writer = new StringWriter()) { template.Output = writer; template.Model = GetModel(); template.Execute(); File.WriteAllText("out.txt", writer.ToString(), Encoding.UTF8); }
      
      







GetModel()メソッドは次のように定義されます。



 private static OrderModel GetModel() { var model = new OrderModel { FirstName = "", LastName = "" }; model.Items = new List<OrderItemModel>(); model.Items.Add(new OrderItemModel { ProductName = "Apple", Price = 4.95m, Qty = 1 }); model.Items.Add(new OrderItemModel { ProductName = "Kiwi", Price = 9.95m, Qty = 2 }); return model; }
      
      







これで、ファイル「out.txt」には、テンプレートの「実行」の結果が含まれます。



 ,  ! #   -  - ---------- ------ ------ ------ 1 Apple 4,95 1 4,95 2 Kiwi 9,95 2 19,90 : 24,85 WBR, ACME team
      
      







以上です!



サンプルコード:

StandaloneRazorDemo.zip



All Articles