- 投稿日:2020-06-23T20:32:06+09:00
#programming #csharp クラス内に定義したクラスの正式名(qualified name)
「+」で連結されるっぽい。
using System; namespace qualified_name { class Program { public class SubClass { public SubClass() { } } static void Main(string[] args) { Console.WriteLine(typeof(SubClass).ToString()); } } }>qualified_name.exe qualified_name.Program+SubClass >
- 投稿日:2020-06-23T11:55:16+09:00
Azure Functions Core Tools v3.xを使ってローカル関数プロジェクト開発をする方法
はじめに
Azure Functions のローカル関数プロジェクトを利用した開発方法については、公式サイトの「Azure Functions Core Tools の操作」で詳しく紹介されています。
このページの通り進めることで Azure Functions Core Tools のバージョンが
v2.x
ではうまく動作させることが可能となっていますが、v3.x
ではうまく動作しません。
// Node.js 13.x 以降を利用している場合に限るまた、Azure Functions は F# に対応しているにも関わらず、Azure Functions Core Toolsを使って F# のローカル関数プロジェクトを作成することができません。
そこで今回は (1) Azure Functions Core Toolsv3.x を利用 しつつ、(2) F# でローカル関数プロジェクト開発するための方法 について紹介していきたいと思います。
前提条件
筆者の動作環境は以下のようになっています。
名称 バージョン Windows 10 64bit 1903 Visual Studio Code 1.46.1 .NET Core 3.1.301 node 14.1.0 npm 6.14.5 バージョンが更新されることで、今回の対応が不要になる可能性が高まると思います。
Azure Functions Core Tools v3.x の導入
以下のコマンドを利用して Azure Functions Core Tools v3.x をインストールします。
powershellnpm install -g azure-functions-core-tools@3 --unsafe-perm trueすると以下のようにダウンロードが完了していないにも関わらず、インストール処理が終了してしまうと思います。
# <中略> attempting to GET "https://functionscdn.azureedge.net/public/3.0.2534/Azure.Functions.Cli.win-x64.3.0.2534.zip" [------------------] Downloading Azure Functions Core Tools+ azure-functions-core-tools@3.0.2534 updated 6 packages in 1.289sそこで以下のコマンドを実行し、azure-functions-core-tools に unzipper@0.10.7 をインストールします。
powershellcd $env:AppData/npm/node_modules/azure-functions-core-tools npm install unzipper@0.10.7 node .\lib\install.jsすると無事 Azure Functions Core Tools v3.x がインストールされます。
これでめでたくfunc
コマンドが利用できるようになりました。F# でローカル関数プロジェクトを作成する
通常、func init コマンドを利用することでローカル関数プロジェクトを作成することができます。
2020/06/23 現在、C# / Java / JavaScript / TypeScript / PowerShell / Python の6言語についてはオプションを指定することでプロジェクトを即座に作成することができます。そう、F# を除くすべての言語が対応しているのです!!
// Microsoft、そういうトコやぞでは F# はどうすればよいのかというと、仕方がないので C# のプロジェクトを修正するしかありません。
まずは以下のコマンドでローカル関数プロジェクトを作成します。
powershellfunc init SampleFuncProjすると以下のような選択肢が現れるので、dotnet を選択します。
実行が完了するとローカル関数プロジェクトができあがります。
これを VSCode で起動します。powershellcode ./SampleFuncProj次に SampleFuncProj .csproj を SampleFuncProj .fsproj にリネームします。
そして SampleFuncProj.fsproj 内の以下の箇所を書き換えます。SampleFuncProj.fsproj<!-- <None Update="host.json"> --> 10行目あたりのコード <None Include="host.json">これで F# 用のローカル関数プロジェクトが完成しました!
おまけ:F# のローカル関数プロジェクトで Hello, World してみる
fsproj を Visual Studio で開くことで C# と同じように開発できるのですが、今回は VSCode を利用しての開発方法について紹介しておきます。
hello.fs というファイルを作成し、SampleFuncProj.fsproj を以下のように修正しています。
※ファイルの追加については ionide を利用することで、fsproj に対象ファイルの情報が自動で反映されるのでそちらを利用する方が良いと思います。
次に hello.fs を以下のようにします。
hello.fsmodule Hello open Microsoft.AspNetCore.Mvc open Microsoft.Azure.WebJobs open Microsoft.Azure.WebJobs.Extensions.Http open Microsoft.AspNetCore.Http [<FunctionName("hello")>] let run ([<HttpTrigger(AuthorizationLevel.Function, "post", "get", Route = null)>] req: HttpRequest) = OkObjectResult "Hello, World" :> ActionResult最後に以下のコマンドを実行することで、Azure Functions をローカル実行することができます。
powershellfunc start --build以下のエンドポイントにアクセスすることで実行することができます。
おまけのおまけ
余談ですが VSCode に Azure Functions の拡張機能を入れているとローカル関数プロジェクトを開いたときに以下のようなメッセージが表示されます。
これで Yes を選択してあげると、F5 でデバッグ実行が可能となります。
もちろんブレークポイントを設定してステップ実行をすることも可能です。
もし VSCode で Azure Functions の開発をする際には是非利用してみてください。
- 投稿日:2020-06-23T11:55:16+09:00
Azure Functions Core Tools v3.x を使ってローカル関数プロジェクト開発をする方法
はじめに
Azure Functions のローカル関数プロジェクトを利用した開発方法については、公式サイトの「Azure Functions Core Tools の操作」で詳しく紹介されています。
このページの通り進めることで Azure Functions Core Tools のバージョンが
v2.x
ではうまく動作させることが可能となっていますが、v3.x
ではうまく動作しません。
// Node.js 13.x 以降を利用している場合に限るまた、Azure Functions は F# に対応しているにも関わらず、Azure Functions Core Toolsを使って F# のローカル関数プロジェクトを作成することができません。
そこで今回は (1) Azure Functions Core Toolsv3.x を利用 しつつ、(2) F# でローカル関数プロジェクト開発するための方法 について紹介していきたいと思います。
前提条件
筆者の動作環境は以下のようになっています。
名称 バージョン Windows 10 64bit 1903 Visual Studio Code 1.46.1 .NET Core 3.1.301 node 14.1.0 npm 6.14.5 バージョンが更新されることで、今回の対応が不要になる可能性が高まると思います。
Azure Functions Core Tools v3.x の導入
以下のコマンドを利用して Azure Functions Core Tools v3.x をインストールします。
powershellnpm install -g azure-functions-core-tools@3 --unsafe-perm trueすると以下のようにダウンロードが完了していないにも関わらず、インストール処理が終了してしまうと思います。
# <中略> attempting to GET "https://functionscdn.azureedge.net/public/3.0.2534/Azure.Functions.Cli.win-x64.3.0.2534.zip" [------------------] Downloading Azure Functions Core Tools+ azure-functions-core-tools@3.0.2534 updated 6 packages in 1.289sそこで以下のコマンドを実行し、azure-functions-core-tools に unzipper@0.10.7 をインストールします。
powershellcd $env:AppData/npm/node_modules/azure-functions-core-tools npm install unzipper@0.10.7 node .\lib\install.jsすると無事 Azure Functions Core Tools v3.x がインストールされます。
これでめでたくfunc
コマンドが利用できるようになりました。F# でローカル関数プロジェクトを作成する
通常、func init コマンドを利用することでローカル関数プロジェクトを作成することができます。
2020/06/23 現在、C# / Java / JavaScript / TypeScript / PowerShell / Python の6言語についてはオプションを指定することでプロジェクトを即座に作成することができます。そう、F# を除くすべての言語が対応しているのです!!
// Microsoft、そういうトコやぞでは F# はどうすればよいのかというと、仕方がないので C# のプロジェクトを修正するしかありません。
まずは以下のコマンドでローカル関数プロジェクトを作成します。
powershellfunc init SampleFuncProjすると以下のような選択肢が現れるので、dotnet を選択します。
実行が完了するとローカル関数プロジェクトができあがります。
これを VSCode で起動します。powershellcode ./SampleFuncProj次に SampleFuncProj .csproj を SampleFuncProj .fsproj にリネームします。
そして SampleFuncProj.fsproj 内の以下の箇所を書き換えます。SampleFuncProj.fsproj<!-- <None Update="host.json"> --> 10行目あたりのコード <None Include="host.json">これで F# 用のローカル関数プロジェクトが完成しました!
おまけ:F# のローカル関数プロジェクトで Hello, World してみる
fsproj を Visual Studio で開くことで C# と同じように開発できるのですが、今回は VSCode を利用しての開発方法について紹介しておきます。
hello.fs というファイルを作成し、SampleFuncProj.fsproj を以下のように修正しています。
※ファイルの追加については ionide を利用することで、fsproj に対象ファイルの情報が自動で反映されるのでそちらを利用する方が良いと思います。
次に hello.fs を以下のようにします。
hello.fsmodule Hello open Microsoft.AspNetCore.Mvc open Microsoft.Azure.WebJobs open Microsoft.Azure.WebJobs.Extensions.Http open Microsoft.AspNetCore.Http [<FunctionName("hello")>] let run ([<HttpTrigger(AuthorizationLevel.Function, "post", "get", Route = null)>] req: HttpRequest) = OkObjectResult "Hello, World" :> ActionResult最後に以下のコマンドを実行することで、Azure Functions をローカル実行することができます。
powershellfunc start --build以下のエンドポイントにアクセスすることで実行することができます。
おまけのおまけ
余談ですが VSCode に Azure Functions の拡張機能を入れているとローカル関数プロジェクトを開いたときに以下のようなメッセージが表示されます。
これで Yes を選択してあげると、F5 でデバッグ実行が可能となります。
もちろんブレークポイントを設定してステップ実行をすることも可能です。
もし VSCode で Azure Functions の開発をする際には是非利用してみてください。
- 投稿日:2020-06-23T07:20:10+09:00
Visual Studio 2019 コードクリーンアップの動作をC#サンプル付きで紹介する
Visual Studio 2019にはコードクリーンアップという機能が付いています。これはあらかじめ設定しておいたルールに従って、ソースコードをまとめて綺麗にしてくれるというものです。
(従来からあった「フォーマット」機能とは異なり、変数宣言にvarを使うか使わないか、自分自身のメソッド呼び出しにthisを付ける/付けないなど、コードの内容部分まで含めて綺麗にしてくれます)これはとても便利な機能なのですが、惜しいことにどのルールが具体的にどんな処理をしてくれるのかが分かりにくいという欠点があります。
ルールの設定画面では処理の実例が表示されず、各ルールについての説明なども特にないためです。そこでこの記事では、各ルールがどのような処理を行ってくれるのかを、C#のコードサンプルと合わせて紹介します。
また、実際に行われるクリーンアップ処理の内容は、Visual Studioのコードスタイル設定や .editorconfig の設定によって変わります。そのため、「Visual Studioや .editorconfig のどの設定がクリーンアップ処理に影響するか」も併せて記載します。1. 'this.' 修飾の基本設定を適用します
自分自身が持つプロパティ、メソッド呼び出しの前に
this.
を付けるかどうかを統一します。クリーンアップ前public class Class1 { private string FirstName { get; set; } private string FamilyName { get; set; } public virtual string GetNameWithSuffix(string suffix) { return this.FirstName + suffix; } public override string ToString() { return this.GetNameWithSuffix("さん"); } }クリーンアップ後public class Class1 { private string FirstName { get; set; } private string FamilyName { get; set; } public virtual string GetNameWithSuffix(string suffix) { return FirstName + suffix; /* this. なしに統一 */ } public override string ToString() { return GetNameWithSuffix("さん"); /* this. なしに統一 */ } }Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 'this' の優先 > ○○ アクセスを 'this' で修飾してくださいeditorconfigで対応する設定:
dotnet_style_qualification_for_***
# this. と Me. の設定 dotnet_style_qualification_for_event = false:silent dotnet_style_qualification_for_field = false:silent dotnet_style_qualification_for_method = false:silent dotnet_style_qualification_for_property = false:silent参考ページ:
"This." 修飾子 と "Me." 修飾子 (EditorConfig での .NET の言語規則 - Visual Studio)2. using を並べ替える
usingを適切な順番に並べ替えます。
クリーンアップ前using System; using Newtonsoft.Json; using System.Linq; using System.Text; using Newtonsoft.Json.Converters; using System.IO; using Semver;クリーンアップ後using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Semver; using System; using System.IO; using System.Linq; using System.Text;なお、Visual Studio 2019の標準では上記のようにアルファベット順に並べ替えますが、設定を変更することでSystemを先頭に並び替えたり、グループごと(名前空間の第1階層ごと)に空行を空けて並べたりすることも可能になります。
using System; using System.IO; using System.Linq; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Semver;Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > 詳細 > ディレクティブを使用するeditorconfigで対応する設定:
dotnet_separate_import_directive_groups
,dotnet_sort_system_directives_first
# using の整理 dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = false file_header_template = unset参考ページ:
using ディレクティブの整理 (EditorConfig での .NET の書式規則 - Visual Studio)3. 不要な using の削除
ファイル内で使用していないusing句を削除します。
クリーンアップ前using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Semver; using System; using System.IO; using System.Linq; using System.Text; namespace CodeCleanupDemo { public class TestClass1 { public static void Main(string[] args) { Console.WriteLine("Hello World!"); File.WriteAllText("world.log", "Hello World!"); } } }クリーンアップ後using System; using System.IO; namespace CodeCleanupDemo { public class TestClass1 { public static void Main(string[] args) { Console.WriteLine("Hello World!"); File.WriteAllText("world.log", "Hello World!"); } } }Visual Studio 2019で対応する設定:
なしeditorconfigで対応する設定:
なし4. 暗黙的/明示的な型の基本設定を適用します
ローカル変数の宣言時に
var
を使うかどうかを統一します。クリーンアップ前var num1 = 100; var str1 = "foo";クリーンアップ後int num1 = 100; string str1 = "foo";Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 'var' を優先editorconfigで対応する設定:
csharp_style_var_***
# var を優先 csharp_style_var_elsewhere = false:silent csharp_style_var_for_built_in_types = false:silent csharp_style_var_when_type_is_apparent = false:silent参考ページ:
暗黙的な型と明示的な型 (EditorConfig での .NET の言語規則 - Visual Studio)5. 未使用の変数を削除する
使われていないローカル変数を削除します。
クリーンアップ前public static void Main(string[] args) { var num = 100; Console.WriteLine("Hello World!"); }クリーンアップ後public static void Main(string[] args) { Console.WriteLine("Hello World!"); }Visual Studio 2019で対応する設定:
なしeditorconfigで対応する設定:
なし参考ページ:
コンパイラの警告 (レベル 3) CS02196. 不要なキャストを削除する
不要と考えられる型キャストを削除します。
クリーンアップ前public static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) { var left = (int)baseRect.X; var scaleRateByWidth = (double)baseRect.Width / (double)requiredWidth; var scaleRateByHeight = (double)baseRect.Height / (double)requiredHeight;クリーンアップ後public static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) { var left = baseRect.X; // Rectangle.Xはもともとint型のため、int型へのキャストは不要 var scaleRateByWidth = baseRect.Width / (double)requiredWidth; // requiredWidthだけdouble型にキャストしても結果は同じ var scaleRateByHeight = baseRect.Height / (double)requiredHeight; // 同上Visual Studio 2019で対応する設定:
なしeditorconfigで対応する設定:
なし参考ページ:
不要なキャストを削除する (共通のクイックアクション - Visual Studio)7. インラインの 'out' 変数の基本設定を適用します
関数呼び出し時にout引数を指定していて、かつout引数で使う変数をそれよりも前に宣言している場合、C# 7.0から導入された書き方(out変数)に変更します。
クリーンアップ前public void TestMethod1() { /* C# 6.0 / Visual Studio 2015以前では、out引数に使うための変数を、事前に宣言しておく必要があった */ int a; TestMethod2(out a); var a3x = a * 3; } public void TestMethod2(out int outParam1) { outParam1 = 100; }クリーンアップ後public void TestMethod1() { /* C# 7.0 / Visual Studio 2017以降ではこのように1行で書ける */ TestMethod2(out int a); var a3x = a * 3; } public void TestMethod2(out int outParam1) { outParam1 = 100; }Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 変数の優先順位 > インライン変数宣言を優先するeditorconfigで対応する設定:
csharp_style_inlined_variable_declaration
# 式レベルの設定 csharp_style_inlined_variable_declaration = true:suggestion参考ページ:
インライン変数宣言 (EditorConfig での .NET の言語規則 - Visual Studio)8. アクセシビリティ修飾子を追加します
アクセシビリティ (public, protected, privateなど) の記載がされていないプロパティやメソッドについて、アクセシビリティを記載します。
クリーンアップ前double Rate { get; set; } // アクセシビリティは指定していないがprivate扱い static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) // 同上 { }クリーンアップ後private double Rate { get; set; } private static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) { }Visual Studio 2019で対応する設定:
なしeditorconfigで対応する設定:
なし9. アクセシビリティ修飾子を並べ替える
アクセシビリティ (public, protected, privateなど) の記載順を、標準の並び順に従って並び替えます。
また、static修飾子やvirtual修飾子などよりも前に(先頭に)来るようにします。クリーンアップ前static private void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) { } internal protected void Resize(Rectangle baseRect) { }クリーンアップ後private static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight) { } protected internal void Resize(Rectangle baseRect) { }Visual Studio 2019で対応する設定:
なしeditorconfigで対応する設定:
なし10. 可能な場合、privateフィールドを読み取り専用にする
初期化以外の箇所で代入を行っていないprivateフィールド(メンバ変数)があれば、それをreadonlyにします。
※readonlyになるのはフィールドだけで、プロパティは読み取り専用にならないことに注意してください。
クリーンアップ前public class TestClass1 { private string _world = "World"; public virtual void HelloWorld() { Console.WriteLine($"Hello, {_world}"); } }クリーンアップ後public class TestClass1 { /* 初期化以外で代入(再設定)を行っていないため、readonlyになる */ private readonly string _world = "World"; public virtual void HelloWorld() { Console.WriteLine($"Hello, {_world}"); } }Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 修飾子設定 > readonly フィールドを優先するeditorconfigで対応する設定:
dotnet_style_readonly_field
# フィールド設定 dotnet_style_readonly_field = true:suggestion参考ページ:
dotnet_style_readonly_field (EditorConfig での .NET の言語規則 - Visual Studio)11. 言語/フレームワークの型の基本設定を適用します。
標準の組み込み型名について、言語キーワード(int, long, stringなど)で記述するか、フレームワークの型名 (Int32, Int64, System.Stringなど) で記述するかを統一します。
クリーンアップ前Int32 num1 = 100; System.String str1 = "foo"; var num2 = Int64.MaxValue;クリーンアップ後int num1 = 100; string str1 = "foo"; var num2 = long.MaxValue;Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 定義済みの型の設定editorconfigで対応する設定:
dotnet_style_predefined_type_for_***
# 言語キーワードと BCL の種類の設定 dotnet_style_predefined_type_for_locals_parameters_members = true:silent dotnet_style_predefined_type_for_member_access = true:silent参考ページ:
型参照のためのフレームワーク型名の代わりの言語キーワード (EditorConfig での .NET の言語規則 - Visual Studio)12. 単一行のコントロール ステートメントに対する波かっこの追加/削除を行います
if文やusing文などの本体を1行だけで書けるような場合に、波かっこをつけて複数行で記述するか、1行で記述するかを統一します。
クリーンアップ前public virtual string GetNameWithSuffix(string name, string suffix) { if (name == null) return null;クリーンアップ後public virtual string GetNameWithSuffix(string name, string suffix) { if (name == null) { return null; }なお、この設定には「はい」「いいえ」の他に「複数行の場合」という設定があり、これを選択すると「本体が1行に収まるときは1行で書く、複数行にわたるときは波かっこをつける」という動作となります。
クリーンアップ前public virtual string GetNameWithSuffix(string name, string suffix) { if (name == null) return null; if (suffix != null) return string.Format("こんにちは、{0} {1}", name, suffix);クリーンアップ後public virtual string GetNameWithSuffix(string name, string suffix) { /* 本体が1行なのでそのまま */ if (name == null) return null; /* 本体が複数行なので波かっこが付く */ if (suffix != null) { return string.Format("こんにちは、{0} {1}", name, suffix); }Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > コード ブロックの優先順位 > 波かっこを優先しますeditorconfigで対応する設定:
csharp_prefer_braces
# コード ブロックの設定 csharp_prefer_braces = true:silent参考ページ:
コード ブロックの基本設定 (EditorConfig での .NET の言語規則 - Visual Studio)13. オブジェクト/コレクションの初期化の基本設定を適用します
オブジェクトやコレクションを初期化する時の書き方を統一します。
クリーンアップ前public class Human { public virtual string Name { get; set; } public virtual int Height { get; set; } public static Human Create() { var human1 = new Human(); human1.Name = "Taro"; human1.Height = 160 * 1000; var specials = new List<string>(); specials.Add("UltraThrow"); specials.Add("TarouBarriar"); var families = new Dictionary<string, string>(); families["mother"] = "Mother of Ultra"; families["brother"] = "Ace"; return human1; } }クリーンアップ後public class Human { public virtual string Name { get; set; } public virtual int Height { get; set; } public static Human Create() { /* オブジェクト初期化子を使った書き方に変更 */ var human1 = new Human { Name = "Taro", Height = 160 * 1000 }; /* コレクション初期化子を使った書き方に変更 */ var specials = new List<string> { "UltraThrow", "TarouBarriar" }; /* コレクション初期化子を使った書き方に変更 (C# 6.0 / Visual Studio 2015以降でのみ可能な書き方) */ var families = new Dictionary<string, string> { ["mother"] = "Mother of Ultra", ["brother"] = "Ace" }; return human1; } }Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 式の優先順位
オブジェクト初期化子を優先する
コレクション初期化子を優先するeditorconfigで対応する設定:
dotnet_style_collection_initializer
,dotnet_style_object_initializer
# 式レベルの設定 dotnet_style_collection_initializer = true:suggestion dotnet_style_object_initializer = true:suggestion参考ページ:
式レベルの基本設定 (EditorConfig での .NET の言語規則 - Visual Studio)14. 式/ブロック本体の基本設定を適用します
メソッド本体やプロパティなどの本体が1行に収まる場合に、従来の形式で記述するか、式形式で記述するかを統一します。
クリーンアップ前public class Class1 { private string FirstName { get; set; } private string FamilyName { get; set; } public virtual string Name { get { return string.Format("{0} {1}", FamilyName, FirstName); } } private string _nickname = null; public virtual string NickName { get { return (_nickname ?? Name); } set { _nickname = value; } } }クリーンアップ後public class Class1 { private string FirstName { get; set; } private string FamilyName { get; set; } /* Nameプロパティは式本体を1行で書けるため、式形式に変換される (C# 6.0 / Visual Studio 2015以降でのみ可能な書き方) */ public virtual string Name => string.Format("{0} {1}", FamilyName, FirstName); private string _nickname = null; public virtual string NickName { /* get, setの両方を持つプロパティにも対応可能 (C# 7.0 / Visual Studio 2017以降でのみ可能な書き方) */ get => (_nickname ?? Name); set => _nickname = value; } }設定によってはコンストラクタ、ローカル関数などプロパティ以外の対象にも適用可能です。
ただし、項目によっては対応するC#のバージョンが異なることに注意してください。
(全項目を使用可能になるのはC# 7.0以降(Visual Studio 2017以降))Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 式の優先順位 > ○○に式本体を使用するeditorconfigで対応する設定:
csharp_style_expression_bodied_***
# 式のようなメンバー csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:silent参考ページ:
式形式のメンバー (EditorConfig での .NET の言語規則 - Visual Studio)補足:ルール選択にかかわらず常に実行される処理
下記の処理は、どのようなルールを選択したかにかかわらず、クリーンアップ時は常に実行されます。
- コードのフォーマット(メニューの 編集 > 詳細 > ドキュメントのフォーマット と同じ)
補足:そのほか、知っておくと便利な知識
コードクリーンアップのための設定内容は、Visual Studioの「設定から .editorconfig を生成」機能を使うことで、ほかの人と共有することができます。
(メニューの オプション > テキスト エディター > C# > コード スタイル から実行可能です)ただし、残念ながら「どのクリーンアップルールを選択したか」の情報は共有できないため、これだけは別途共有する必要があります。
(例:「不要な using の削除」をオンにしているかオフにしているかの情報は .editorconfig には含まれないため、各開発者がコードクリーンアップの設定ダイアログからオン/オフを切り替えなくてはなりません)Microsoftの公式ドキュメントでも紹介されている拡張機能「Code Cleanup On Save」を使うと、ファイルを保存するたびに自動でクリーンアップが実行されるようにできます。
- 投稿日:2020-06-23T00:49:32+09:00
[C#]ValidationAttributeを継承して2つの項目の大小関係を検証する
これを読んでできること
- 2つの入力項目を検証し、定義した大小関係を満たしているか検証できる
- カスタムバリデーションを作れるようになる
ValidationAttributeとは
すべての検証属性の基本クラスとして機能します。
ValidationAttribute クラス - docs.microsoft.comMSのドキュメントを引用しましたが、これだけではよくわかりませんね。
ValidationAttribute
とはフォームからの入力値やモデルオブジェクトのプロパティが正しいかどうかの検証を強制し、また正しさを定義する属性の基底クラスです。
カスタム検証属性を定義する場合はこれを継承していきます。参考によく使われる検証属性を以下に記載します。
属性名 機能 Required 必須項目。nullや未入力の場合エラーとします StringLength(int) 最大文字長。指定した文字数を超えるとエラーとします EmailAddress メールアドレス形式の項目。メールアドレスの形式を満たさない場合エラーとします
System.ComponentModel.DataAnnotations
には標準で複数の検証属性が実装されています。しかし、開始時刻と終了時刻のように他のプロパティに依存する検証属性はありません1。そのためカスタム検証属性を作る必要があります。カスタム検証属性を作ってみた
結果として、検証属性を4種類作りました。
どれもほぼ内容が同じなので「AよりBが大きい」を検証するGreaterThanAttribute
2のソースコードを添付します。GreaterThanAttribute.cs[AttributeUsage(AttributeTargets.Property)] public class GreaterThanAttribute : ValidationAttribute { public string OtherProperty { get; private set; } public string OtherPropertyDisplayName { get; internal set; } public GreaterThanAttribute(string otherProperty) { OtherProperty = otherProperty; ErrorMessage = "{0}は{1}より大きい値を指定してください。"; } public override string FormatErrorMessage(string name) { // エラーメッセージを返す return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, OtherPropertyDisplayName ?? OtherProperty); } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 比較対象のPropertyInfo PropertyInfo propertyInfo = validationContext.ObjectType.GetProperty(OtherProperty); // 比較対象のプロパティの値 object propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null); Type type = propertyInfo.PropertyType; if (type == typeof(DateTime)) { // ここで値の比較。条件を満たしていれば検証成功を返す if ((DateTime)value > (DateTime)propertyValue) { return ValidationResult.Success; } } else if (type == typeof(int)) { if ((int)value > (int)propertyValue) { return ValidationResult.Success; } } // ...other type if (OtherPropertyDisplayName == null) { OtherPropertyDisplayName = GetDisplayNameForProperty(validationContext.ObjectType, OtherProperty); } return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } // 比較対象のプロパティ名を取得する。ここはオマケなので削っても問題ない private static string GetDisplayNameForProperty(Type containerType, string propertyName) { ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType); PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true); if (property == null) { throw new ArgumentException(); } IEnumerable<Attribute> attributes = property.Attributes.Cast<Attribute>(); DisplayAttribute display = attributes.OfType<DisplayAttribute>().FirstOrDefault(); if (display != null) { // DisplayAttributeがついてたらその名称を返す return display.GetName(); } DisplayNameAttribute displayName = attributes.OfType<DisplayNameAttribute>().FirstOrDefault(); if (displayName != null) { // DisplayNameAttributeがついてたらその名称を返す return displayName.DisplayName; } return propertyName; } private static ICustomTypeDescriptor GetTypeDescriptor(Type type) { return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); } }カスタム検証属性を使用するモデル側は以下の通りです。
Model.cs// 開始時刻 // LessThanAttributeのコードは割愛 [LessThan("EndDateTime")] public DateTime StartDateTime { get; set; } // 終了時刻 [GreaterThan("StartDateTime")] public DateTime EndDateTime { get; set; }使い方
開始時刻
<終了時刻
であることを検証したい場合、StartDateTime
に[LessThan]
属性、EndDateTime
に[GreaterThan]
属性を付けます。引数には比較対象のプロパティをstring
で指定します。Compare
属性と似ていますね。属性をつけることでASP.NET MVCのモデル検証やBlazorのフォーム検証時に大小関係が正しいか検証されます。大小関係を満たさない場合、「StartDateTimeはEndDateTimeより小さい値を指定してください。」や「EndDateTimeはStartDateTimeより大きい値を指定してください。」がエラーメッセージとして表示されます。
おわりに
もっと良い書き方があるような気もしますが、一旦ここで切り上げます。型の分岐とかもっとイケてる書き方3ができそうですね。元気があるときに書き直したいと思います。
参考
- 投稿日:2020-06-23T00:30:49+09:00
# NPocoの紹介 -機能編- ページング
NPocoの紹介 -機能編- ページング
最初の記事はこちら
今回はページング機能について紹介します。
(「全115件中11~20件目を表示」とかの機能を実現するためのものです。)公式ドキュメントだとこちらに記載されています。
データベースはPostgreSQLを使用しました。
テーブルの用意
emp
前回と同じものを流用
自動クエリ の場合
生SQLを使わない自動生成クエリの場合、終端操作で
ToPage
メソッドを呼ぶことでページングを実現できます。Page<Emp> page = database.Query<Emp>() .Where(x => x.DeptCode == "001") .OrderBy(x => x.EmpId) .ToPage(2, 10);以下のクエリが実行されます。
SELECT COUNT(*) FROM "emp" "E" WHERE ("E"."dept_code" = @p0) -> @p0 [String] = "001" SELECT "E"."emp_id" as "EmpId", "E"."first_name" as "FirstName", "E"."family_name" as "FamilyName", "E"."dept_code" as "DeptCode", "E"."created_at" as "CreatedAt", "E"."updated_at" as "UpdatedAt" FROM "emp" "E" WHERE ("E"."dept_code" = @p0) ORDER BY "EmpId" ASC LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"全件数取得のクエリと、現在のページ部分のデータを取得するクエリの2つが実行されます。
ToPage
の第1引数はページ番号(1スタート, 0じゃないよ)
第2引数は1ページあたりの件数です。上記の引数だと「1ページ当たり10件で2ページ目を取得」なので
LIMIT 10 OFFSET 10
というクエリが生成されています。また
OrderBy
で常に同じ並び順になるように注意してください。
Page<>
についてソースはこちら
以下のプロパティが定義されています。
CurrentPage
現在のページ(メソッドの第1引数と同じ)TotalPages
総ページ数TotalItems
総件数ItemsPerPage
1ページ当たりの件数(メソッドの第2引数と同じ)Items
現在のページのレコードLimit メソッドについて
Limit
メソッドを使っても 似たようなことが可能です。List<Emp> list = database.Query<Emp>() .Where(x => x.DeptCode == "001") .OrderBy(x => x.EmpId) .Limit(10, 10) .ToList();以下のクエリが実行されます。
SELECT "E"."emp_id" as "EmpId", "E"."first_name" as "FirstName", "E"."family_name" as "FamilyName", "E"."dept_code" as "DeptCode", "E"."created_at" as "CreatedAt", "E"."updated_at" as "UpdatedAt" FROM "emp" "E" WHERE ("E"."dept_code" = @p0) ORDER BY "EmpId" ASC LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"ただしこちらは、第1引数はスキップするレコード数となっており、戻り値も
List<>
となっています。生SQLを記述する場合
生SQLを使う場合
Page
メソッドを呼ぶことでページングを実現できます。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; Page<Emp> page = database.Page<Emp>(2, 10, sql, param);以下のクエリが実行されます。
SELECT COUNT(*) FROM (SELECT emp_id FROM emp WHERE dept_code = @p0 ) npoco_tbl -> @p0 [String] = "001" SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"クエリ自動生成の場合と同じく全件数取得のクエリと、現在のページ部分のデータを取得するクエリの2つが実行されます。
また同様に
Page
の第1引数はページ番号、第2引数は1ページあたりの件数です。このページングについては
- Database.BuildPageQueries
- PagingHelper
- DatabaseType.BuildPageQuery (データベース別のオーバーライドあり)によって実現されています。
クエリには必ず
ORDER BY
の記述を忘れないようにしてください。Fetch メソッドについて
実は
Fetch
メソッドもオーバーライドされており、ページングのクエリを発行できます。
ただしこちらの戻り値はList<>
であり、件数取得は行われません。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; List<Emp> list = database.Fetch<Emp>(2, 10, sql, param);以下のクエリが実行されます。
SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"
Page
と同じく第1引数はページ番号です。SkipTake メソッドについて
SkipTake
メソッドでもFetch
メソッドと似たようなことは可能です。
ただしこちらの第1引数は「スキップする件数」です。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; List<Emp> list = database.SkipTake<Emp>(10, 10, sql, param);以下のクエリが実行されます。
SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"注意点
非常に便利なページングの機能ですが注意点があります。
生SQLを使う場合
with
句を使うことができません。
(クエリ加工を正規表現で行っており、対応していない)なので
with
を使わないクエリを書くか、with
を使う場合は自前のページングをする、ビューを作成する などの対応が必要となります。
- 投稿日:2020-06-23T00:30:49+09:00
NPocoの紹介 -機能編- ページング
NPocoの紹介 -機能編- ページング
最初の記事はこちら
今回はページング機能について紹介します。
(「全115件中11~20件目を表示」とかの機能を実現するためのものです。)公式ドキュメントだとこちらに記載されています。
データベースはPostgreSQLを使用しました。
テーブルの用意
emp
前回と同じものを流用
自動クエリ の場合
生SQLを使わない自動生成クエリの場合、終端操作で
ToPage
メソッドを呼ぶことでページングを実現できます。Page<Emp> page = database.Query<Emp>() .Where(x => x.DeptCode == "001") .OrderBy(x => x.EmpId) .ToPage(2, 10);以下のクエリが実行されます。
SELECT COUNT(*) FROM "emp" "E" WHERE ("E"."dept_code" = @p0) -> @p0 [String] = "001" SELECT "E"."emp_id" as "EmpId", "E"."first_name" as "FirstName", "E"."family_name" as "FamilyName", "E"."dept_code" as "DeptCode", "E"."created_at" as "CreatedAt", "E"."updated_at" as "UpdatedAt" FROM "emp" "E" WHERE ("E"."dept_code" = @p0) ORDER BY "EmpId" ASC LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"全件数取得のクエリと、現在のページ部分のデータを取得するクエリの2つが実行されます。
ToPage
の第1引数はページ番号(1スタート, 0じゃないよ)
第2引数は1ページあたりの件数です。上記の引数だと「1ページ当たり10件で2ページ目を取得」なので
LIMIT 10 OFFSET 10
というクエリが生成されています。また
OrderBy
で常に同じ並び順になるように注意してください。
Page<>
についてソースはこちら
以下のプロパティが定義されています。
CurrentPage
現在のページ(メソッドの第1引数と同じ)TotalPages
総ページ数TotalItems
総件数ItemsPerPage
1ページ当たりの件数(メソッドの第2引数と同じ)Items
現在のページのレコードLimit メソッドについて
Limit
メソッドを使っても 似たようなことが可能です。List<Emp> list = database.Query<Emp>() .Where(x => x.DeptCode == "001") .OrderBy(x => x.EmpId) .Limit(10, 10) .ToList();以下のクエリが実行されます。
SELECT "E"."emp_id" as "EmpId", "E"."first_name" as "FirstName", "E"."family_name" as "FamilyName", "E"."dept_code" as "DeptCode", "E"."created_at" as "CreatedAt", "E"."updated_at" as "UpdatedAt" FROM "emp" "E" WHERE ("E"."dept_code" = @p0) ORDER BY "EmpId" ASC LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"ただしこちらは、第1引数はスキップするレコード数となっており、戻り値も
List<>
となっています。生SQLを記述する場合
生SQLを使う場合
Page
メソッドを呼ぶことでページングを実現できます。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; Page<Emp> page = database.Page<Emp>(2, 10, sql, param);以下のクエリが実行されます。
SELECT COUNT(*) FROM (SELECT emp_id FROM emp WHERE dept_code = @p0 ) npoco_tbl -> @p0 [String] = "001" SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"クエリ自動生成の場合と同じく全件数取得のクエリと、現在のページ部分のデータを取得するクエリの2つが実行されます。
また同様に
Page
の第1引数はページ番号、第2引数は1ページあたりの件数です。このページングについては
- Database.BuildPageQueries
- PagingHelper
- DatabaseType.BuildPageQuery (データベース別のオーバーライドあり)によって実現されています。
クエリには必ず
ORDER BY
の記述を忘れないようにしてください。Fetch メソッドについて
実は
Fetch
メソッドもオーバーライドされており、ページングのクエリを発行できます。
ただしこちらの戻り値はList<>
であり、件数取得は行われません。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; List<Emp> list = database.Fetch<Emp>(2, 10, sql, param);以下のクエリが実行されます。
SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"
Page
と同じく第1引数はページ番号です。SkipTake メソッドについて
SkipTake
メソッドでもFetch
メソッドと似たようなことは可能です。
ただしこちらの第1引数は「スキップする件数」です。string sql = @" SELECT emp_id FROM emp WHERE dept_code = @deptCode ORDER BY emp_id "; var param = new { deptCode = "001" }; List<Emp> list = database.SkipTake<Emp>(10, 10, sql, param);以下のクエリが実行されます。
SELECT emp_id FROM emp WHERE dept_code = @p0 ORDER BY emp_id LIMIT @p1 OFFSET @p2 -> @p0 [String] = "001" -> @p1 [Int64] = "10" -> @p2 [Int64] = "10"注意点
非常に便利なページングの機能ですが注意点があります。
生SQLを使う場合
with
句を使うことができません。
(クエリ加工を正規表現で行っており、対応していない)なので
with
を使わないクエリを書くか、with
を使う場合は自前のページングをする、ビューを作成する などの対応が必要となります。