.NET Framework → .NET Coreの移行可能難易度を評価してくれるAWSのPorting Assistant for .NET
AWSのブログでPorting Assistant for .NET といAWS製のツールが紹介されていました。
このツールは既存の.NET Frameworkのソリューションを読み取り、その中で利用されているNugetライブラリーの.NET Core対応状況や、ソースコードそのものがどれだけ移植しやすい状況かを確認するツールのようです。
ポートソリューションの機能を使うと、参照しているライブラリで移行可能なライブラリのバージョンを調整したり、プロジェクトファイルの構成を変更してくれるようです。
Visual Studio Version 16.4.0のコンテナ用ツール
.NET Core 3.1 に合わせて Visual Studio 2019 の最新版である Version 16.4.0 が同時に公開されましたが、このバージョンに含まれるコンテナ用のツールが結構よさげです。Kitematicでもいいんですが、表示されている情報が古かったりして微妙なことが多いので結構いいかも。
ツールメニュー > その他のウインドウ > コンテナー から起動できるこの子ですが、
コンテナを起動したり、起動しているコンテナの環境変数やポート情報を確認したり、
ログを表示したり、
コンテナの中身のファイルして開いたり(!!)が簡単にできます。
残念ながらホストにファイルをコピーするなどは、ここからは行えないみたいですね。
必要になったらコマンドアイコンをクリックすれば、コマンドプロンプトが開きます。
統合ターミナル(Whack Whack Terminal)がインストールされていればそちらを起動してくれるみたいです。
Riderのコンテナツールもいい感じですよね
Viewのマイグレーションを考える
異なるスキーマを参照するViewを作って、Entity Frameworkから参照したいという要件があって、Viewの更新管理をどうにかしたいなーと思っていたんだけれど、だいたい固まったのでメモしておく
方針としてはこんな感じ
- Viewの形のEntityを作り、DbContextにDbQueryとして公開する
- OnModelCreatingで、Query~ToViewで関連付ける
- 空のマイグレーションファイルを作り、upでcreate viewを、downでdrop viewを行う。スキーマの差し替えもこのタイミングで行う。
サンプル
https://github.com/karuakun/EntityFrameworkSchemaAcrossDbViewSample
EntityとDbContext
1,2はササッとこんな感じで
Data/Entities/QueryTest.cs
public class Test1 { [Key] public string Id { get; set; } public string Name { get; set; } public string QueryTest2Id { get; set; } }
Data/SampleDbContext.cs
public class SampleDbContext: DbContext { public const string DefaultConnectionStringName = "sampledb"; public SampleDbContext(DbContextOptions options) : base(options) { } public virtual DbQuery QueryTest2 { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Query(entity => { entity.ToView("v_seconddb_test2"); }); } }
マイグレーションの作成
DbContextにマイグレーションに関わる変更がない状態で、マイグレーションファイルを追加すると、空のマイグレーションファイルが出来上がるので、この子にViewの作成と削除を書いてあげる。
dotnet migrations add addsampleview
追加されたマイグレーションファイル
public partial class addview : Migration { protected override void Up(MigrationBuilder migrationBuilder) { } protected override void Down(MigrationBuilder migrationBuilder) { } }
まずは単純に
create view にスキーマ名が入っているので気持ち悪い。。。
public partial class addview : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql( "create view v_seconddb_test2 as select Id, Name, Property1 from schema2.test2"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.Sql("drop view v_seconddb_test2"); } }
Configurationもしくは環境変数から、マッピングを取得する
Startup時に作るIConfigurationRootにアクセスできれば良いんだけれど、MigrationだとDIでConfigurationを受け取ることができない。
ConfigurationをStartupのstaticにしてあげるのは嫌なので、、、DesignTimeContextを使う
こんなクラスをプロジェクトのルートに置いておくと、dotnet efで起動した場合にこっちのコンテキストで初期化してくれる。これなら多少無茶してもいいかなぁ、、、と言うことで、IConfigurationRootをstaticで生やしてあげる。
public class DesignTimeSampleDbContextFactory : IDesignTimeDbContextFactory { public static IConfigurationRoot DesignTimeConfiguration { get; private set; } public SampleDbContext CreateDbContext(string[] args) { DesignTimeConfiguration = DesignTimeConfigurationBuilder.BuildConfiguration(args); var builder = new DbContextOptionsBuilder() .UseMySql(DesignTimeConfiguration.GetConnectionString(SampleDbContext.DefaultConnectionStringName) ); return new SampleDbContext(builder.Options); } }
Configurationを参照できれば、あとはJSONと環境変数の世界なので、appsettings.jsonにこんな感じでエントリを追加して
"MigrationSettings": { "MigrationType": "Local", "SchemaMappings": { "Local": { "AppDb": "appdb", "SecondDb": "seconddb" }, "Stating": { "AppDb": "stg_appdb", "SecondDb": "stg_seconddb" } } },
設定を受けるようにこんなクラスを作っておく
public class MigrationSchemaMappings { public string AppDb { get; set; } public string SecondDb { get; set; } }
で、手作業で作ったSQLがマイグレーションの海に埋もれるのは悲しいので、手作業で作った子はマイグレーション配下にこんな感じでmanualフォルダーを作って、そこに物理ファイルとしておいておきたい。
マイグレーションの拡張メソッドとして、こんな感じのものを作っておけば、
public static class MigrationExtensions { public static string GetSqlText(this Migration source, string migrationId, string fileName) { var sqlPath = Path.Combine(Environment.CurrentDirectory, "Migrations", "manual", migrationId, fileName); if (!File.Exists(sqlPath)) throw new FileNotFoundException(sqlPath); var sqlText = File.ReadAllText(sqlPath); if (string.IsNullOrEmpty(sqlText)) throw new InvalidOperationException($"sqlfile is empty: {sqlText}"); var settings = GetMigrationSettings(); var schemaReplacedSqlText = sqlText.Replace("$SecondDb$", settings.SecondDb); return schemaReplacedSqlText; } private static MigrationSchemaMappings GetMigrationSettings() { var configuration = DesignTimeSampleDbContextFactory.DesignTimeConfiguration; var migrationType = configuration.GetValue("MigrationSettings:MigrationType"); return configuration.GetSection($"MigrationSettings:SchemaMappings:{migrationType}") .Get(); } }
マイグレーションスクリプトはこうかける。
public partial class addview : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(this.GetSqlText(this.GetId(), "up.v_seconddb_test2.sql")); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(this.GetSqlText(this.GetId(), "down.v_seconddb_test2.sql")); } }
ソリューションファイルとプロジェクトファイルが同じディレクトリにある状態で、Dockerサポート(Dockerfile)を作成すると、docker build時にビルドが失敗する。
Visual StudioのソリューションエクスプローラーからDockerサポートの追加をして、docker build をしたところ、こんなエラーが出ました。
ログの全体はこんな感じ。
> docker build . -t sample Sending build context to Docker daemon 339.5MB Step 1/16 : FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base ---> df0d46ff6229 Step 2/16 : WORKDIR /app ---> Using cache ---> e6407778a2d1 Step 3/16 : EXPOSE 80 ---> Using cache ---> a17ec6fc7197 Step 4/16 : FROM microsoft/dotnet:2.2-sdk AS build ---> 343e2dc38168 Step 5/16 : WORKDIR /src ---> Using cache ---> 064e8cb7ff8d Step 6/16 : COPY signalsample.csproj ./ ---> 3361ca72b93c Step 7/16 : RUN dotnet restore /signalsample.csproj ---> Running in d5b618d70751 MSBUILD : error MSB1001: Unknown switch. Switch: /signalsample.csproj For switch syntax, type "MSBuild /help" The command '/bin/sh -c dotnet restore /signalsample.csproj' returned a non-zero code: 1
msbuild 時によくわからないスイッチがあると言われる。
For switch syntax, type “MSBuild /help”
The command ‘/bin/sh -c dotnet restore /signalsample.csproj’ returned a non-zero code: 1
dockerfileを見ると、
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.2-sdk AS build WORKDIR /src COPY signalsample.csproj ./ RUN dotnet restore /signalsample.csproj COPY . . WORKDIR /src/ RUN dotnet build signalsample.csproj -c Release -o /app FROM build AS publish RUN dotnet publish signalsample.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "signalsample.dll"]
8行目の restore でプロジェクトファイルの指定/から始まっていてこいつがスイッチとして認識されているっぽい。
RUN dotnet restore /signalsample.csproj
たぶん、dotnet コマンドからプロジェクトを作ったあとに、同じフォルダーにソリューションフォルダーを作った事が原因だと思われる。(dotnet build や dotnet publish はソリューションフォルダーが無いことを考慮しているのに、dotnet restore は考慮されていのか…)
とりあえず、/を取るか、./とするとdocker buildもdocker-compose buildもうまく通った。
RUN dotnet restore signalsample.csproj
Visual Studio 2017 15.9.2インストール後、15.8以前で作った、docker-composeプロジェクトがあると、ソリューションロード時にエラーダイアログが表示される。
Visual Studio 2017を15.9.2にアップデートした後に、ソリューションを開いたらこんなダイアログが表示されました。
プロジェクト システムでエラーが発生しました。
パラメーターが間違っています。 (HRESULT からの例外:0x80070057 (E_INVALIDARG))
診断ログは次の場所に書き込まれました: “C:\Users\xxxxxxxx\AppData\Local\Temp\VsProjectFault_9eeb5562-6d00-4839-a077-abc0b27b60bf.failure.txt”。
診断ログにはこんなログが記載されていました。
=====================
2018/11/20 14:59:42
Recoverable
System.ArgumentException: パラメーターが間違っています。 (HRESULT からの例外:0x80070057 (E_INVALIDARG))
場所 EnvDTE.Projects.Item(Object index)
場所 Microsoft.VisualStudio.Docker.Shared.HierarchyExtensions.IsInStartupProjects(Hierarchy hierarchy, IServiceProvider serviceProvider)
場所 Microsoft.VisualStudio.Docker.Compose.ProjectSystem.ProjectLoadHandler.d__31.MoveNext()
— 直前に例外がスローされた場所からのスタック トレースの終わり —
場所 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
場所 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
場所 Microsoft.VisualStudio.ProjectSystem.UnconfiguredProjectImpl.AutoLoadMethodStateMachine.d.MoveNext()
===================
この投稿を見ると、asp.net coreのプロジェクトでdocker-composeが入っているのに、docker-composeをスタートアッププロジェクトに指定してしないソリューションが問題を受けるみたいです。
E_INVALIDARG error when loading solution after upgrading to 15.9 (worked in 15.8)
https://developercommunity.visualstudio.com/content/problem/385450/e-invalidarg-error-when-loading-solution-after-upg.html
テスト用データベースや認証サービス、キャッシュサービスもdockerで起動しているので、asp.netをdocker起動にした場合、docker-composeでdepends_onを指定しないとネットワーク的に通信できないのでdocker-composeを導入したものの、起動に時間がかかるのでとりあえずdockerを使わないでデバックしているソリューションで問題が出ました。
docker-composeをスタートプロジェクトの一つに加えるか、ソリューションから除外したらダイアログは表示されなくなりました。
別にアラートが出てもデバックはできるし問題はないのですが、気になるなら暫くの間はdocker-composeをソリューションから除外しておいても良いかもしれません。
ReSharperの Type in comment は日本語だとちょっと使いにくい
ReShaper 2018.2 で Type in comment という機能が追加されています。
https://blog.jetbrains.com/dotnet/2013/01/14/respeller-a-spell-checking-plugin-for-resharper/
この機能を有効にすると、あらかじめ登録された辞書に基づいてタイプミスがないか指摘してくれるのですが、日本語の場合分かち書きがうまくできていないこともあり、単語の単位が微妙です。
デフォルトのInspectionレベルはSuggestionなので、ビルド自体には影響はないのですが、気になり場合は無効にする場合は、Action Indicatorから Do not Show を選択すれば消えてくれます。
ReSharperのOptionsから、Spelling issues の Typo in comment を変更してもいいですね。
dotCoverでテストカバレッジが表示されない場合、JetBrainsフォルダーのCoverageDataフォルダーを削除すると解決する
ReSharperを導入してもらたので、UnitTestのカバレッジ表示にdotCoverを使っています。
UnitTestのカバレッジをとったら、UnitTestSessionsウインドウにこんなエラーが表示され、Unit Test Coverageウインドウにカバレッジが表示されなくなってしまいました。
Coverage analysis: Processing coverage snapshots: スナップショットのパス failed: Unknown storage type
今回はここで触れらているように、%temp%\JetBrains\ReSharperPlatformVs14\vAny\CoverageData フォルダーをリネームしたところ、表示できるようになりました。
https://dotnettools-support.jetbrains.com/hc/en-us/community/posts/360000167744-Continuous-Testing-Failed-to-Process-Coverage-Results-The-given-key-was-not-present-in-the-dictionary-
ReSharperのテストランナーを使うとファイルを見失う、、、
Visual Studioのテストランナーだとうまくいくのに、ReSharperのテストランナーを使うとうまくいかないケースが有った。
デバックでおっていってみると、相対パスにあるユニットテスト用の外部ツールを起動している部分で、ファイルを見失っている感じこんなコードがあるところで、
Process.Start(Path.Combine(Assembly.GetExecutingAssembly().Location, @”..\..\..\..\Hoge.exe”))
Visual StudioのテストランナーだとAssembly.GetExecutingAssembly().Locationには実行しているアセンブリのパスが入るんだけれど、ReSharperのテストランナーから実行するとこんなパスが入ってくる。「%LOCALAPPDATA%\Temp\0ywyjnls.q4g\dbsqoo3y.wab\HogeHoge.Test\assembly\dl3\8aa495c4\efd6deeb_4459d301\」
これは、ReShaperのテストランナーがユニットテスト前にDLLをシャドーコピーしてからテストを実施するためなので、Visual StudioのReSharperメニュー > Options > Tools > UnitTesting > Unit Test Runnerの Shadow-copy assemblies begin tested のチェックを外してあげれば Visual Studioのテストランナーと同じ挙動になる。
Visual Studio 2017が起動しない場合、privateregistry.binを消してみよう
どうもVisual Studio 2017を起動してもなかなか立ち上がってこない。タスクマネージャーを見ていると、ほとんどCPUを使わない状態で停止し待っている感じがする。
Visual Studioをログ出力モードで起動したら、「AppId starting registry detouring」ってステップで動作が止まっているようなので、キーワードに入れて検索してみたら%LOCALAPPDATA%\Microsoft\VisualStudio\配下のprivateregistry.binを消したら解決したという情報があったので、とりあえずリネームしたら動いた。
VS2017 version 15.1 (26403.0) will only start as administrator.
privateregistory.binって何者?
まぁ、なんとなく名前から検討はつくんですが、Visual Studio インスタンスの検出および管理用のツールのVisual Studio インスタンスのレジストリの編集をみると、
Visual Studio 2017 ではレジストリ設定はプライベートな場所に保存されているため、同じバージョンの Visual Studio の複数のインスタンスを side-by-side で同じコンピューターで使用できます。
ってなっているので、レジストリ情報が何らかの原因で壊れていたのが原因なのかな。
No EditorOptionDefinitionと表示され、Visual Studioでソースコードが開けなくなった場合の対応
Windows Update をしたら、こんなダイアログが表示されソースコードが表示できなくなってしまいました。
No EditorOptionDefinition export found for the given option name: TextViewHost/ChangeTracking
パラメーター名:optionId
ぐぐると、似たような事例が。
http://stackoverflow.com/questions/23893497/no-editoroptiondefinition-export-found-error
Visual Studioを閉じた後、%LocalAppData%\Microsoft\VisualStudio\12.0\のフォルダーをリネームしたら直りました。
Microsoft.VisualStudio.Default.cacheで検索すると、該当フォルダーを消すと言うのは一般的?な対応みたいですね。
https://www.google.co.jp/search?q=Microsoft.VisualStudio.Default.cache