.NET 5では、ソースジェネレータが導入されています。私たちは彼の助けを借りてこれを行います。この記事では、ソースジェネレーターとそのソリューションを使用するときに発生した主な問題について説明します。UI自体の生成は、この記事の範囲を超えています。Visual Studio2019で使用されます。
したがって、これに必要なものは
次のとおりです。1。js / vue / jsxファイルを生成する機能
2.メインプロジェクトディレクトリ
へのアクセス3.設定ファイルへのアクセス
4.ジェネレーター内のサードパーティライブラリ(Newtonsoft.Jsonなど)の使用
5.ジェネレーター内で他のアセンブリを使用する
6.異なるアセンブリにあるコントローラーとモデルのクラス/タイプへのアクセス
7.デバッグ
T4について一言
.NET4.xにはT4コードジェネレーターがあります。最初は、問題を解決しようとしました。主にシステムライブラリのロードに関連する多くの問題がありましたが、さまざまな成功を収めて解決されました。しかし、エイリアン(.NET 4.xランタイム用)のAspNetCoreライブラリを参照するコントローラーを使用して.NET 5アセンブリを処理することになると、私の頭脳は行き詰まりました。T4は、それを見つけてロードすることを望んでいませんでした。
プロジェクト構造
すべての新しいMicrosoftテクノロジは、すべてがクールに機能するHelloWorldから始まります。しかし、実際のプロジェクトでそれらを使い始めると、多くの問題が発生します。これらの1つはプロジェクト構造です。Hello Worldでは、これは1つのアセンブリです。そして実際のプロジェクトではそれらのいくつかがあります。
私のプロジェクトには、4つの条件付きアセンブリが含まれてい
ます。1。NetGenerator5.Web-起動されたメインのWebアプリケーション(net5.0)には、コントローラー、モデルを含むアセンブリが含まれ、ジェネレーター自体が接続されています。
2. NetGenerator5.Model-モデルを使用したアセンブリ(net5.0)
3。NetGenerator5.Generator-ジェネレータを使用したアセンブリ(netstandard2.0)
4. NetGenerator5.Generator.Dependency-ジェネレーター内で使用される条件付きアセンブリ(netstandard2.0)
発生器
ジェネレータークラスは、InitializeとExecuteの2つのメソッドを使用してISourceGeneratorインターフェイスを実装します。Executeメソッドは、ジェネレーターが接続されているプロジェクトのコンパイル中に直接実行されます。
発電機プロジェクト自体
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
</Project>
接続方法は?メインプロジェクト(NetGenerator5.Web)で、以下を登録する必要があります。
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NetGenerator5.Generator\NetGenerator5.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
js / vue / jsxファイルを生成する機能
最初に、ジェネレーターはC#コードでcsファイルを出力します。これを行うには、Executeメソッド内で、GeneratorExecutionContext.AddSourceコンテキストメソッドが使用されます。私の知る限り、拡張子を変更することは不可能であり、これらのファイルもコンパイルされます。したがって、他の言語でコードを配置することはできません。VisualStudioはコンパイルエラーのスローを開始します。
したがって、js / vue / jsxファイルを保存するには別のアプローチが必要です。通常のSystem.IO.File.WriteAllTextが役に立ちました。ただし、このためには、生成されたファイルを保存する必要がある場所を正確に知る必要があります。メインプロジェクトのディレクトリを知っています。
メインプロジェクトディレクトリへのアクセス
次のように取得できます。
メインのNetGenerator5.Webプロジェクトで、次のように記述します。
<ItemGroup>
<CompilerVisibleProperty Include="MSBuildProjectDirectory" />
</ItemGroup>
これにより、ソースジェネレータのシステム変数が表示されます。
また、ジェネレーター自体では、次のようにExecuteメソッドでジェネレーターにアクセスします。
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.MSBuildProjectDirectory", out var projectDirectory)
さらに、生成されたファイルをWebプロジェクト自体のどこに配置するかを正確に知る必要があります(たとえば、wwwroot / js)。これをメインプロジェクトのgeneratorsettings.json構成ファイルに渡すことになりました。しかし今、私はどういうわけかそれについてジェネレータに伝える必要があります。
設定ファイルへのアクセス
ジェネレーターには、Executeメソッド内のGeneratorExecutionContext.AdditionalFilesコンテキストコレクションを介してファイルにアクセスする機能があります。私の構成ファイルをそこに置くには、ビルドアクション= C#アナライザーの追加ファイルプロパティを設定する必要があります。
<ItemGroup>
<AdditionalFiles Include="generatorsettings.json" />
</ItemGroup>
その後、ファイルの内容は次のように読み取ることができます
var content = context.AdditionalFiles.First(e => e.Path.EndsWith("generatorsettings.json")).GetText(context.CancellationToken);
次に、問題が発生します-それはjsonですが、実際にどのように解析できますか?
ジェネレーター内でサードパーティライブラリを使用する
外部ライブラリを使用します。たとえば、Newtonsoft.Jsonです。これは何かが本当にうまくいかなかったところです。nugetを介して接続しましたが、ジェネレーターはこのライブラリをまったく見たくありませんでした。
Exception was of type 'FileNotFoundException' with message 'Could not load file or assembly 'Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
そして、あなたが割れたとしても。 料理は、この専用のセクションがあります。 nugetパッケージとしてあなたの発電機を設計する方法について少しでも多くの情報があります。どういうわけかそれは私を助けませんでした。 その結果、最初は奇妙な方法で決めました。私は愚かなことに、ライブラリ自体をファイルとしてプロジェクトに直接追加し、[出力ディレクトリにコピー] = [常にコピー] /新しい場合は[コピー]を指定して、すべてが機能しました。しかし、後で私はroslynディスカッションセクションの質問に対する答えを得ました。アドバイスは私を助けました。次のようにジェネレータプロジェクトに登録する必要があります。
<ItemGroup>
<!-- Generator dependencies -->
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NetGenerator5.Generator.Dependency\NetGenerator5.Generator.Dependency.csproj" />
</ItemGroup>
<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
または、組み込みのSystem.Text.Jsonを使用します。
ジェネレーター内で他のアセンブリを使用する
さらに、ジェネレーター内で他のアセンブリを使用すると便利です。たとえば、VueとReactのヘルパークラスは、2つの異なるアセンブリに分散し、必要に応じてジェネレーターに接続すると便利です。
奇妙なことに、ここではすべてが順調に進みました。依存関係を介してNetGenerator5.Generator.Dependencyを接続しました-プロジェクト参照を追加します。いくつか問題が ありましたが。
異なるアセンブリにあるコントローラーとモデルのクラス/タイプへのアクセス
それでは、楽しい部分に取り掛かりましょう。ファイルを生成するには-クラス/タイプのコントローラーとモデルにアクセスする必要がありました。Microsoft はSyntaxReceiverの使用を推奨し
ていますが、現在コンパイルされているプロジェクトのクラス(つまり、私の場合はNetGenerator5.Web)にしかアクセスできず、NetGenerator5.Modelクラスはありません。
同じroslynディスカッションセクションで、解決策が見つかりました 。GeneratorExecutionContextのコンテキスト内には、Compilation.GlobalNamespaceがあります。再帰的に調べて、現在コンパイルされているアセンブリやモデルを使用したアセンブリなど、すべてのタイプの説明を取得できます。
デバッグ
デバッグの場合は、Initializeメソッドのジェネレータークラスに書き込むだけで十分です。
#if DEBUG
if (!Debugger.IsAttached)
{
Debugger.Launch();
}
#endif
メインプロジェクトのビルドを開始すると、デバッガーを開始するための提案を含むウィンドウが開きます。[OK]をクリックすると、Visual Studioの別のインスタンスが起動し、このジェネレーターのデバッグモードがその中にあります。別のアセンブリNetGenerator5.Generator.Dependencyにあるものも含め、他のすべてのクラスとメソッドの内部に入ることができます。
結果
コンパイル後、NetGenerator5.Web / wwwroot / jsファイルはgenerate.jsになり、NetGenerator5.Web \ obj \ GeneratedFiles \ NetGenerator5.Generator \ NetGenerator5.Generator.SourceGeneratorはダミーのgenerate.csファイルになります。
完全なソースコードは、ここで表示でき ます。
ソース
- github.com/amis92/csharp-source-generators
- github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md
- github.com/dotnet/roslyn/blob/master/docs/features/source-generators.cookbook.md
- mihailromanov.wordpress.com/2021/01/31/net-code-generation-part-6-c-source-generators
- dominikjeske.github.io/source-generators
- github.com/dotnet/roslyn/discussions
- habr.com/ru/post/530454
- habr.com/ru/post/533128