3
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?

DTOを作るのにT4 テンプレートを使ってみたら尽く詰まってしまった話

Posted at

DTOを作るのにT4 テンプレートを使ってみたら尽く詰まってしまった話

はじめに

モデルの情報をやりとりするために、DTOを用いる場合があります。たとえば、以下の記事のようにWeb APIでのやり取りのこともあれば、同一アプリ内でもダイアログを出す場合のときに利用することもあるかと思います。
https://learn.microsoft.com/ja-jp/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5

ただ、自分でDTOを用意するのも面倒そうです。そこでChatGPTにきいてみたら、T4テンプレートを使うと良いとの回答。基本的にモデルの値を送受するだけなので、T4テンプレートでリフレクションを使ってプロパティ名とタイプを取ったら簡単に作れそう!と思い取り組みました。

結果、.NET Frameworkでは一応それっぽいところまではいくものの、.NET8のように最新のフレームワークではうまくいかないことがわかりました。
こちらの拡張機能を利用すれば動くらしいのですが、素の VisualStudio ではうまく動かせないようです。
アドバイス等ありましたら教えていただけますと幸いです。

.NET Frameworkで試す場合

  • モデルを別プロジェクトに用意します(.NET Framework4.6.2で試しました)
    namespace ClassLibrary1
    {
      public class Book
      {
          public int Id
          {
              get;
              set;
          }
          public string Title
          {
              get;
              set;
          }
          public string AuthorName
          {
              get;
              set;
          }
          // ...
      }
    }
    
  • 上記のモデルのプロジェクトがビルドしてできたDLLを読み込むようなT4 テンプレートを用意します
    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ assembly name="$(SolutionDir)ClassLibrary1\bin\$(Configuration)\ClassLibrary1.dll" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="ClassLibrary1" #>
    <#@ output extension=".cs" #>
    using System;
    
    namespace ConsoleApp2
    {
    	public class <#= nameof(Book) #>DTO
    	{
    <#
    	var properties = typeof(Book).GetProperties();
    	foreach(var property in properties)
    	{
    #>
    		public <#= property.PropertyType.Name #> <#= property.Name #> { get; set; }
    <#
    	}
    #>
    	}
    }
    
  • 上記を保存して生成すると、以下のようなコードが生成されます(一応最低限のところまでできてます。intがInt32になっていたりしますが)。ただ、もう少し頑張ればそれっぽいところまでは行けそうな予感がします
    using System;
    
    namespace ConsoleApp2
    {
    	public class BookDTO
    	{
    		public Int32 Id { get; set; }
    		public String Title { get; set; }
    		public String AuthorName { get; set; }
    	}
    }
    

.NET8で試したときに起きたこと

.NET Frameworkでうまくいくことはわかりましたが、本当に動かしたいのは最新バージョンです。
しかし、上記と同じようなアプローチをしてみたものの、以下のようなエラーが出てしまいました。

重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態
エラー 変換をコンパイルしています: 型 'Object' は、参照されていないアセンブリに定義されています。アセンブリ 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' に参照を追加する必要があります。 ...(省略)...TextTemplate1.tt 13

一応回避策としてstackoverflowにも回答がありましたが、わざわざコンフィグを書き換える手法でいまいち、かつ.NET6以降ではうまく解決できないみたいです。
https://stackoverflow.com/questions/51550265/t4-template-could-not-load-file-or-assembly-system-runtime-version-4-2-0-0

更に回答をたどると以下の回答にたどり着き、はじめにで紹介した拡張機能のページにたどり着きました。
https://stackoverflow.com/questions/60153842/using-net-core-assemblies-in-t4-templates/60778223#60778223

なかなか記事も見つけられず、正式サポートはまだ先?のようです。

おわりに

T4テンプレートのような簡単にコード生成できる仕組みがあると色々と融通が効きそうなので、最新の.NETでも動作するようになると嬉しいですね!
この件での良い解決手段を自分では見つけられず…他に良い実現方法があればぜひアドバイスいただけると幸いです。

3
0
1

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
3
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?