20210430のC#に関する記事は12件です。

Blazor のコードビハインド、プロパティ・インジェクション、C# の null 許容参照型、を組み合わせたときの警告 CS8618 を黙らせる方法

はじめに C# の null 許容参照型 C# 8.0以降、C# のプログラミングでは「null 許容参照型」という機能が使えるようになりました。 これにより、C# でより堅牢なアプリケーションを構築することができます。 この機能を有効にする方法のひとつとして、C# のプロジェクトファイル(.csproj)に <Nullable>enable</Nullabl> MSBuild プロパティ値を追加する方法があります。 .csproj <!-- これは C# プロジェクトファイル (.csproj) --> <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> ... <!-- ? この行を追加 --> <Nullable>enable</Nullable> .... Blazor プログラミングにおけるコードビハインド Blazor プログラミングでは、コンポーネントを実装するために、通常、HTMLマークアップと C# コードとを混ぜて Razorファイル(.razor)に記述し、C# コードは @code { } ブロック内に配置します。 MyComponent.razor <p>Here is an HTML markup.</p> @code { // ここに C# コードを書く } そしてもう一つの方法として、「コードビハインド」という方法があります。 この方法では、通常の C# ソースファイル (.cs) に、C# コードを、パーシャルクラスとして記述します。 MyComponent.razor.cs public class partial MyComponent { // .razor ファイルから分離して、C# コードをここに書く } 「コードビハインド」スタイルは、Blazor コンポーネントを読みやすく、メンテナンスしやすくするために便利な場合があります。 Blazor コンポーネントの "コードビハインド" での依存性注入 DI コンテナからサービスを取得するには、Blazor コンポーネント(.razor)で @inject ディレクティブを使用する必要があります。 MyComponent.razor <!-- ? "IJSRuntime" サービスのインスタンスが、DI コンテナから注入されます。 --> @inject IJSRuntime JS @inject ディレクティブは、プロパティ・インジェクションとして機能します。 つまり、「コードビハインド」の C# ソースファイルにてサービスを注入したい場合は、@inject ディレクティブではなく、[Inject] 属性でアノテーションされたプロパティを使って注入することができます。 MyComponent.razor.cs public class partial MyComponent { // ? "IJSRuntime" サービスのインスタンスが、DI コンテナから注入されます。 [Inject] private IJSRuntime JS { get; set; } ... これら 3 つのプログラミングスタイルを混ぜるとどうなるのか? C# の null 許容参照型、コードビハインドスタイル、コードビハインドでのプロパティインジェクション、これら3つのプログラミング要素を混ぜるとどうなるでしょうか? 答えは、「CS8618」という恐ろしい警告が発生します。 "Warning CS8618 Non-nullable property 'JS' must contain a non-null value when exiting constructor. Consider declaring the property as nullable." しかし、DI コンテナから注入されるプロパティは、アプリケーション開発者が書くコードの場所では決して null にならないはず、と思います。 そのため、この CS8618 の警告は意味をなさないと感じます。 この警告を避けるためにはどうすればよいでしょうか? 思いついた 3つのアイデア(しかしいずれも不完全)+ [2021/05/02 追記] コメントで教えてもらった方法 今のところ、この問題の完璧な解決策についてはわかりません。 しかし、この問題を軽減するための3つのアイデアを思いつきました。 [2021/05/02 追記] コメントにて、なるほどこれはスマート! と自分には思われる方法を教えてもらいましたので、4つめの解決方法として追記しました。 1. プロパティを null 許容にする。 アイデアの1つは、プロパティを null 許容にすることです。 そのためには、型名の最後に「?」記号を付けます。 [Inject] // ? "?" はこのプロパティ値が null になり得ることを示します. public IJSRuntime? JS { get; set; } その代わりに、プロパティを参照する前に必ず null チェックを行う必要があります。 // ? null チェックが必要 if (this.JS != null) { await this.JS.InvokeVoidAsync("foo"); } もし null チェックをさぼると、C# コンパイラが「CS8604」という別の警告を出します。 この解決法は厳密には正しい方法です。 しかし、先に述べたように、注入されたプロパティが開発者のコードで null になることは現実問題としてありません。 そのため、毎度の null チェックが必要になってしまうこの解決策は冗長すぎると感じます。 また、毎回 null チェックする以外の別の方法として、「!」マークを使って、C# コンパイラに変数が null かどうかの条件を無視させることもできます。 // ? "!" 記号で、null 状態確認を無視させる。 await this.JS!.InvokeVoidAsync("foo"); とはいえ、この方法はまったくもってお勧めしません。 なぜなら、この方法では何のために null 許容参照型の機能を有効にしているのか、意味不明になってしまうからです。 2. [AllowNull] 属性の追加 次のアイデアは、プロパティに [AllowNull] 属性を付加することです。 // ? [AllowNull] 属性を追加 [Inject, AllowNull] public IJSRuntime JS { get; set; } この解決策は、概ね良好に機能します。 ただし、プロパティが null 許容ではないにも関わらず、null を設定することができてしまいます。 // ? null を設定してもコンパイラから何も言われない ? this.JS = null; 3. #pragma waning disable を使う 最後のアイデアは、プロパティを "warning disable "というプラグマで囲むことです。 // ? "warning disable" プラグマで当該プロパティを囲う #pragma warning disable CS8618 [Inject] public IJSRuntime JS { get; set; } #pragma warning restore CS8618 この解決策も問題なく動作します。 そしてこの方法はある意味、開発者が最も直接的にやりたいこと = "警告を抑止したい" を反映していると思います。 しかし、この方法ではC#コードのインデントが崩れ、記述量が多くなり、コードの読みやすさが損なわれてしまいます。 このため、自分はこの方法は好きではありません。 4. [2021/05/02 追記] コメント欄にて教えてもらった、より賢い方法 コメント欄にて、以下のようにプロパティを初期化するコードを書くとよいのではないか、と教えてもらいました。 [Inject] public IJSRuntime JS { get; set; } = null!; // ? 「!」記号付きの null で初期化 「!」記号を末尾に付記した null であれば、非 null 許容参照型の変数に、強制的(?)に null を代入することができ、かつ、コンパイラからも文句を言われないのは知りませんでした。 自分的には、この方法はいちばんスマートな解決方法であると思いました。 @taks@github さん、コメントありがとうございます。 まとめ 自分は、仕事と趣味の両方で、どちらかといえば上記のうちの最初の解決策 (null 許容を指定しつつ、使用箇所で適宜 null チェックを行なう) を使うことが多いです。 しかし、これらの解決策はまったくもってスマートではないので、自分は上記 1~3 の解決策に大きな不満を持っています。 ということで、近い将来、C# のコンパイラや構文が、この厄介な問題をスマートに解決してくれることを期待したいところです。 ([2021/05/02 追記] 前述のとおり、コメントにて、null! で初期化する方法を知りまして、今はこの null! 初期化方式がいちばん妥当なように感じています。) 実は、自分は別の方法で実装することも多いです。それは「これらコーディングスタイルを混ぜない」という方法です。 つまり、コードビハインドスタイルでコーディングはするのですが、ただし DI からの注入だけは @inject ディレクティブを .razor ファイルに記述する方法で書き、DI コンテナから注入されるプロパティは .cs ファイルに書かない、というやり方です。(下記参照) MyComponent.razor <!-- ? DI コンテナからのサービス注入は .razor ファイルに書き... --> @inject IJSRuntime JS MyComponent.razor.cs public partial class MyComponent { ... // コードビハインドの C# ファイル中辛はそれを使うだけ。 await this.JS.InvokeVoidAsync("foo"); もしこの問題のより良い解決策があれば、この記事のコメントで教えてくれるとありがたいです。 Happy Coding! :)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# 同じフォルダで複数のプロジェクト

やりたいこと 複数バージョンを開発・サポートする場合に、以下のような状況に遭遇します。 どのバージョンにも実施したいユニットテスト群がある どのバージョンでもソース群が同じか、ほとんど同じプロジェクトがある どのバージョンも同時に編集したい(せざるを得ない) ところが、ソースファイルはほとんど同じでありながら、ターゲット環境・ランタイム(.NET Framework / Standard / Core)や参照パッケージ/バージョンが異なることがよくあります。 このへんをいい感じに管理する1つの方法です。 解決方法 プロジェクト(*.csproj)はSDKスタイルを使う バージョンでプロジェクトを分けて同じフォルダに格納する それぞれ異なる出力フォルダを指定する <Project> <PropertyGroup> <BaseIntermediateOutputPath>obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> </PropertyGroup> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> <PropertyGroup> <OutputPath>bin\$(Configuration)\$(MSBuildProjectName)</OutputPath> </PropertyGroup> <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> やっていること 複数プロジェクトに分ける SDKスタイルのプロジェクトファイルはフォルダ内のソースをビルド対象とみなすので、1つのフォルダに複数のプロジェクトファイルを作ればソースを共有したことになります。 └ ProjectA ├ ProjectA.V1.csproj # V1保守用のプロジェクト ├ ProjectA.V2.csproj # V2開発用のプロジェクト ├ source1.cs # ソースは同じ ├ source2.cs : 以前からVisual Studioのプロジェクトはソースファイルを複数プロジェクトで共有できます。プロジェクトにソースファイルを「リンクとして追加」するんですが、ファイル増減や名前変更時は各プロジェクトの設定を手動で追従させる必要があって手間でした。 SDKスタイルのプロジェクトなら、どれか1つのプロジェクトでファイル追加・削除すれば、他のプロジェクトにも自動的に反映されるので、リファクタリング機能を使ってファイル名変更や移動もどんどんやれます。 出力フォルダを区別する 実は、1つのフォルダに複数プロジェクトを作成するだけではダメです。そのままでは生成ファイルを格納するフォルダが衝突してしまいます。 複数プロジェクトでフォルダが同じだと、最後にビルドしたプロジェクトの生成ファイルで上書きしてしまうので、参照パッケージが別プロジェクトで参照しているバージョンにすり替わってしまったり、Visual Studioを開いているとフォルダ内のファイル変更を検出して他プロジェクトの参照設定が消えてしまったりします(あらためてnuget参照を復元すると復活したり)。 ですので、出力フォルダをプロジェクトごとに分けてあげる必要があります。 BaseIntermediateOutputPath - 中間ファイル出力フォルダ 通常は先頭のProject要素の属性でSDKを指定します。 <Project Sdk="Microsoft.NET.Sdk"> しかし、中間ファイルの出力パスはSDKの設定より前に指定しておく必要がある(SDKの設定はこの属性を使用するため)ので以下のようにします。最初にSDKは読み込まず、出力パスを設定してからSDKを読み込んでいます。 <Project> <PropertyGroup> <BaseIntermediateOutputPath>obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> </PropertyGroup> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> MSDN - .NET プロジェクト SDK OutputPath - ビルド結果の出力フォルダ ビルド結果の出力フォルダも分けます。 フォルダ名は何でもよいですが、プロジェクト名でサブフォルダを分けるようにするとどのプロジェクトも同じ設定を使いまわせて便利です。 <PropertyGroup> <OutputPath>bin\$(Configuration)\$(MSBuildProjectName)</OutputPath> </PropertyGroup> Q&A プロジェクトファイルを別フォルダに置けばフォルダ指定変えなくていいんじゃないの? そうです。その場合は同じファイル名でもいいです。 同時に保守・開発するバージョン数や、それらの差分規模、同時にビルドしたいかどうか、などやりたいこと次第ですね。 1つのフォルダに複数プロジェクトをつっこむ(本記事の例) バージョンの増減でフォルダ構成を変えなくていい。特定バージョンだけの削除やリポジトリ移動してもフォルダ構成が変わらない。 プロジェクトファイルにフォルダ指定が必要だし、プロジェクトファイル名やプロジェクト名も重複してはいけない。 バージョン間の違いが限定的で、#if~#endif で切り分ければよい程度の違いにとどまる場合、つまり機能的には枯れて安定していて、ターゲット変更や些細な拡張をするくらい、という想定の場合におすすめ。 プロジェクトごとにフォルダを作る フォルダ指定を変えなくていいし、プロジェクトファイル名も同じでいい。 プロジェクトファイルにソースファイルの場所(フォルダ)を指定する必要がある。 あるバージョンのみのソースファイルがあるとか、ファイル単位の差分があるような場合におすすめ。1つのフォルダにまとめるとかえって混沌としてしまうので。 1つのプロジェクトにまとめられないの? csprojファイル(正しくはMSBuildの定義ファイル)には条件式評価や分岐が記述できるので、1つのプロジェクトファイルにターゲットの異なる複数のビルド設定を定義したり、パラメータに応じて参照ファイルやバージョンを切り替えることも可能です。 MSDN - MSBuild の条件構造 可能ですが、プロジェクト全体を共有するようなケースにはおすすめしません。 特定バージョン向けの設定を変更したいだけなのに、別バージョンの設定を変更しなければならない場合がある 新規追加したファイルが過去バージョンには含まれないようにしたり、条件分岐を増やすために既存の定義を変更するとか 過去バージョンの保守が完了して破棄したり、特定バージョンだけ別リポジトリへ引っ越したいなんて場合でも、プロジェクトが分かれていれば簡単だし間違えない ちゃんと確認しろって話ですが、それでも間違いは起きるもので。。。 既存のプロジェクトファイルは変更せず、新バージョン追加時は追加分だけの別プロジェクトを作るようにするとよいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# 同じフォルダ・ソースを複数のプロジェクトで共有する

やりたいこと 複数バージョンを開発・サポートする場合に、以下のような状況に遭遇します。 どのバージョンにも実施したいユニットテスト群がある どのバージョンでもソース群が同じか、ほとんど同じプロジェクトがある どのバージョンも同時に編集したい(せざるを得ない) ところが、ソースファイルはほとんど同じでありながら、ターゲット環境・ランタイム(.NET Framework / Standard / Core)や参照パッケージ/バージョンが異なることがよくあります。 このへんをいい感じに管理する1つの方法です。 解決方法 プロジェクト(*.csproj)はSDKスタイルを使う バージョンでプロジェクトを分けて同じフォルダに格納する それぞれ異なる出力フォルダを指定する <Project> <PropertyGroup> <BaseIntermediateOutputPath>obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> </PropertyGroup> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> <PropertyGroup> <OutputPath>bin\$(Configuration)\$(MSBuildProjectName)</OutputPath> </PropertyGroup> <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> やっていること 複数プロジェクトに分ける SDKスタイルのプロジェクトファイルはフォルダ内のソースをビルド対象とみなすので、1つのフォルダに複数のプロジェクトファイルを作ればソースを共有したことになります。 └ ProjectA ├ ProjectA.V1.csproj # V1保守用のプロジェクト ├ ProjectA.V2.csproj # V2開発用のプロジェクト ├ source1.cs # ソースは同じ ├ source2.cs : 以前からVisual Studioのプロジェクトはソースファイルを複数プロジェクトで共有できます。プロジェクトにソースファイルを「リンクとして追加」するんですが、ファイル増減や名前変更時は各プロジェクトの設定を手動で追従させる必要があって手間でした。 SDKスタイルのプロジェクトなら、どれか1つのプロジェクトでファイル追加・削除すれば、他のプロジェクトにも自動的に反映されるので、リファクタリング機能を使ってファイル名変更や移動もどんどんやれます。 出力フォルダを区別する 実は、1つのフォルダに複数プロジェクトを作成するだけではダメです。そのままでは生成ファイルを格納するフォルダが衝突してしまいます。 複数プロジェクトでフォルダが同じだと、最後にビルドしたプロジェクトの生成ファイルで上書きしてしまうので、参照パッケージが別プロジェクトで参照しているバージョンにすり替わってしまったり、Visual Studioを開いているとフォルダ内のファイル変更を検出して他プロジェクトの参照設定が消えてしまったりします(あらためてnuget参照を復元すると復活したり)。 ですので、出力フォルダをプロジェクトごとに分けてあげる必要があります。 BaseIntermediateOutputPath - 中間ファイル出力フォルダ 通常は先頭のProject要素の属性でSDKを指定します。 <Project Sdk="Microsoft.NET.Sdk"> しかし、中間ファイルの出力パスはSDKの設定より前に指定しておく必要がある(SDKの設定はこの属性を使用するため)ので以下のようにします。最初にSDKは読み込まず、出力パスを設定してからSDKを読み込んでいます。 <Project> <PropertyGroup> <BaseIntermediateOutputPath>obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> </PropertyGroup> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> MSDN - .NET プロジェクト SDK OutputPath - ビルド結果の出力フォルダ ビルド結果の出力フォルダも分けます。 フォルダ名は何でもよいですが、プロジェクト名でサブフォルダを分けるようにするとどのプロジェクトも同じ設定を使いまわせて便利です。 <PropertyGroup> <OutputPath>bin\$(Configuration)\$(MSBuildProjectName)</OutputPath> </PropertyGroup> Q&A プロジェクトファイルを別フォルダに置けばフォルダ指定変えなくていいんじゃないの? そうです。その場合は同じファイル名でもいいです。 同時に保守・開発するバージョン数や、それらの差分規模、同時にビルドしたいかどうか、などやりたいこと次第ですね。 1つのフォルダに複数プロジェクトをつっこむ(本記事の例) バージョンの増減でフォルダ構成を変えなくていい。特定バージョンだけの削除やリポジトリ移動してもフォルダ構成が変わらない。 プロジェクトファイルにフォルダ指定が必要だし、プロジェクトファイル名やプロジェクト名も重複してはいけない。 バージョン間の違いが限定的で、#if~#endif で切り分ければよい程度の違いにとどまる場合、つまり機能的には枯れて安定していて、ターゲット変更や些細な拡張をするくらい、という想定の場合におすすめ。 プロジェクトごとにフォルダを作る フォルダ指定を変えなくていいし、プロジェクトファイル名も同じでいい。 プロジェクトファイルにソースファイルの場所(フォルダ)を指定する必要がある。 あるバージョンのみのソースファイルがあるとか、ファイル単位の差分があるような場合におすすめ。1つのフォルダにまとめるとかえって混沌としてしまうので。 1つのプロジェクトにまとめられないの? csprojファイル(正しくはMSBuildの定義ファイル)には条件式評価や分岐が記述できるので、1つのプロジェクトファイルにターゲットの異なる複数のビルド設定を定義したり、パラメータに応じて参照ファイルやバージョンを切り替えることも可能です。 MSDN - MSBuild の条件構造 可能ですが、プロジェクト全体を共有するようなケースにはおすすめしません。 特定バージョン向けの設定を変更したいだけなのに、別バージョンの設定を変更しなければならない場合がある 新規追加したファイルが過去バージョンには含まれないようにしたり、条件分岐を増やすために既存の定義を変更するとか 過去バージョンの保守が完了して破棄したり、特定バージョンだけ別リポジトリへ引っ越したいなんて場合でも、プロジェクトが分かれていれば簡単だし間違えない ちゃんと確認しろって話ですが、それでも間違いは起きるもので。。。 既存のプロジェクトファイルは変更せず、新バージョン追加時は追加分だけの別プロジェクトを作るようにするとよいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MSTest】Assert.ThrowsExceptionを使用した例外のテスト

※ 個人blogに投稿した記事(投稿日:2020/3/29)をQiitaに移行しました 前置き 「ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本」のサンプルコード内で、 以下のようなテストコードを見かけました。 [TestMethod] public void TestInvalidUserNameLengthMin() { var userFactory = new InMemoryUserFactory(); var userRepository = new InMemoryUserRepository(); var userService = new UserService(userRepository); var userApplicationService = new UserApplicationService(userFactory, userRepository, userService); bool exceptionOccured = false; try { var command = new UserRegisterCommand("12"); userApplicationService.Register(command); } catch { exceptionOccured = true; } Assert.IsTrue(exceptionOccured); } UserRegisterCommand内でユーザー名が3文字以上で無い場合ArgumentExceptionが発生することを検証するテストコードです。 上記のコードでは、例外が発生する箇所をtry〜catchで補足し検証する形式とされています。 MSTestで例外をテストする(ExpectedExceptionAttribute) 前述のテストコードを見たときに、MSTestで例外をアサーションする場合、ExpectedExceptionAttributeを使用するパターンも考えられるのではと感じました。 [TestMethod] [ExpectedException(typeof(ArgumentException))] public void TestInvalidUserNameLengthMin_old() { var userFactory = new InMemoryUserFactory(); var userRepository = new InMemoryUserRepository(); var userService = new UserService(userRepository); var userApplicationService = new UserApplicationService(userFactory, userRepository, userService); var command = new UserRegisterCommand("12"); userApplicationService.Register(command); } MSTestで例外をテストする(Assert.ThrowsException) 最新のMSTestのバージョンではAssert.ThrowsExceptionを使うのが良いようです。 [TestMethod] public void TestInvalidUserNameLengthMin() { var userFactory = new InMemoryUserFactory(); var userRepository = new InMemoryUserRepository(); var userService = new UserService(userRepository); var userApplicationService = new UserApplicationService(userFactory, userRepository, userService); var ex = Assert.ThrowsException<ArgumentException>(() => { var command = new UserRegisterCommand("12"); userApplicationService.Register(command); } ); Assert.AreEqual("ユーザ名は3文字以上です。 (Parameter 'value')", ex.Message); } アノテーションよりもAssert.ThrowsExceptionの方が、例外が発生する箇所も明確になるし、発生したexceptionを更に検証できるのも良いですね。 Assert.ThrowsExceptionはVS2017の時代にリリースされた「MSTest v2」で追加された機能のようです。 そもそもMSTest v2の存在自体把握できていませんでした・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Azure Cognitive Services】Speech Serviceのニューラル音声を使って、日本語テキスト読み上げをしてみた

ニューラル音声とは ディープニューラルネットワークを使用して、話し言葉のアクセントとイントネーションに関する従来の音声合成の限界を克服します。韻律予測と音声合成が同時に行われるため、より滑らかで自然な音声出力が得られます。ニューラル音声を使用すると、チャットボットや音声アシスタントとの対話をより自然で魅力的なものにすることができます。 [ 引用 ] https://docs.microsoft.com/ja-jp/azure/cognitive-services/speech-service/text-to-speech#core-features はじめに この記事では、日本語で女性のニューラル音声であるNanamiがテキストを読み上げてくれます。 以下のMicrosoftドキュメントを参考に作成してみました。 テキスト読み上げの概要 Cognitive Servicesのリソースを作成する際に、リージョン選択に注意してください。 かなり重要なことですが、ニューラル音声を使用できるリージョンが限られています。この記事を日本人が見る場合が多いと思うので、おそらく東日本もしくは西日本のリージョンを選択すると思いますが、なんとニューラル音声がサポートされていません!!! ニューラル音声がサポートされているリージョンに関してはMSドキュメントで確認してください。 私は米国東部のリージョンを選択して、リソースを作成しています。 前提・環境 Windows 10 home visual studio 2019 Azure portal上でCognitive Servicesのリソース作成済み キーを取得できる。 場所を取得できる。 NuGetパッケージ Microsoft.CognitiveServices.Speech v1.16.0 ソースコード using Microsoft.CognitiveServices.Speech; using System.Threading.Tasks; namespace AzureCognitiveServicesAPI { class Program { static string subscriptionKey = "YourSubscriptionKey"; static string serviceRegion = "YourServiceRegion"; static async Task Main(string[] args) { var config = SpeechConfig.FromSubscription(subscriptionKey, serviceRegion); var synthesizer = new SpeechSynthesizer(config); var text = "こんにちは"; await synthesizer.SpeakSsmlAsync(CreateTextReadOut(text)); } static string CreateTextReadOut(string text) { return $"<speak version='1.0' xmlns='https://www.w3.org/2001/10/synthesis' xml:lang='ja-JP'><voice name='ja-JP-NanamiNeural'>{text}</voice></speak>"; } } } フィールド変数のsubscriptionKeyとserviceRegionに前提で取得しておいたキーと場所に書き換えてください。 実行すると、”こんにちは”とNanamiが話してくれるかと思います。 以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C# / ASP.NET WebForm】HttpRequestBase を継承しMock クラスを作成し、HttpRequest をテストする

※ 個人blogに投稿した記事(投稿日:2019/10/21)をQiitaに移行しました 前置き 現場の案件で、外部APIやDB接続が絡んだ少々複雑な判定でCookie を作成するという処理を実装することになり、 対象の処理でユニットテストを作成しておきたくなりました。 Cookie の作成や、作成した値を検証するには当然ですがHttpRequestやHttpResponseのインスタンスが必要ですが、 これらのクラスのインスタンスをユニットテストで生成するのは困難(ほぼ不可能のはず)です。 そこで、ASP.NET ではHttpRequestWrapperというクラスが実装されています。 HttpRequestWrapperはHttpRequestBaseという抽象クラスから派生されています。 HttpRequestBaseはHttpRequestと同じプロパティ、関数を保有したクラスです。 対象となる処理の実装ではHttpRequestBaseを使用し、 HttpRequestWrapperでHttpRequestをラップしたインスタンスを使用することにします。 これらはHttpResponseでも同様です。 文章だとややこしいので、サンプルコードを実装してみました。 Controller(HttpRequestBase, HttpResponseBaseを使用する処理) public class IndexController { public IndexController() { } public void createCookie(HttpRequestBase request, HttpResponseBase response) { if (request.QueryString == null) { throw new ArgumentNullException(); } string value = request.QueryString["hoge"]; var cookie = new HttpCookie("hoge"); cookie.Value = "hoge" + value; response.Cookies.Add(cookie); } } aspx.cs public partial class Index : System.Web.UI.Page { private readonly IndexController _controller = new IndexController(); protected void Page_Load(object sender, EventArgs e) { _controller.createCookie(new HttpRequestWrapper(Request), new HttpResponseWrapper(Response)); } } そして、ユニットテストコードでは以下のようにHttpRequestBaseから派生したMockクラスを作成し、 テストで必要となるプロパティや関数をOverrideして実装します。 Mockクラス public class HttpRequestMock : HttpRequestBase { private readonly NameValueCollection _queryString; public HttpRequestMock(NameValueCollection queryString) { _queryString = queryString; } public override NameValueCollection QueryString => _queryString; } public class HttpResponseMock : HttpResponseBase { private readonly HttpCookieCollection _cookies = new HttpCookieCollection(); public override HttpCookieCollection Cookies => _cookies; } テストコード [TestMethod] public void createCookieNormalCase() { // Arrange var requestMock = new HttpRequestMock(new NameValueCollection() { { "hoge", "12345" } }); var responseMock = new HttpResponseMock(); var sut = new IndexController(); // Act sut.createCookie(requestMock, responseMock); // Assert Assert.AreEqual("hoge12345", responseMock.Cookies["hoge"].Value); } サンプルコード 今回の記事で作成したサンプルコードをGitHubにアップしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[MS Docs クイックスタート] Azure Cosmos DBをC#(.NET)で操作してみた

はじめに 以下のMicrosoftドキュメントを参考に作成してみました。ドキュメント通りに動かなかった箇所があり、いくつか修正をして動くようにしたので記事にしてみました。 クイックスタート: Azure Cosmos DB SQL API アカウント リソースを管理するための .NET V4 SDK (プレビュー) を使用したコンソール アプリを構築する 前提・環境 Windows 10 home visual studio 2019 Azure portal上でCosmos DBのリソース作成済み URIを取得できる。 プライマリキーを取得できる。 NuGetパッケージ Microsoft.Azure.Cosmos v3.17.1 Newtonsoft.Json v13.0.1 作成 Family.csの作成 //using System.Text.Json; //using System.Text.Json.Serialization; using Newtonsoft.Json; namespace todo { public class Family { //[JsonPropertyName("id")] [JsonProperty(PropertyName = "id")] public string Id { get; set; } public string LastName { get; set; } public Parent[] Parents { get; set; } public Child[] Children { get; set; } public Address Address { get; set; } public bool IsRegistered { get; set; } public override string ToString() { //return JsonSerializer.Serialize(this); return JsonConvert.SerializeObject(this); } } public class Parent { public string FamilyName { get; set; } public string FirstName { get; set; } } public class Child { public string FamilyName { get; set; } public string FirstName { get; set; } public string Gender { get; set; } public int Grade { get; set; } public Pet[] Pets { get; set; } } public class Pet { public string GivenName { get; set; } } public class Address { public string State { get; set; } public string County { get; set; } public string City { get; set; } } } ドキュメントの方では、 using System.Text.Json; using System.Text.Json.Serialization; を使って、JSONの処理をしますが、私がこの通りにやった時にうまく動かなかったため、「Newtonsoft.Json」を使いました。 おそらくFamilyクラスのIdをキーに使う際に小文字のidに適用させたいところ、うまく小文字のidにならなかったのが原因なのかなと思います。 フィールド変数とMain関数の作成 class Program { private const string EndpointUrl = "https://<your-account>.documents.azure.com:443/"; private const string AuthorizationKey = "<your-account-key>"; private const string DatabaseId = "FamilyDatabase"; private const string ContainerId = "FamilyContainer"; static async Task Main(string[] args) { CosmosClient cosmosClient = new CosmosClient(EndpointUrl, AuthorizationKey); await CreateDatabaseAsync(cosmosClient); await CreateContainerAsync(cosmosClient); await AddItemsToContainerAsync(cosmosClient); await QueryItemsAsync(cosmosClient); await ReplaceFamilyItemAsync(cosmosClient); await DeleteFamilyItemAsync(cosmosClient); await DeleteDatabaseAndCleanupAsync(cosmosClient); } } ドキュメント通りです。 フィールド変数のEndpointUrlとAuthorizationKeyは前提で取得しておいた、URIとプライマリキーに書き換えればOKです。 ここから、Main関数で実行されている関数たちを定義していきます。 CreateDatabaseAsync関数の作成 /// <summary> /// Create the database if it does not exist /// </summary> private static async Task CreateDatabaseAsync(CosmosClient cosmosClient) { // Create a new database Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId); Console.WriteLine("Created Database: {0}\n", database.Id); } databaseの型はCosmosDatabaseではなく、Databaseに書き換えています。 CreateContainerAsync関数の作成 /// <summary> /// Create the container if it does not exist. /// Specify "/LastName" as the partition key since we're storing family information, to ensure good distribution of requests and storage. /// </summary> /// <returns></returns> private static async Task CreateContainerAsync(CosmosClient cosmosClient) { // Create a new container Container container = await cosmosClient.GetDatabase(DatabaseId).CreateContainerIfNotExistsAsync(ContainerId, "/LastName"); Console.WriteLine("Created Container: {0}\n", container.Id); } containerの型はCosmosContainerではなく、Containerに書き換えています。 AddItemsToContainerAsync関数の作成 /// <summary> /// Add Family items to the container /// </summary> private static async Task AddItemsToContainerAsync(CosmosClient cosmosClient) { // Create a family object for the Andersen family Family andersenFamily = new Family { Id = "Andersen.1", LastName = "Andersen", Parents = new Parent[] { new Parent { FirstName = "Thomas" }, new Parent { FirstName = "Mary Kay" } }, Children = new Child[] { new Child { FirstName = "Henriette Thaulow", Gender = "female", Grade = 5, Pets = new Pet[] { new Pet { GivenName = "Fluffy" } } } }, Address = new Address { State = "WA", County = "King", City = "Seattle" }, IsRegistered = false }; Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); try { // Read the item to see if it exists. ItemResponse<Family> andersenFamilyResponse = await container.ReadItemAsync<Family>(andersenFamily.Id, new PartitionKey(andersenFamily.LastName)); Console.WriteLine("Item in database with id: {0} already exists\n", andersenFamilyResponse.Resource.Id); } catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { // Create an item in the container representing the Andersen family. Note we provide the value of the partition key for this item, which is "Andersen" ItemResponse<Family> andersenFamilyResponse = await container.CreateItemAsync<Family>(andersenFamily, new PartitionKey(andersenFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. Console.WriteLine("Created item in database with id: {0}\n", andersenFamilyResponse.Resource.Id); } // Create a family object for the Wakefield family Family wakefieldFamily = new Family { Id = "Wakefield.7", LastName = "Wakefield", Parents = new Parent[] { new Parent { FamilyName = "Wakefield", FirstName = "Robin" }, new Parent { FamilyName = "Miller", FirstName = "Ben" } }, Children = new Child[] { new Child { FamilyName = "Merriam", FirstName = "Jesse", Gender = "female", Grade = 8, Pets = new Pet[] { new Pet { GivenName = "Goofy" }, new Pet { GivenName = "Shadow" } } }, new Child { FamilyName = "Miller", FirstName = "Lisa", Gender = "female", Grade = 1 } }, Address = new Address { State = "NY", County = "Manhattan", City = "NY" }, IsRegistered = true }; // Create an item in the container representing the Wakefield family. Note we provide the value of the partition key for this item, which is "Wakefield" ItemResponse<Family> wakefieldFamilyResponse = await container.UpsertItemAsync<Family>(wakefieldFamily, new PartitionKey(wakefieldFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. We can also access the RequestCharge property to see the amount of RUs consumed on this request. Console.WriteLine("Created item in database with id: {0}\n", wakefieldFamilyResponse.Resource.Id); } id取得の際にドキュメントでは andersenFamilyResponse.Value.IdやwakefieldFamilyResponse.Value.Idといった形で取得していますが、うまく取得できませんでした。 私の場合はandersenFamilyResponse.Resource.IdやwakefieldFamilyResponse.Resource.Idに書き換えることで取得できるようになりました。 QueryItemsAsync関数の作成 /// <summary> /// Run a query (using Azure Cosmos DB SQL syntax) against the container /// </summary> private static async Task QueryItemsAsync(CosmosClient cosmosClient) { //var sqlQueryText = "SELECT * FROM c WHERE c.LastName = 'Andersen'"; var sqlQueryText = "SELECT * FROM c"; Console.WriteLine("Running query: {0}\n", sqlQueryText); Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); //List<Family> families = new List<Family>(); using (FeedIterator<Family> feedIterator = container.GetItemQueryIterator<Family>(queryDefinition)) { while (feedIterator.HasMoreResults) { FeedResponse<Family> response = await feedIterator.ReadNextAsync(); foreach (Family family in response) { //families.Add(family); Console.WriteLine("\tRead {0}\n", family); } } } } ここは、割とドキュメント通りには動きませんでした。上記のように修正することでうまく動くようになります。 やりたいことは同じだと思います。ちなみに、familiesのリストがありますが、使わなかったのでコメントアウトしています。 ReplaceFamilyItemAsync関数の作成 /// <summary> /// Replace an item in the container /// </summary> private static async Task ReplaceFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); ItemResponse<Family> wakefieldFamilyResponse = await container.ReadItemAsync<Family>("Wakefield.7", new PartitionKey("Wakefield")); Family itemBody = wakefieldFamilyResponse; // update registration status from false to true itemBody.IsRegistered = true; // update grade of child itemBody.Children[0].Grade = 6; // replace the item with the updated content wakefieldFamilyResponse = await container.ReplaceItemAsync<Family>(itemBody, itemBody.Id, new PartitionKey(itemBody.LastName)); Console.WriteLine("Updated Family [{0},{1}].\n \tBody is now: {2}\n", itemBody.LastName, itemBody.Id, wakefieldFamilyResponse.Resource); } wakefieldFamilyResponse.Valueではなく、wakefieldFamilyResponse.Resourceぐらいでほとんどドキュメント通りです。 DeleteFamilyItemAsync関数の作成 /// <summary> /// Delete an item in the container /// </summary> private static async Task DeleteFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); string partitionKeyValue = "Wakefield"; string familyId = "Wakefield.7"; // Delete an item. Note we must provide the partition key value and id of the item to delete ItemResponse<Family> wakefieldFamilyResponse = await container.DeleteItemAsync<Family>(familyId, new PartitionKey(partitionKeyValue)); Console.WriteLine("Deleted Family [{0},{1}]\n", partitionKeyValue, familyId); } containerの型はCosmosContainerではなく、Containerに書き換えています。 DeleteDatabaseAndCleanupAsync関数の作成 /// <summary> /// Delete the database and dispose of the Cosmos Client instance /// </summary> private static async Task DeleteDatabaseAndCleanupAsync(CosmosClient cosmosClient) { Database database = cosmosClient.GetDatabase(DatabaseId); DatabaseResponse databaseResourceResponse = await database.DeleteAsync(); Console.WriteLine("Deleted Database: {0}\n", DatabaseId); } databaseの型はCosmosDatabaseではなく、Databaseに書き換えています。 全体のソースコード using System; using System.Net; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using todo; namespace AzureCosmosDBAPI { class Program { private const string EndpointUrl = "https://<your-account>.documents.azure.com:443/"; private const string AuthorizationKey = "<your-account-key>"; private const string DatabaseId = "FamilyDatabase"; private const string ContainerId = "FamilyContainer"; static async Task Main(string[] args) { CosmosClient cosmosClient = new CosmosClient(EndpointUrl, AuthorizationKey); await CreateDatabaseAsync(cosmosClient); await CreateContainerAsync(cosmosClient); await AddItemsToContainerAsync(cosmosClient); await QueryItemsAsync(cosmosClient); await ReplaceFamilyItemAsync(cosmosClient); await DeleteFamilyItemAsync(cosmosClient); await DeleteDatabaseAndCleanupAsync(cosmosClient); } /// <summary> /// Create the database if it does not exist /// </summary> private static async Task CreateDatabaseAsync(CosmosClient cosmosClient) { // Create a new database Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId); Console.WriteLine("Created Database: {0}\n", database.Id); } /// <summary> /// Create the container if it does not exist. /// Specify "/LastName" as the partition key since we're storing family information, to ensure good distribution of requests and storage. /// </summary> /// <returns></returns> private static async Task CreateContainerAsync(CosmosClient cosmosClient) { // Create a new container Container container = await cosmosClient.GetDatabase(DatabaseId).CreateContainerIfNotExistsAsync(ContainerId, "/LastName"); Console.WriteLine("Created Container: {0}\n", container.Id); } /// <summary> /// Add Family items to the container /// </summary> private static async Task AddItemsToContainerAsync(CosmosClient cosmosClient) { // Create a family object for the Andersen family Family andersenFamily = new Family { Id = "Andersen.1", LastName = "Andersen", Parents = new Parent[] { new Parent { FirstName = "Thomas" }, new Parent { FirstName = "Mary Kay" } }, Children = new Child[] { new Child { FirstName = "Henriette Thaulow", Gender = "female", Grade = 5, Pets = new Pet[] { new Pet { GivenName = "Fluffy" } } } }, Address = new Address { State = "WA", County = "King", City = "Seattle" }, IsRegistered = false }; Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); try { // Read the item to see if it exists. ItemResponse<Family> andersenFamilyResponse = await container.ReadItemAsync<Family>(andersenFamily.Id, new PartitionKey(andersenFamily.LastName)); Console.WriteLine("Item in database with id: {0} already exists\n", andersenFamilyResponse.Resource.Id); } catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { // Create an item in the container representing the Andersen family. Note we provide the value of the partition key for this item, which is "Andersen" ItemResponse<Family> andersenFamilyResponse = await container.CreateItemAsync<Family>(andersenFamily, new PartitionKey(andersenFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. Console.WriteLine("Created item in database with id: {0}\n", andersenFamilyResponse.Resource.Id); } // Create a family object for the Wakefield family Family wakefieldFamily = new Family { Id = "Wakefield.7", LastName = "Wakefield", Parents = new Parent[] { new Parent { FamilyName = "Wakefield", FirstName = "Robin" }, new Parent { FamilyName = "Miller", FirstName = "Ben" } }, Children = new Child[] { new Child { FamilyName = "Merriam", FirstName = "Jesse", Gender = "female", Grade = 8, Pets = new Pet[] { new Pet { GivenName = "Goofy" }, new Pet { GivenName = "Shadow" } } }, new Child { FamilyName = "Miller", FirstName = "Lisa", Gender = "female", Grade = 1 } }, Address = new Address { State = "NY", County = "Manhattan", City = "NY" }, IsRegistered = true }; // Create an item in the container representing the Wakefield family. Note we provide the value of the partition key for this item, which is "Wakefield" ItemResponse<Family> wakefieldFamilyResponse = await container.UpsertItemAsync<Family>(wakefieldFamily, new PartitionKey(wakefieldFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. We can also access the RequestCharge property to see the amount of RUs consumed on this request. Console.WriteLine("Created item in database with id: {0}\n", wakefieldFamilyResponse.Resource.Id); } /// <summary> /// Run a query (using Azure Cosmos DB SQL syntax) against the container /// </summary> private static async Task QueryItemsAsync(CosmosClient cosmosClient) { //var sqlQueryText = "SELECT * FROM c WHERE c.LastName = 'Andersen'"; var sqlQueryText = "SELECT * FROM c"; Console.WriteLine("Running query: {0}\n", sqlQueryText); Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); //List<Family> families = new List<Family>(); using (FeedIterator<Family> feedIterator = container.GetItemQueryIterator<Family>(queryDefinition)) { while (feedIterator.HasMoreResults) { FeedResponse<Family> response = await feedIterator.ReadNextAsync(); foreach (Family family in response) { //families.Add(family); Console.WriteLine("\tRead {0}\n", family); } } } } /// <summary> /// Replace an item in the container /// </summary> private static async Task ReplaceFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); ItemResponse<Family> wakefieldFamilyResponse = await container.ReadItemAsync<Family>("Wakefield.7", new PartitionKey("Wakefield")); Family itemBody = wakefieldFamilyResponse; // update registration status from false to true itemBody.IsRegistered = true; // update grade of child itemBody.Children[0].Grade = 6; // replace the item with the updated content wakefieldFamilyResponse = await container.ReplaceItemAsync<Family>(itemBody, itemBody.Id, new PartitionKey(itemBody.LastName)); Console.WriteLine("Updated Family [{0},{1}].\n \tBody is now: {2}\n", itemBody.LastName, itemBody.Id, wakefieldFamilyResponse.Resource); } /// <summary> /// Delete an item in the container /// </summary> private static async Task DeleteFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); string partitionKeyValue = "Wakefield"; string familyId = "Wakefield.7"; // Delete an item. Note we must provide the partition key value and id of the item to delete ItemResponse<Family> wakefieldFamilyResponse = await container.DeleteItemAsync<Family>(familyId, new PartitionKey(partitionKeyValue)); Console.WriteLine("Deleted Family [{0},{1}]\n", partitionKeyValue, familyId); } /// <summary> /// Delete the database and dispose of the Cosmos Client instance /// </summary> private static async Task DeleteDatabaseAndCleanupAsync(CosmosClient cosmosClient) { Database database = cosmosClient.GetDatabase(DatabaseId); DatabaseResponse databaseResourceResponse = await database.DeleteAsync(); Console.WriteLine("Deleted Database: {0}\n", DatabaseId); } } } おわりに 全体的に動くように、細かい部分で書き換えています。 説明できずに書き換えている箇所もあるかもしれませんが、ご理解お願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MS Docs クイックスタート】Azure Cosmos DBをC#(.NET)で操作してみた

はじめに 以下のMicrosoftドキュメントを参考に作成してみました。ドキュメント通りに動かなかった箇所があり、いくつか修正をして動くようにしたので記事にしてみました。 クイックスタート: Azure Cosmos DB SQL API アカウント リソースを管理するための .NET V4 SDK (プレビュー) を使用したコンソール アプリを構築する 前提・環境 Windows 10 home visual studio 2019 Azure portal上でCosmos DBのリソース作成済み URIを取得できる。 プライマリキーを取得できる。 NuGetパッケージ Microsoft.Azure.Cosmos v3.17.1 Newtonsoft.Json v13.0.1 作成 Family.csの作成 //using System.Text.Json; //using System.Text.Json.Serialization; using Newtonsoft.Json; namespace todo { public class Family { //[JsonPropertyName("id")] [JsonProperty(PropertyName = "id")] public string Id { get; set; } public string LastName { get; set; } public Parent[] Parents { get; set; } public Child[] Children { get; set; } public Address Address { get; set; } public bool IsRegistered { get; set; } public override string ToString() { //return JsonSerializer.Serialize(this); return JsonConvert.SerializeObject(this); } } public class Parent { public string FamilyName { get; set; } public string FirstName { get; set; } } public class Child { public string FamilyName { get; set; } public string FirstName { get; set; } public string Gender { get; set; } public int Grade { get; set; } public Pet[] Pets { get; set; } } public class Pet { public string GivenName { get; set; } } public class Address { public string State { get; set; } public string County { get; set; } public string City { get; set; } } } ドキュメントの方では、 using System.Text.Json; using System.Text.Json.Serialization; を使って、JSONの処理をしますが、私がこの通りにやった時にうまく動かなかったため、「Newtonsoft.Json」を使いました。 おそらくFamilyクラスのIdをキーに使う際に小文字のidに適用させたいところ、うまく小文字のidにならなかったのが原因なのかなと思います。 フィールド変数とMain関数の作成 class Program { private const string EndpointUrl = "https://<your-account>.documents.azure.com:443/"; private const string AuthorizationKey = "<your-account-key>"; private const string DatabaseId = "FamilyDatabase"; private const string ContainerId = "FamilyContainer"; static async Task Main(string[] args) { CosmosClient cosmosClient = new CosmosClient(EndpointUrl, AuthorizationKey); await CreateDatabaseAsync(cosmosClient); await CreateContainerAsync(cosmosClient); await AddItemsToContainerAsync(cosmosClient); await QueryItemsAsync(cosmosClient); await ReplaceFamilyItemAsync(cosmosClient); await DeleteFamilyItemAsync(cosmosClient); await DeleteDatabaseAndCleanupAsync(cosmosClient); } } ドキュメント通りです。 フィールド変数のEndpointUrlとAuthorizationKeyは前提で取得しておいたURIとプライマリキーに書き換えればOKです。 ここから、Main関数で実行されている関数たちを定義していきます。 CreateDatabaseAsync関数の作成 /// <summary> /// Create the database if it does not exist /// </summary> private static async Task CreateDatabaseAsync(CosmosClient cosmosClient) { // Create a new database Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId); Console.WriteLine("Created Database: {0}\n", database.Id); } databaseの型はCosmosDatabaseではなく、Databaseに書き換えています。 CreateContainerAsync関数の作成 /// <summary> /// Create the container if it does not exist. /// Specify "/LastName" as the partition key since we're storing family information, to ensure good distribution of requests and storage. /// </summary> /// <returns></returns> private static async Task CreateContainerAsync(CosmosClient cosmosClient) { // Create a new container Container container = await cosmosClient.GetDatabase(DatabaseId).CreateContainerIfNotExistsAsync(ContainerId, "/LastName"); Console.WriteLine("Created Container: {0}\n", container.Id); } containerの型はCosmosContainerではなく、Containerに書き換えています。 AddItemsToContainerAsync関数の作成 /// <summary> /// Add Family items to the container /// </summary> private static async Task AddItemsToContainerAsync(CosmosClient cosmosClient) { // Create a family object for the Andersen family Family andersenFamily = new Family { Id = "Andersen.1", LastName = "Andersen", Parents = new Parent[] { new Parent { FirstName = "Thomas" }, new Parent { FirstName = "Mary Kay" } }, Children = new Child[] { new Child { FirstName = "Henriette Thaulow", Gender = "female", Grade = 5, Pets = new Pet[] { new Pet { GivenName = "Fluffy" } } } }, Address = new Address { State = "WA", County = "King", City = "Seattle" }, IsRegistered = false }; Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); try { // Read the item to see if it exists. ItemResponse<Family> andersenFamilyResponse = await container.ReadItemAsync<Family>(andersenFamily.Id, new PartitionKey(andersenFamily.LastName)); Console.WriteLine("Item in database with id: {0} already exists\n", andersenFamilyResponse.Resource.Id); } catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { // Create an item in the container representing the Andersen family. Note we provide the value of the partition key for this item, which is "Andersen" ItemResponse<Family> andersenFamilyResponse = await container.CreateItemAsync<Family>(andersenFamily, new PartitionKey(andersenFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. Console.WriteLine("Created item in database with id: {0}\n", andersenFamilyResponse.Resource.Id); } // Create a family object for the Wakefield family Family wakefieldFamily = new Family { Id = "Wakefield.7", LastName = "Wakefield", Parents = new Parent[] { new Parent { FamilyName = "Wakefield", FirstName = "Robin" }, new Parent { FamilyName = "Miller", FirstName = "Ben" } }, Children = new Child[] { new Child { FamilyName = "Merriam", FirstName = "Jesse", Gender = "female", Grade = 8, Pets = new Pet[] { new Pet { GivenName = "Goofy" }, new Pet { GivenName = "Shadow" } } }, new Child { FamilyName = "Miller", FirstName = "Lisa", Gender = "female", Grade = 1 } }, Address = new Address { State = "NY", County = "Manhattan", City = "NY" }, IsRegistered = true }; // Create an item in the container representing the Wakefield family. Note we provide the value of the partition key for this item, which is "Wakefield" ItemResponse<Family> wakefieldFamilyResponse = await container.UpsertItemAsync<Family>(wakefieldFamily, new PartitionKey(wakefieldFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. We can also access the RequestCharge property to see the amount of RUs consumed on this request. Console.WriteLine("Created item in database with id: {0}\n", wakefieldFamilyResponse.Resource.Id); } id取得の際にドキュメントでは andersenFamilyResponse.Value.IdやwakefieldFamilyResponse.Value.Idといった形で取得していますが、うまく取得できませんでした。 私の場合はandersenFamilyResponse.Resource.IdやwakefieldFamilyResponse.Resource.Idに書き換えることで取得できるようになりました。 QueryItemsAsync関数の作成 /// <summary> /// Run a query (using Azure Cosmos DB SQL syntax) against the container /// </summary> private static async Task QueryItemsAsync(CosmosClient cosmosClient) { //var sqlQueryText = "SELECT * FROM c WHERE c.LastName = 'Andersen'"; var sqlQueryText = "SELECT * FROM c"; Console.WriteLine("Running query: {0}\n", sqlQueryText); Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); //List<Family> families = new List<Family>(); using (FeedIterator<Family> feedIterator = container.GetItemQueryIterator<Family>(queryDefinition)) { while (feedIterator.HasMoreResults) { FeedResponse<Family> response = await feedIterator.ReadNextAsync(); foreach (Family family in response) { //families.Add(family); Console.WriteLine("\tRead {0}\n", family); } } } } ここは、割とドキュメント通りには動きませんでした。上記のように修正することでうまく動くようになります。 やりたいことは同じだと思います。ちなみに、familiesのリストがありますが、使わなかったのでコメントアウトしています。 ReplaceFamilyItemAsync関数の作成 /// <summary> /// Replace an item in the container /// </summary> private static async Task ReplaceFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); ItemResponse<Family> wakefieldFamilyResponse = await container.ReadItemAsync<Family>("Wakefield.7", new PartitionKey("Wakefield")); Family itemBody = wakefieldFamilyResponse; // update registration status from false to true itemBody.IsRegistered = true; // update grade of child itemBody.Children[0].Grade = 6; // replace the item with the updated content wakefieldFamilyResponse = await container.ReplaceItemAsync<Family>(itemBody, itemBody.Id, new PartitionKey(itemBody.LastName)); Console.WriteLine("Updated Family [{0},{1}].\n \tBody is now: {2}\n", itemBody.LastName, itemBody.Id, wakefieldFamilyResponse.Resource); } wakefieldFamilyResponse.Valueではなく、wakefieldFamilyResponse.Resourceぐらいでほとんどドキュメント通りです。 DeleteFamilyItemAsync関数の作成 /// <summary> /// Delete an item in the container /// </summary> private static async Task DeleteFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); string partitionKeyValue = "Wakefield"; string familyId = "Wakefield.7"; // Delete an item. Note we must provide the partition key value and id of the item to delete ItemResponse<Family> wakefieldFamilyResponse = await container.DeleteItemAsync<Family>(familyId, new PartitionKey(partitionKeyValue)); Console.WriteLine("Deleted Family [{0},{1}]\n", partitionKeyValue, familyId); } containerの型はCosmosContainerではなく、Containerに書き換えています。 DeleteDatabaseAndCleanupAsync関数の作成 /// <summary> /// Delete the database and dispose of the Cosmos Client instance /// </summary> private static async Task DeleteDatabaseAndCleanupAsync(CosmosClient cosmosClient) { Database database = cosmosClient.GetDatabase(DatabaseId); DatabaseResponse databaseResourceResponse = await database.DeleteAsync(); Console.WriteLine("Deleted Database: {0}\n", DatabaseId); } databaseの型はCosmosDatabaseではなく、Databaseに書き換えています。 全体のソースコード using System; using System.Net; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using todo; namespace AzureCosmosDBAPI { class Program { private const string EndpointUrl = "https://<your-account>.documents.azure.com:443/"; private const string AuthorizationKey = "<your-account-key>"; private const string DatabaseId = "FamilyDatabase"; private const string ContainerId = "FamilyContainer"; static async Task Main(string[] args) { CosmosClient cosmosClient = new CosmosClient(EndpointUrl, AuthorizationKey); await CreateDatabaseAsync(cosmosClient); await CreateContainerAsync(cosmosClient); await AddItemsToContainerAsync(cosmosClient); await QueryItemsAsync(cosmosClient); await ReplaceFamilyItemAsync(cosmosClient); await DeleteFamilyItemAsync(cosmosClient); await DeleteDatabaseAndCleanupAsync(cosmosClient); } /// <summary> /// Create the database if it does not exist /// </summary> private static async Task CreateDatabaseAsync(CosmosClient cosmosClient) { // Create a new database Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId); Console.WriteLine("Created Database: {0}\n", database.Id); } /// <summary> /// Create the container if it does not exist. /// Specify "/LastName" as the partition key since we're storing family information, to ensure good distribution of requests and storage. /// </summary> /// <returns></returns> private static async Task CreateContainerAsync(CosmosClient cosmosClient) { // Create a new container Container container = await cosmosClient.GetDatabase(DatabaseId).CreateContainerIfNotExistsAsync(ContainerId, "/LastName"); Console.WriteLine("Created Container: {0}\n", container.Id); } /// <summary> /// Add Family items to the container /// </summary> private static async Task AddItemsToContainerAsync(CosmosClient cosmosClient) { // Create a family object for the Andersen family Family andersenFamily = new Family { Id = "Andersen.1", LastName = "Andersen", Parents = new Parent[] { new Parent { FirstName = "Thomas" }, new Parent { FirstName = "Mary Kay" } }, Children = new Child[] { new Child { FirstName = "Henriette Thaulow", Gender = "female", Grade = 5, Pets = new Pet[] { new Pet { GivenName = "Fluffy" } } } }, Address = new Address { State = "WA", County = "King", City = "Seattle" }, IsRegistered = false }; Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); try { // Read the item to see if it exists. ItemResponse<Family> andersenFamilyResponse = await container.ReadItemAsync<Family>(andersenFamily.Id, new PartitionKey(andersenFamily.LastName)); Console.WriteLine("Item in database with id: {0} already exists\n", andersenFamilyResponse.Resource.Id); } catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { // Create an item in the container representing the Andersen family. Note we provide the value of the partition key for this item, which is "Andersen" ItemResponse<Family> andersenFamilyResponse = await container.CreateItemAsync<Family>(andersenFamily, new PartitionKey(andersenFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. Console.WriteLine("Created item in database with id: {0}\n", andersenFamilyResponse.Resource.Id); } // Create a family object for the Wakefield family Family wakefieldFamily = new Family { Id = "Wakefield.7", LastName = "Wakefield", Parents = new Parent[] { new Parent { FamilyName = "Wakefield", FirstName = "Robin" }, new Parent { FamilyName = "Miller", FirstName = "Ben" } }, Children = new Child[] { new Child { FamilyName = "Merriam", FirstName = "Jesse", Gender = "female", Grade = 8, Pets = new Pet[] { new Pet { GivenName = "Goofy" }, new Pet { GivenName = "Shadow" } } }, new Child { FamilyName = "Miller", FirstName = "Lisa", Gender = "female", Grade = 1 } }, Address = new Address { State = "NY", County = "Manhattan", City = "NY" }, IsRegistered = true }; // Create an item in the container representing the Wakefield family. Note we provide the value of the partition key for this item, which is "Wakefield" ItemResponse<Family> wakefieldFamilyResponse = await container.UpsertItemAsync<Family>(wakefieldFamily, new PartitionKey(wakefieldFamily.LastName)); // Note that after creating the item, we can access the body of the item with the Resource property off the ItemResponse. We can also access the RequestCharge property to see the amount of RUs consumed on this request. Console.WriteLine("Created item in database with id: {0}\n", wakefieldFamilyResponse.Resource.Id); } /// <summary> /// Run a query (using Azure Cosmos DB SQL syntax) against the container /// </summary> private static async Task QueryItemsAsync(CosmosClient cosmosClient) { //var sqlQueryText = "SELECT * FROM c WHERE c.LastName = 'Andersen'"; var sqlQueryText = "SELECT * FROM c"; Console.WriteLine("Running query: {0}\n", sqlQueryText); Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); //List<Family> families = new List<Family>(); using (FeedIterator<Family> feedIterator = container.GetItemQueryIterator<Family>(queryDefinition)) { while (feedIterator.HasMoreResults) { FeedResponse<Family> response = await feedIterator.ReadNextAsync(); foreach (Family family in response) { //families.Add(family); Console.WriteLine("\tRead {0}\n", family); } } } } /// <summary> /// Replace an item in the container /// </summary> private static async Task ReplaceFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); ItemResponse<Family> wakefieldFamilyResponse = await container.ReadItemAsync<Family>("Wakefield.7", new PartitionKey("Wakefield")); Family itemBody = wakefieldFamilyResponse; // update registration status from false to true itemBody.IsRegistered = true; // update grade of child itemBody.Children[0].Grade = 6; // replace the item with the updated content wakefieldFamilyResponse = await container.ReplaceItemAsync<Family>(itemBody, itemBody.Id, new PartitionKey(itemBody.LastName)); Console.WriteLine("Updated Family [{0},{1}].\n \tBody is now: {2}\n", itemBody.LastName, itemBody.Id, wakefieldFamilyResponse.Resource); } /// <summary> /// Delete an item in the container /// </summary> private static async Task DeleteFamilyItemAsync(CosmosClient cosmosClient) { Container container = cosmosClient.GetContainer(DatabaseId, ContainerId); string partitionKeyValue = "Wakefield"; string familyId = "Wakefield.7"; // Delete an item. Note we must provide the partition key value and id of the item to delete ItemResponse<Family> wakefieldFamilyResponse = await container.DeleteItemAsync<Family>(familyId, new PartitionKey(partitionKeyValue)); Console.WriteLine("Deleted Family [{0},{1}]\n", partitionKeyValue, familyId); } /// <summary> /// Delete the database and dispose of the Cosmos Client instance /// </summary> private static async Task DeleteDatabaseAndCleanupAsync(CosmosClient cosmosClient) { Database database = cosmosClient.GetDatabase(DatabaseId); DatabaseResponse databaseResourceResponse = await database.DeleteAsync(); Console.WriteLine("Deleted Database: {0}\n", DatabaseId); } } } おわりに 全体的に動くように、細かい部分で書き換えています。 説明できずに書き換えている箇所もあるかもしれませんが、ご理解お願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C# Linq】Aggregateの動き方・使い方

Aggregate? Aggregateメソッドとはなんぞや Microsoft曰く まずはMSの公式ドキュメントを見てみます。 シーケンスにアキュムレータ関数を適用します。 (中略) 一般的な集計操作を簡略化するために、標準クエリ演算子には、汎用カウントメソッド、 Count 、および4つの数値集計メソッド (、、、および) も含まれて Min Max Sum Average います。 ???????? MSの公式ドキュメントは翻訳の質が低いことが多くて困りますね。 概要 "Aggregate"は日本語で「集計」とか大体その辺を意味する言葉です。 その名の通り、IEnumerable<T>を継承したコレクションの中身全てについて集計処理を行ってくれる汎用性の高いメソッドです。 やろうと思えばLinqの他のメソッド、Min・MaxもSumもAverageも、Aggregateを使えば自力で書けたりするわけですね。 (処理の意味の明確化のためにはそれ用にあつらえてあるメソッドを使うべきですが) Aggregateの動き方 使い方を知る前に、Aggregateがどのような動きをするのか確認しておきましょう。 なお、サンプルコードはトップレベルステートメントを使っているので、C# 9(.NET 5) 以降のバージョンでしかコンパイルが通らない点に注意してください。 Aggregateメソッドには3つのオーバーロードがあります。 まずは一番基本的な、集計用のラムダ式一つだけを引数としてとるものからはじめます。 using System; using System.Linq; var array = new[] { 1, 2, 3, 4, 5 }; var sum = array.Aggregate((result, current) => result + current); Console.WriteLine($"1~5 の総和は{sum}です!"); // 1~5 の総和は15です! Aggregateに渡す集計用ラムダ式には2つの引数が必要です。 上記の例ではresultとcurrentと名付けています。 それではAggregateメソッドを実行したときの動きを追ってみましょう。 初回はresultに最初の要素が、currentには次の要素が代入される。 この例だとresult = 1 current = 2 result + currentが評価され、結果がreturnされる。 例では3が返ります。 返り値がresultに新たに代入され、currentに次の要素が入る。 result = 3 current = 3 同様に最後の要素まで計算する。 つまり、Aggregateにわたすラムダ式の構成要素は以下のようなものと言えるわけです: 第一引数には直前の評価の戻り値が入っていて、次の評価に値を伝えてくれる。 第ニ引数には現在の要素が次々と入っていく。 ラムダ式の戻り値が次に持ち越される計算結果になる。 このことがわかれば、あとの2つのオーバーロードの理解も簡単です。単に処理の前後に何かしらの処理をくっつけているだけなので。 using System; using System.Linq; var array = new[] { 1, 2, 3, 4, 5 }; // 第一引数として値や変数を渡すことで、第二引数のラムダ式の最初の引数 result の初期値を決められます。 // 初期値を与えたとき、最初の current はコレクションの最初の要素になります。 var sum1 = array.Aggregate(15, (result, current) => result + current); Console.WriteLine($"15から始めた総和は{sum1}です!"); // 15から始めた総和は30です! // 第三引数としてラムダ式を渡すことで、計算結果に対して何かしらの処理を施すことができます。 var sum2 = array.Aggregate(0, (result, current) => result + current, result => result * result); Console.WriteLine($"1~5 の総和の2乗は{sum2}です!"); // 1~5 の総和の2乗は225です! 便利な使い方 Aggregateの動き方がわかったところで、ようやく本題の具体的にはどんな活用方法があるかを紹介していきます。 文字列の連結 要素を文字列化して任意のセパレータを挟んだ文字列に変換できます。 using System; using System.Linq; var array = new[] { 1, 2, 3, 4, 5 }; // 要素の加工はSelectによって予め済ましておくほうが、Aggregateに渡すラムダ式を簡潔にできます。 var csvRow = array.Select(i => i.ToString()).Aggregate((result, current) => $"{result}, {current}"); Console.WriteLine(csvRow); // 出力 // 1, 2, 3, 4, 5 var matrix = Enumerable.Repeat(csvRow, 4); var csvMat = matrix.Aggregate((result, current) => result + Environment.NewLine + current); Console.WriteLine(csvMat); // 出力 // 1, 2, 3, 4, 5 // 1, 2, 3, 4, 5 // 1, 2, 3, 4, 5 // 1, 2, 3, 4, 5 複数要素の同時集計 Linqに用意されているSumやAverage、Min・Maxメソッドは同時に一つの値についてしか計算できません。 リストの中に入っているオブジェクトの複数のプロパティについてこれらの結果を求めたい場合、Aggregateを使って自分で書いてやる必要があります。 using System; using System.Linq; using System.Drawing; var array = new[] { new Point(1, 4), new Point(3, 2), new Point(2, 8), new Point(4, 6) }; var sum = array.Aggregate((result, current) => new Point(result.X + current.X, result.Y + current.Y)); Console.WriteLine(sum); // {X=10,Y=20} var max = array.Aggregate((result, current) => new Point( Math.Max(result.X, current.X), Math.Max(result.Y, current.Y) )); Console.WriteLine(max); // {X=4,Y=8} 複数の条件を満たしているかの調査 条件をまとめた配列があれば、真理値の総計を取ることでそれらを満たしているかの調査が1行で書けてしまいます。 using System; using System.Linq; var conditions = new Func<int, bool>[] { (value) => 0 < value, (value) => value < 10, (value) => value % 2 == 0 }; // すべての条件を満たしているか? // 論理積計算のときは初期値を true にします。 var x = 3; var isSatisfy = conditions.Aggregate(true, (result, current) => result && current(x)); Console.WriteLine(isSatisfy); // False var y = 4; isSatisfy = conditions.Aggregate(true, (result, current) => result && current(y)); Console.WriteLine(isSatisfy); // True // 少なくとも一つの条件を満たしているか? // 論理和計算のときは初期値を false にします。 var z = -1; isSatisfy = conditions.Aggregate(false, (result, current) => result || current(z)); Console.WriteLine(isSatisfy); // True おわり 紹介する使い方は以上でおわりですが、Aggregateメソッドは汎用性の高いメソッドなので、この他にもアイデア次第でいろんな事ができるでしょう。 ただ、慣れていないと中々何をやっているかがわかりにくくなりやすいメソッドでもあると思います。 実際に活用する際はAggregateをそのまま使用するのではなく、拡張メソッドにラップして、どのような処理なのかを明確にすることも検討したほうがいいかもしれませんね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

もっと早く知りたかったVisual Studioの作業速度を上げる小技集

はじめに Linux開発環境からWindows開発環境へ移り約1年、最初はVisual Studioの操作に慣れるのに苦労した。とにかく、”多機能すぎて、どこに何があるか分からない”。毎度ネット検索を挟むと、作業時間ロスがじわじわ効いてくる。 時間をおいて振り返ると、以下の点を早く知っていたら、「もっと作業時間を短縮できたのでは?」と思って記事にしてみた。 Visual Studio内で機能を検索して、実行する方法 ソースコードの調査/実装に役立つ便利機能/ショートカット 同じくVisual Studioの操作に苦しんでいる人のヒントになれば良いなと思います。 ご指摘やアドバイスを貰えると、すごく嬉しいです。 目次 動作確認環境 もっと早く知りたかった機能 よく使うショートカット 蛇足 参考 動作確認環境 Visual Studio2019 もっと早く知りたかった機能 クイック起動 Visual Studioの搭載機能の中から、目的の操作を探して・実行するための機能。 この機能で「Visual Studioが多機能すぎて、分からない」って状態から解放される。 手順 1.Visual Studioにフォーカスをあてた状態で、「Ctrl +Q」あるいは「クイック起動のテキストボックス」を選択する。 2.任意の文字列を入力すると、関連する機能が表示される。一覧から選択すると、選択した機能を実行できる。 下記はクイック起動でデバックを実行した例です。 デバックをわざわざクイック起動で実行する人はいないと思いますが、「機能の検索と実行ができること」を伝えるために身近な機能にしました。 この記事Visual Studioの機能を呼び出すショートカット"クイック起動"の解説を参考にしました。 コードスニペット あらかじめ用意されているコードを挿入するVisual Studioの標準機能です。 開発言語ごとに準備されているようです。 C#のコンソール出力を挿入する例 手順 1.ショートカットキーを入力する。 2.inteliSenceが候補を表示するので、Tabキーで確定すると、コード挿入される。 3.更にTabキーを押すと、自動挿入されたコード内でジャンプできる。 手順を用いて、C#のforeach文を挿入した例 コードスニペット一覧 こちらの記事Visual Studio C#コードスニペット早見表が参考になります。 より深く知りたい方はこちらの記事C#コードスニペットを参考にすると良いです。 よく使うショートカット Microsoft社の下記記事より、よく利用するものを抜粋。 Visual Studio のショートカットに関するヒント C#開発者のためのVisual Studio 生産性ガイド ウインドウ操作 1つのモニタで作業するときに使うショートカット。 タスク ショートカット ウィンドウの最大化/最小化 Windowsキー + ↑ / Windowsキー + ↓ ウィンドウの移動 Windowsキー + ← / Windowsキー + → 検索 タスク ショートカット 備考 ソリューションエクスプローラの検索 Ctrl+ ; ソリューリョンエクスプローラを選択した状態で実行すること。 エディター内のクイック検索 Ctrl+F クイック検索の次の結果 Enter クイック検索を実行後に使える クイック検索の前の結果 Shift + Enter クイック検索を実行後に使える [フォルダーを指定して検索] Ctrl+Shift+F ソリューション内にソースコードが多いときに使う検索。 コードエディタ タスク ショートカット 備考 ドキュメントのフォーマット Ctrl+K、Ctrl+D ソースのインデントを調整したいときなどに使う。Ctrl + Kを押下してから、そのままCtrl + Dを押下する。 クイックアクション Ctrl+. キー C#だと、Alt + Enterでも可能。あるいは行頭に出てきた電球アイコンをクリックする。 移動 Ctrl+ , 複数ソースコードが存在すると現在開かれているソース以外の候補が表示される。筆者の環境はJetBrain社のResharperが入っているため、多少異なるかも。 行の複製 Ctrl+E、V 複製したい行を選択した状態で実行すること。C#なら、Ctrl+Dでカーソルのあるコード行を複製できる(個人的にはこっちをよく使う)。 定義へ移動 F12 定義をここに表示 Alt+F12 表示された定義の画面を編集すると、対象ソースを含むファイルも更新される。 選択範囲をコメント Ctrl+K,Ctrl+C Ctrl + Kを押下してから、そのままCtrl + Cを押下する。 選択範囲をコメントを解除 Ctrl+K,Ctrl+U Ctrl + Kを押下してから、そのままCtrl+Uを押下する。 前の問題に移動 Alt + PgUp ドキュメント内の【前】のエラー、警告、提案に移動する。 次の問題に移動 Alt + PgDn ドキュメント内の【次】のエラー、警告、提案に移動する。 矩形選択 Alt+Shift+カーソル Altキー押しながらマウス移動でも可能。 ソリューションのビルド Ctrl+Shift+B 編集位置を1つ前に戻る Ctrl + -(マイナス) ソースコードの調査で別の実装にジャンプして、元の編集位置が分からなくなったときに重宝する。 編集位置を1つ前に進める Ctrl+ Shift + -(マイナス) デバック タスク ショートカット 備考 デバッグの開始 F5 デバッグなしで開始 Ctrl+F5 デバッグの停止 Shift+F5 ステップ オーバー F10 ステップ イン F11 ステップ アウト Shift+F11 ブレークポイントの設定と切り替え F9 ブレークポイントの無効化 Ctrl+F9 蛇足 Visual Studio 2022が世に浸透すると、操作性の違いにまた苦しむんだろうなー( -_-) 参考 Visual Studio のショートカットに関するヒント C#開発者のためのVisual Studio 生産性ガイド Visual Studioの機能を呼び出すショートカット"クイック起動"の解説 クイックアクション Visual Studio 2017 のリファクタリング機能 IntelliSenseを活用してキモチいいコーディングを実現しよう C#コードスニペット Visual Studio C#コードスニペット早見表 Visual Studio 2022
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Visual Studio】もっと早く知りたかった作業速度を上げる小技集(随時更新)

はじめに Linux開発環境からWindows開発環境へ移り、最初はVisual Studioの操作に苦労した。”多機能すぎて、どこに何があるか分からない”。操作方法をネットで調べる度に、作業時間ロスがちりつも。 振り返ると以下の点を早く知っていたら、「もっと作業を速くできたのでは?」と思って記事にしてみました。 Visual Studio内で機能を検索して、実行する方法 ソースコードの調査/実装に役立つ便利機能/ショートカット 同じく、Visual Studioの操作に苦しんでいる方、初心者の方のヒントになれば良いなと思います。 ご指摘やアドバイスを貰えると、すごく嬉しいです。 目次 動作確認環境 もっと早く知りたかった機能 よく使うショートカット 参考 動作確認環境 Visual Studio2019 もっと早く知りたかった機能 クイック起動 Visual Studioの搭載機能の中から、目的の操作を探して・実行するための機能。 この機能で「Visual Studioが多機能すぎて、分からない」って状態から解放される。 手順 1.Visual Studioにフォーカスをあてた状態で、「Ctrl +Q」あるいは「クイック起動のテキストボックス」を選択する。 2.任意の文字列を入力すると、関連する機能が表示される。一覧から選択すると、選択した機能を実行できる。 下記はクイック起動でデバックを実行した例です。 デバックをわざわざクイック起動で実行する人はいないと思いますが、「機能の検索と実行ができること」を伝えるために身近な機能にしました。 この記事Visual Studioの機能を呼び出すショートカット"クイック起動"の解説を参考にしました。 コードスニペット あらかじめ用意されているコードを挿入するVisual Studioの標準機能です。 開発言語ごとに準備されているようです。 C#のコンソール出力を挿入する例 手順 1.ショートカットキーを入力する。 2.inteliSenceが候補を表示するので、Tabキーで確定すると、コード挿入される。 3.更にTabキーを押すと、自動挿入されたコード内でジャンプできる。 手順を用いて、C#のforeach文を挿入した例 コードスニペット一覧 こちらの記事Visual Studio C#コードスニペット早見表が参考になります。 より深く知りたい方はこちらの記事C#コードスニペットを参考にすると良いです。 よく使うショートカット Microsoft社の下記記事より、よく利用するものを抜粋。 Visual Studio のショートカットに関するヒント C#開発者のためのVisual Studio 生産性ガイド ウインドウ操作 1つのモニタで作業するときに使うショートカット。 タスク ショートカット ウィンドウの最大化/最小化 Windowsキー + ↑ / Windowsキー + ↓ ウィンドウの移動 Windowsキー + ← / Windowsキー + → 検索 タスク ショートカット 備考 ソリューションエクスプローラの検索 Ctrl+ ; ソリューリョンエクスプローラを選択した状態で実行すること。 エディター内のクイック検索 Ctrl+F クイック検索の次の結果 Enter クイック検索を実行後に使える クイック検索の前の結果 Shift + Enter クイック検索を実行後に使える [フォルダーを指定して検索] Ctrl+Shift+F ソリューション内にソースコードが多いときに使う検索。 コードエディタ タスク ショートカット 備考 ドキュメントのフォーマット Ctrl+K、Ctrl+D ソースのインデントを調整したいときなどに使う。Ctrl + Kを押下してから、そのままCtrl + Dを押下する。 クイックアクション Ctrl+. キー C#だと、Alt + Enterでも可能。あるいは行頭に出てきた電球アイコンをクリックする。 移動 Ctrl+ , 複数ソースコードが存在すると現在開かれているソース以外の候補が表示される。筆者の環境はJetBrain社のResharperが入っているため、多少異なるかも。 行の複製 Ctrl+E、V 複製したい行を選択した状態で実行すること。C#なら、Ctrl+Dでカーソルのあるコード行を複製できる(個人的にはこっちをよく使う)。 定義へ移動 F12 定義をここに表示 Alt+F12 表示された定義の画面を編集すると、対象ソースを含むファイルも更新される。 選択範囲をコメント Ctrl+K,Ctrl+C Ctrl + Kを押下してから、そのままCtrl + Cを押下する。 選択範囲をコメントを解除 Ctrl+K,Ctrl+U Ctrl + Kを押下してから、そのままCtrl+Uを押下する。 前の問題に移動 Alt + PgUp ドキュメント内の【前】のエラー、警告、提案に移動する。 次の問題に移動 Alt + PgDn ドキュメント内の【次】のエラー、警告、提案に移動する。 矩形選択 Alt+Shift+カーソル Altキー押しながらマウス移動でも可能。 ソリューションのビルド Ctrl+Shift+B 編集位置を1つ前に戻る Ctrl + -(マイナス) ソースコードの調査で別の実装にジャンプして、元の編集位置が分からなくなったときに重宝する。 編集位置を1つ前に進める Ctrl+ Shift + -(マイナス) デバック タスク ショートカット 備考 デバッグの開始 F5 デバッグなしで開始 Ctrl+F5 デバッグの停止 Shift+F5 ステップ オーバー F10 ステップ イン F11 ステップ アウト Shift+F11 ブレークポイントの設定と切り替え F9 ブレークポイントの無効化 Ctrl+F9 蛇足 Visual Studio 2022が世に浸透すると、操作性の違いにまた苦しむんだろうなー( -_-) 参考 Visual Studio のショートカットに関するヒント C#開発者のためのVisual Studio 生産性ガイド Visual Studioの機能を呼び出すショートカット"クイック起動"の解説 クイックアクション Visual Studio 2017 のリファクタリング機能 IntelliSenseを活用してキモチいいコーディングを実現しよう C#コードスニペット Visual Studio C#コードスニペット早見表 Visual Studio 2022
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

周波数空間での相関処理

相関処理とは 相関値とは「どれくらい似ているか」という数値で、ある信号と別の信号がどれくらい似ているかを調べるのが相関処理です。 この図ではleft(青色)が基準となる信号で、right(橙色)がそれをずらした信号です。この2つを相関処理するとcorr(赤色)の信号になります。 この図では複素数の実部のみを表示していますが、実際にはMagnitudeが1になるランダムな複素数が入力されています。35ポイントからのrightはleftと同じく大きさが1なので、相関値も1になっています。一方で60ポイントからのrightはleftの80%の振幅なので、相関値も0.8になっています。基準信号が正規化されていて、入力信号と基準信号が全く同じ形の場合、相関処理を行うと入力信号の大きさが求まります。 上の図の相関処理は、あらかじめ基準となる信号がわかっている場合の処理でした。これは例えばレーダーのように、自分が出した信号の反射を受信して処理するような状況に相当します(他の状況では、重力波望遠鏡のような、「シミュレーションで作った基準信号(本当の形は未知)」に対して相関処理を行う場合もあります)。 逆に、全く知らない信号に対しての相関処理も可能です。 この図ではランダムな信号を110ポイント作り、leftには後ろの100ポイントを、rightには前の100ポイントを与えています。このような信号に対して相関を行っても、正しく10ポイントの位置にピークが出ています(位置がずれているので、相関値は1よりも小さくなります)。この図ではleftに対してrightが遅れている状態ですが、leftに対してrightが進んでいる状態の場合は後ろ側にピークが出現します(一般的なFFTでの負の周波数のような形)。 今回はleftとrightは位置がずれた同じ雑音を使用していますが、一つの雑音に対してさらにleftとrightで個別の雑音が乗った信号でも同じように相関処理を行うことができます。 このような手法は例えばVLBIと呼ばれる分野で使用されています。ブラックホールを撮影したことで話題のアレです。数億光年というとてつもない距離からやってきた電波を離れた2箇所(数千kmオーダー)で受信し、相関処理を行うことで、その2箇所間の正確な距離(ミリメートルオーダー)や運動を求めることができます(これによって大陸の運動や地球の自転を極めて正確に観測できるようになりました)。 時間空間での相関処理 一番シンプルなやり方です。力技とも言います。計算力が大量に必要です。 static Complex[] CorrelationTime(Complex[] left, Complex[] right) { if (right.Length < left.Length) { throw new ArgumentException(); } var result = new Complex[right.Length]; for (int i = 0; i < right.Length; i++) { var tmp = Complex.Zero; for (int j = 0; j < left.Length; j++) { tmp += left[j].Conjugate() * right[(i + j) % right.Length]; } result[i] = tmp / left.Length; } return result; } このコードではすべての遅延量を計算していますが、ある程度予想ができる場合は必要な範囲だけ計算すれば大幅に計算量を削減できます。 蛇足ですが、昔の人はこれを人力でやったそうです。ゼロイチの二値データ2組を二人で同時に読み上げて、3人目が耳でXORを処理して正の字を書く、というふうにやっていたらしいです(もちろん遅延量(上のコードでは変数i)を変えて何回もやり直すわけです)。昔の人すごい。 周波数空間での相関処理 フーリエ変換で周波数空間に移動し、かけ合わせた上で逆変換を行って時間空間に戻します。 static Complex[] CorrelationFreq(Complex[] left, Complex[] right) { if (right.Length < left.Length) { throw new ArgumentException(); } var a = new Complex[right.Length]; Array.Copy(left, a, left.Length); Fourier.Forward(a, FourierOptions.NoScaling); for (int i = 0; i < a.Length; i++) { a[i] = a[i].Conjugate() / (left.Length * right.Length); } var b = new Complex[right.Length]; Array.Copy(right, b, right.Length); Fourier.Forward(b, FourierOptions.NoScaling); for (int i = 0; i < a.Length; i++) { b[i] = a[i] * b[i]; } Fourier.Inverse(b, FourierOptions.NoScaling); return b; } このコードでは相関するたびにleftの計算を行っています。leftが毎回異なる場合はこうする他ありません。 leftがいつも同じである場合(例えばレーダー等)は、あらかじめ周波数空間で共役を取ったleftを用意しておけば、計算量を2/3まで減らすことができます。 比較 大雑把に時間空間と周波数空間での計算時間の比較を行ってみました。使用する環境やライブラリによってもかなり変わるはずなので、あくまでも参考程度に。FFTライブラリはマルチスレッドで処理を行うので公平な比較でもないですし。 時間空間での計算は単純なforループなのでポイント数と計算時間が綺麗に比例します。 周波数空間での計算も、やはり傾向としてはポイント数が増えれば計算時間も増えますが、時間空間よりは穏やかな傾きです。また、高速フーリエ変換のアルゴリズムとして、ポイント数が2のべき乗のときは計算が早く、それが下の線です。512ポイントまでは任意のポイント数の場合と比べて1桁(10倍)程度の差があります。1024ポイント以上は差が縮まりますが、それでも任意ポイントの数倍、時間空間と比べれば数百倍程度早く処理できます(512ポイント以下と1024ポイント以上での差は使用しているライブラリの特性に関する挙動だと思います)。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む