0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Avanade Beef でテンプレートを使用して生成されるコードをカスタマイズする

Last updated at Posted at 2023-01-02

はじめに

Avanade Beef (以下、Beef) は ASP.NET Core をベースとする Web API の自動生成ツールです。

Beefについての説明はこのあたりを参照。

今回は、Beefでテンプレートをどのように定義するかについて記載します。

Beefでのコード生成

Beef では、エンティティの定義に対して各レイヤーのボイラープレートコードを自動生成し、これにより一定の品質を保ちつつコーディングの手間を軽減することが可能です。
さらに、標準で生成されるコード以上のことをしたくなった場合には、独自のコードテンプレートを追加することで生成されるコードをカスタマイズすることが可能となります。

以降、どのように独自のテンプレートを定義して、必要な処理をどのように組み込むかについて、実験した結果を基に説明します。

標準のテンプレート

標準のテンプレートは、Githubから取得したソースコード内の、/tools/Beef.CodeGen.Core/Templates 内にある *.hbs ファイルです。これらは、Handlebars.Net を用いて処理されます。

どのようなテンプレートか、以下に EntityWebApiController_cs.hbs のごく一部を抜粋します。

namespace {{Root.NamespaceApi}}.Controllers
{
    /// <summary>
    /// Provides the {{{EntityNameSeeComments}}} Web API functionality.
    /// </summary>
{{#ifval WebApiAuthorize}}
    [{{{WebApiAuthorize}}}]
{{/ifval}}
{{#ifval WebApiRoutePrefix}}
    [Route("{{WebApiRoutePrefix}}")]
{{/ifval}}
    public partial class {{Name}}Controller : ControllerBase
    {
{{#each WebApiCtorParameters}}
        private readonly {{Type}} {{PrivateName}};
  {{#if @last}}

  {{/if}}
{{/each}}

{{}} で囲まれた部分がHandleBars.Netの制御文です。抜粋した範囲では、WebApiAuthorizeやNameなどの値を条件分岐に使用したり出力したりしています。これらの値は、定義ファイルに設定した値を基に Beef の前処理で加工された内容であるため、いつどのような値が入るかは Beef のソースコードを確認する必要があります。

ただし、テンプレートにすでに定義されているものを参考にして、テンプレートを編集するのであれば、それほど難しくはありません。

例: Controllerの各メソッドが呼び出されたときにログを出力する

まずは、対象のテンプレートをBeefのソースコードから自分のプロジェクトにコピーします。
今回は、Controllerのテンプレートである EntityWebApiController_cs.hbs を、自分の生成されたプロジェクトの_Company_.AppName.CodeGen/Templates ディレクトリにコピーします。

次に、Visual Studioでこのファイルに対するビルド アクションを埋め込みリソースに設定します。(Visual Studioを使用しない場合は .csproj ファイルの EmbeddedResource 要素に追加 - 例: https://blog.yucchiy.com/2020/11/csharp-embedded-resources/)

このテンプレートファイル(以下、テンプレートファイル)に対する変更を行います。結果的にどのようなコードが生成されるかを考えながらテンプレートファイルに追加します。

まず、ログを出力するためのloggerが必要となります。毎回型を名前空間から指定するのは面倒なので、using を追加します。

using Microsoft.Extensions.Logging;

次に、ログを出力するためのloggerをクラス内に定義する必要があります。
Beefでは、標準でメンバーを追加し、コンストラクタから渡すコードを生成するための属性があるため、Company.AppName.CodeGen/Company.AppName.yaml(以下、エンティティ定義ファイル)の各エンティティに以下のようにwebApiCtorParams 属性を定義します。

entities:
- { name: SomeEntity,
    webApiCtorParams: ["ILogger<SomeEntityController>^Logger"],
    ...
}

webApiCtorParams 属性に定義した値は、「型名^メンバ名」というように解釈され、コンストラクタのパラメータとなるとともにメンバー変数としても定義され、コンストラクター内でメンバー変数に値が設定されます。したがって、生成されたクラスの各メソッド内で使用することが可能です。

準備ができたので、各メソッドでログを出力するコードをテンプレートファイルに追加します。

元々のテンプレートでは、HasFromEntityPropertiesParametersが満たされない場合にメソッド本体が一つの式となるため、ラムダ式の形で生成するようになっていますが、今回はログ出力を追加するためテンプレートファイルからラムダ式の部分を削除し、さらに、元の式は常にreturnをつける必要があるので、returnを囲っているifも削除します。
そのうえで、ログを出力するコードを追加します。

-        public IActionResult {{Name}}({{#each PagingLessParameters}}{{#unless @first}}, {{/unless}}{{#ifeq WebApiFrom 'FromEntityProperties'}}{{#each RelatedEntity.Properties}}{{#unless @first}}, {{/unless}}{{#ifne ArgumentName JsonName}}[FromQuery(Name = "{{JsonName}}")] {{/ifne}}{{{WebApiParameterType}}} {{ArgumentName}} = {{#ifval Default}}{{Default}}{{else}}default{{/ifval}}{{/each}}{{else}}{{#ifne WebApiFrom 'FromQuery'}}[{{WebApiFrom}}] {{/ifne}}{{{WebApiParameterType}}} {{ArgumentName}}{{#ifval Default}} = {{Default}}{{/ifval}}{{/ifeq}}{{/each}}){{#unless HasFromEntityPropertiesParameters}} =>{{/unless}}
+        public IActionResult {{Name}}({{#each PagingLessParameters}}{{#unless @first}}, {{/unless}}{{#ifeq WebApiFrom 'FromEntityProperties'}}{{#each RelatedEntity.Properties}}{{#unless @first}}, {{/unless}}{{#ifne ArgumentName JsonName}}[FromQuery(Name = "{{JsonName}}")] {{/ifne}}{{{WebApiParameterType}}} {{ArgumentName}} = {{#ifval Default}}{{Default}}{{else}}default{{/ifval}}{{/each}}{{else}}{{#ifne WebApiFrom 'FromQuery'}}[{{WebApiFrom}}] {{/ifne}}{{{WebApiParameterType}}} {{ArgumentName}}{{#ifval Default}} = {{Default}}{{/ifval}}{{/ifeq}}{{/each}})
-  {{#if HasFromEntityPropertiesParameters}} 
        {
-  {{/if}}
+          _logger.LogTrace($"{nameof({{Name}})}({{#each PagingLessParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}: { {{ArgumentName}} }{{/each}})");
   ...
- {{#if HasFromEntityPropertiesParameters}}return {{/if}}new WebApiDelete{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, () => _manager. ...
+ return new WebApiDelete{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, () => _manager. ...
   ...
-  {{#if HasFromEntityPropertiesParameters}} 
        }
-  {{/if}}

これで、テンプレートは出来上がりです。CodeGenプロジェクトを実行してコードを生成することで、変更したテンプレートを用いてコードが生成されます。

おわりに

今回は、Beefを使用したアプリケーションでテンプレートをカスタマイズする方法について記載しました。
本稿を参考にすることで、Beefを使用した開発が少しでも楽になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?