.NET のプロジェクトで .NET Framework と .NET Core を共存させたい

技術

はじめに

タイトルの通りですが、いきさつを少し。

元々 .NET Framework で動いているプロジェクトを .NET Core に移行する必要がありまして。

別プロジェクトで作るのが安パイなのですが、各フレームワークに依存しないコードは両プロジェクトに存在してしまい、何か無駄な気がしてしまいます。

また段階的に移行する場合で、大人数で開発が進んでいたりすれば、.NET Framework 側の変更も随時追従する必要があり、それはそれは面倒なことになってしまいます。

いっそ同プロジェクト内で .NET Framework と .NET Core が共存できる形にできないだろうか?と思ったのがいきさつです。

多少問題点は残るものの、それなりに方向性が定まってきたので、ひとまずまとめておきます。

※ 今回の対象は ASP.NET 系なので、Web プロジェクトです。


構成

SampleSolution
│
├─ SampleProject
│    │
│    ├─ Views ← .NET Framework のビューフォルダ
│    │
│    ├─ Views_Core ← .NET Core のビューフォルダ
│    │
│    ├─ SampleProject.csproj ← .NET Framework のプロジェクト
│    │
│    ├─ SampleProjectCore.csproj ← .NET Core のプロジェクト
│    │
│    └─ ...
│
├─ SampleSolution.sln ← .NET Framework のソリューション
│
└─ SampleSolutionCore.sln ← .NET Core のソリューション

ざっくりした構成はこんな感じ。

.NET Framework と .NET Core のプロジェクトを同階層に入れたことが今回やりたかったことです。


ソリューション

構成図にもある通り、今回 .NET Framework と .NET Core でソリューションを分けました。

結構ここがミソになっており、同ソリューション内に両プロジェクトが共存する方法ではうまくビルドできず、下記のようなエラーが出てしまいます。

Your project does not reference ".NETFramework,Version=v4.8" framework. Add a reference to ".NETFramework,Version=v4.8" in the "TargetFrameworks" property of your project file and then re-run NuGet restore.

もちろんプロジェクトファイルに TargetFrameworkVersion の項目があったりと間違っているわけではなくても、こんなエラーが出てしまいます。

なぜ、という詳しいところまでは究明できていませんが、ひとまずソリューションを分けることでうまく動くようになりました。

(1つのソリューションで両フレームワークを管理する方がレアケースな気もするので、別に問題ないかなと思ったり)


プロジェクト、コード

1つのコードで .NET Framework と .NET Core の動作を共存させるため、プロジェクトとコードに工夫が必要になります。

各プロジェクトに DefineConstants 要素を追加します。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <DefineConstants>NETFRAMEWORK</DefineConstants>
    ...
  </PropertyGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <DefineConstants>NETCORE</DefineConstants>
    ...
  </PropertyGroup>

</Project>

コード内で .NET Framework と .NET Core 固有の処理を追加する時は、#if ディレクティブ (条件付きコンパイル) を使います。

#if NETFRAMEWORK
using System.Web;
using System.Web.Mvc;
#elif NETCORE
using Microsoft.AspNetCore.Mvc;
#endif

#if NETFRAMEWORK
    FrameworkSpecificLogic();
#elif NETCORE
    CoreSpecificLogic();
#endif

このように記述することで、各プロジェクトで該当する箇所のみがビルドされ、両フレームワークのコードを共存させることができます。

この要領で、.NET Framework 側固有の Global.asaxRouteConfig.cs などは .NET Core 側からは除外していきます。

(.NET Framework 側はプロジェクトファイルでファイル、フォルダを追加しないと対象にならないので、.NET Core 固有のファイルは考慮しなくて大丈夫です)

また、今回は .NET Framework のプロジェクトに .NET Core のプロジェクトをそのまま移植する形だったので、さすがにビュー系は形式が違いすぎて混在は難しい状態でした。

というより、cshtml ファイル内で #if の分岐がうまくいきませんでした。

(うまくいく方法はきっとあるんでしょうけれど…僕の知識不足です。つらい)

今回は .NET Core のビューフォルダは Views_Core と名前を変え、カスタムビュー場所から変えることにしました。

// RazorViewEngineOptionsを設定
builder.Services.Configure<RazorViewEngineOptions>(options =>
{
    // 既存のビュー場所をクリア
    options.ViewLocationFormats.Clear();

    // カスタムビュー場所を追加
    options.ViewLocationFormats.Add("/Views_Core/{1}/{0}.cshtml");
    options.ViewLocationFormats.Add("/Views_Core/Shared/{0}.cshtml");
});

Program.cs 内はこんな感じになります。

ちなみに {0} にはビュー名、{1} にはコントローラ名が入るようです。


動かし方

両プロジェクトを同時にデバッグすることはできませんでした。

またビルド、デバッグにも工夫が必要になります。

.NET Core のプロジェクトをビルドする分には特に考慮は要らないのですが、.NET Framework のプロジェクトをビルドするには、先に obj フォルダを空にしておく必要があります。

obj 配下に .NET Core のビルドで生成されたものがあると、.NET Framework がビルドできなくなってしまいます。

少々力業ですが、解消はできます。

ビルドの生成物が出力されるフォルダを分ける方法も考えたのですが、両プロジェクトを共存させている以上、うまくいきませんでした。

(Nuget とビルド生成物の出力先が違う旨のエラーが出たりと、こりゃまた苦戦しそうだったので今回は断念…)


終わりに

サンプルはこちら。一応動きます。

まだ簡単なプロジェクト構成でしか考えられていないですが、基本思想はこれでいけるかと思います。

大規模なプロジェクトへの移行が成功すれば、脳汁ダバダバでしょうね。

もうしばらく .NET Framework と .NET Core の橋渡しの戦いは続きそうなので、根気強く頑張りたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です