20200531のC#に関する記事は13件です。

RaspberryPi センサーサンプルプロジェクト URLまとめ

RaspberryPi センサーサンプルプロジェクト URLまとめ

センサーのサンプルプロジェクトファイルをWeb上で配布している会社のまとめです。
基本どの会社のセットもCDの中にサンプルを収録しセットに付いてきます。

OSOYOO

OSOYOO(オソヨー) Raspberry Pi 学ぶ電子工作キット 初心者演習用パーツセット ラズパイ11実例 回路配線図とサンプルスケッチ有り プログラミング ラズベリー パイ 超入門 スターター学習キット LED制御 スイッチ I2C LCD 温湿度センサー マイクロサーボ 人体感知センサー A/Dコンバータ ブザー 大気圧センサー リレーモジュール制御 赤外線リモコン等IoTを実践する電子部品セット (Pi 3 DIY Kit 22in1)
スクリーンショット 2020-03-29 12.52.41.png
Project File

ELEGOO

ELEGOO Arduino用のMega2560スタータキット最終版 初心者向け、チュートリアル付、MEGA 2560ボード, LCD1602
スクリーンショット 2020-03-29 12.52.41.png
Project File

Kuman

Kuman 44個キット センサー キット Raspberry Pi用 センサーモジュール スターター キット iot学習キット 電子工作 Raspberry Pi 4 B 3 2 Model B B+ A A+に適用 ラズベリーパイ K47
スクリーンショット 2020-03-29 12.52.41.png
Project File

sunfounder

SunFounder Raspberry pi スターター電子工作キット,ラズベリーパイ初心者向けプログラミング,詳細な教本と豊富な学習用レッスン付き,Raspberry pi 4B/3B+/3B/3A+/2B/1B+/1A+/Zero W/Zeroに対応、C/Pythonコードをサポート
スクリーンショット 2020-03-29 12.52.41.png
Project File

KeeYees

KeeYees 電子工作キット 初心者向け スターターキット 電子部品 基本部品56種類 エレクトロニクス入門キット Electronics Fun Kit Arduino用 Raspberry Pi用 チュートリアルあり
スクリーンショット 2020-03-29 12.52.41.png
Project File

Freenove

Freenove Raspberry Pi 4 B 3 BのためのRFIDスターターキット +、423ページ詳細ガイド、Python C Java、204アイテム、53プロジェクト、エレクトロニクスとプログラミングを学ぶ、ソルダーレスブレッドボード
スクリーンショット 2020-03-29 12.52.41.png
Project File

KEYESTUDIO

KEYESTUDIO ラズベリーパイ 拡張ボード スターターキット for Raspberry Pi 3 4 4B 電子工作 電子部品 キット 実験キット LED, 超音波 センサー, RFID モジュール,ブレッドボード セット
スクリーンショット 2020-03-29 12.52.41.png
Project File

WayinTop

WayinTop Raspberry Pi用センサーキット 電子工作キット ラズベリー パイ 超入門 初心者向け 中級者向け 学習キット 電子部品セット プログラミング JAVA C++ Python 日本語チュートリアル
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

UNIROI

UNIROI arduino/Raspberry Pi用 電子キット 40種セット 初心者 R3 PNP S8550+電解コンデンサ+電源モジュール 電子工作 キット ブレッドボード mega 3 2 model B A A+ +に互換 UA001
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

Miuzei

Miuzei 初心者スターターキット mega2560 r3 nanoと互換できる(日本語マニュアル付き)
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RaspberryPi センサーサンプルファイル配布 URLまとめ

RaspberryPi センサーサンプルファイル配布 URLまとめ

センサーをまとめ売りしている業者はサンプルファイルをCDに収録しセット売りしているのですが、Web上にもファイルを公開している会社をまとめてみました。

OSOYOO

OSOYOO(オソヨー) Raspberry Pi 学ぶ電子工作キット 初心者演習用パーツセット ラズパイ11実例 回路配線図とサンプルスケッチ有り プログラミング ラズベリー パイ 超入門 スターター学習キット LED制御 スイッチ I2C LCD 温湿度センサー マイクロサーボ 人体感知センサー A/Dコンバータ ブザー 大気圧センサー リレーモジュール制御 赤外線リモコン等IoTを実践する電子部品セット (Pi 3 DIY Kit 22in1)
スクリーンショット 2020-03-29 12.52.41.png
Project File

ELEGOO

ELEGOO Arduino用のMega2560スタータキット最終版 初心者向け、チュートリアル付、MEGA 2560ボード, LCD1602
スクリーンショット 2020-03-29 12.52.41.png
Project File

Kuman

Kuman 44個キット センサー キット Raspberry Pi用 センサーモジュール スターター キット iot学習キット 電子工作 Raspberry Pi 4 B 3 2 Model B B+ A A+に適用 ラズベリーパイ K47
スクリーンショット 2020-03-29 12.52.41.png
Project File

sunfounder

SunFounder Raspberry pi スターター電子工作キット,ラズベリーパイ初心者向けプログラミング,詳細な教本と豊富な学習用レッスン付き,Raspberry pi 4B/3B+/3B/3A+/2B/1B+/1A+/Zero W/Zeroに対応、C/Pythonコードをサポート
スクリーンショット 2020-03-29 12.52.41.png
Project File

KeeYees

KeeYees 電子工作キット 初心者向け スターターキット 電子部品 基本部品56種類 エレクトロニクス入門キット Electronics Fun Kit Arduino用 Raspberry Pi用 チュートリアルあり
スクリーンショット 2020-03-29 12.52.41.png
Project File

Freenove

Freenove Raspberry Pi 4 B 3 BのためのRFIDスターターキット +、423ページ詳細ガイド、Python C Java、204アイテム、53プロジェクト、エレクトロニクスとプログラミングを学ぶ、ソルダーレスブレッドボード
スクリーンショット 2020-03-29 12.52.41.png
Project File

KEYESTUDIO

KEYESTUDIO ラズベリーパイ 拡張ボード スターターキット for Raspberry Pi 3 4 4B 電子工作 電子部品 キット 実験キット LED, 超音波 センサー, RFID モジュール,ブレッドボード セット
スクリーンショット 2020-03-29 12.52.41.png
Project File

WayinTop

WayinTop Raspberry Pi用センサーキット 電子工作キット ラズベリー パイ 超入門 初心者向け 中級者向け 学習キット 電子部品セット プログラミング JAVA C++ Python 日本語チュートリアル
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

UNIROI

UNIROI arduino/Raspberry Pi用 電子キット 40種セット 初心者 R3 PNP S8550+電解コンデンサ+電源モジュール 電子工作 キット ブレッドボード mega 3 2 model B A A+ +に互換 UA001
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

Miuzei

Miuzei 初心者スターターキット mega2560 r3 nanoと互換できる(日本語マニュアル付き)
スクリーンショット 2020-03-29 12.52.41.png
Web上未確認 CD教材

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# 変数と宣言

C# 変数

変数とは?
数値や文字などを格納しておく箱のようなもの。
(それに名前を付けた物)

宣言の書き方

宣言方法
2つの変数の宣言 int a; int b; int a,b;
一つの変数の宣言と代入 int a; a = 2; int a= 2;
二つの変数の宣言と代入 int a;int b; a = 1; b = 2;

変数の宣言における命名規則について

(Microsoftからの引用になります!)
フィールド名には Pascal 形式を使用してください。
フィールドの名前には、名詞や名詞句を使用してください。
フィールド名にはプリフィックスを使用しないでください。
たとえば、静的フィールドと非静的フィールドを区別するために、g_ や s_ を使用しないでください

※Pascal形式とは!
・それぞれの単語の頭は大文字
・単語の間に「-(ハイフン)」や「_(アンダースコア)」は使わない

変数の型
・値型と参照型がある
値型
 ・単純型
 ・列挙型
 ・構造体の型
 ・null許容値型
 ・ダブル値型
参照型
 ・クラス型
 ・インターフェイス型
 ・配列型
 ・デリゲート型

※変数の型は別途記載したいと思う。
(各型について調べたが結構量が多い為、細かく分類します)

参考資料
・Microsoft Build
・Wikipedia
・C#の絵本

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてC#を学習する方へのおすすめサイト

数あるプログラミング言語。
初めての学習でC#を選んだけど、どうやって進めていくか迷っている方へおすすめのサイトをまとめました。

初めてならば動画やシステムで学べるサイトが一番

私は初めての言語はC言語でした。
書籍を購入して、頭から順番に読み進めながら問題に挑戦するという、王道ともいうべき進め方でした。

昨今ではWebサイトの充実により、書籍でなくても入門しやすくなっています。
HTML/CSS/JavaScript/Java/Goはすべてサイト学習しました。

そんなサイト学習での定番サイトと言えば

が有名です。(Web関連の言語が多いです。)

C#はというと

となっています。
初めての言語学習で、いきなり有料会員登録というのも抵抗があるかと思います。

そこで、C#の動画学習サイトとしては、paizaラーニングがおすすめです。

C#の初期学習はpaizaラーニングがおすすめ

paizaラーニングドットインストール/Progateのように一部有料会員向けで構成される動画学習サイトですが、
C#は(2020/05/31現在)完全無料で公開されています。

内容としては

  • C#入門編1: プログラミングを学ぶ
  • C#入門編2: 条件によって処理を変えてみよう
  • C#入門編3: ループ処理を学ぶ
  • C#入門編4: 配列の基礎
  • C#入門編5: 多次元配列を理解しよう
  • C#入門編6: メソッドを理解しよう
  • C#入門編7: クラスを理解しよう
  • C#入門編8: さらにクラスを理解しよう
  • C#入門編9: Dictionaryの基礎
  • C#入門編10: 例外処理を理解しよう

と、全10レッスンで構成され、各章6~11チャプターで構成されています。
全くプログラミングしたことない方でも入門できる難易度で構成されているのでおすすめです。

C#以外で言語学習したことがある方は各レッスン/チャプターの演習問題だけトライすることで
誤答だったり、曖昧だった箇所のみ解説を聞くことでスムーズに学習できると思います。

またpaizaラーニングではブラウザ上でコーディングできるので開発環境はいりません。
※PC上で環境を作りたい方は以下を参考にしてみてください。
Windows10+VSCode+.NET CoreでC#開発環境を作ろう!

知識がついたら練習あるのみ

一通り知識がついたら、実際にプログラミングすることが上達にはかかせません。
その為には問題・回答がスムーズにできるサイトを利用するのがおすすめです。

そのなかでもおすすめなのはプログラミングコンテストで有名なAtCoderの練習サイトであるAtCoder Problemsです。

数多くの問題を、こちらもサイト上でプログラミング・提出することができ、
正解・不正解をその場で確認できます。

はじめはA問題:灰グレード、B問題:灰グレードを選んで挑戦すると良いでしょう。
基本である入力・出力を中心にアルゴリズムも学ぶことができます。

AtCoderはC++における解説ページが多いですが、人気なため学習のたすけとなるサイトも多いので、詰まった時にも助けになるケースは多いです。

まとめ

今回は初学者向けのサイトを紹介しました。
C#はWindowsアプリケーションだけでなくMac/LinuxやCloudなどのバックエンドでも利用が増えている言語です。
是非、学習してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# PEファイルの文字列リソースをすべて取得する

言語と動作確認環境

  • C# 8、.NET Core 3.1 (Preview)
  • Visual Studio Community 2019 Preview(Version 16.7.0 Preview 1.0)、Windows 10。

目的

C# 8と.NET Core 3.1でPEファイルの文字列リソースをすべて取得するサンプルコードです。同様のコードはQiitaや海外サイトでも公開されていますが、自身の学習のために作成しています。

サンプルコード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            using var handle = NativeMethods.LoadLibraryExW(
                "user32.dll",
                IntPtr.Zero,
                NativeMethods.DONT_RESOLVE_DLL_REFERENCES
                | NativeMethods.LOAD_LIBRARY_AS_DATAFILE
                | NativeMethods.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
            if (handle.IsInvalid)
            {
                throw new Win32Exception();
            }

            var strings = GetStringResources(handle.DangerousGetHandle());
        }

        /// <summary>
        /// ある型のリソースのIDをすべて取得します。
        /// </summary>
        private static ushort[] GetResourceIDs(IntPtr moduleHandle, IntPtr resourceType)
        {
            var resnames = new List<ushort>();
            NativeMethods.EnumResourceNamesW(
                moduleHandle,
                resourceType,
                (IntPtr hModule, IntPtr lpszType, IntPtr lpszName, nint lParam) =>
                {
                    var id = lpszName.ToInt64();
                    if (id >> 16 != 0)
                        throw new Exception();
                    resnames.Add((ushort)id);
                    return true;
                },
                0);
            return resnames.ToArray();
        }

        /// <summary>
        /// モジュールの文字列リソースをすべて取得します。
        /// </summary>
        private static string[] GetStringResources(IntPtr moduleHandle)
        {
            var stringResIds = GetResourceIDs(moduleHandle, NativeMethods.RT_STRING);
            Array.Sort(stringResIds);
            var strings = new List<string>();
            foreach (var strResId in stringResIds)
            {
                var resHandle = NativeMethods.FindResourceW(moduleHandle,
                    new IntPtr(strResId), NativeMethods.RT_STRING);
                var memoryHandle = NativeMethods.LoadResource(moduleHandle, resHandle);
                var size = NativeMethods.SizeofResource(moduleHandle, resHandle);
                // pointerの中身は2バイト(文字数N)+N*2バイト(UTF-16文字列)の配列
                var pointer = NativeMethods.LockResource(memoryHandle);
                for (int offset = 0; offset < size;)
                {
                    uint len = (ushort)Marshal.ReadInt16(pointer + offset);
                    strings.Add(Marshal.PtrToStringUni(pointer + offset + sizeof(ushort), (int)len));
                    offset += sizeof(ushort) + (int)len * 2;
                }
            }
            return strings.ToArray();
        }

        private static class NativeMethods
        {
            [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
            public static extern SafeModuleHandle LoadLibraryExW(
                [In] string lpLibFileName,
                IntPtr hFile,
                uint dwFlags);

            [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EnumResourceNamesW(
                IntPtr hModule,
                IntPtr lpType,
                EnumResNameProcW lpEnumFunc,
                nint lParam);

            public const uint DONT_RESOLVE_DLL_REFERENCES = 0x00000001;
            public const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
            public const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;

            [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public delegate bool EnumResNameProcW(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, nint lParam);

            public static readonly IntPtr RT_STRING = new IntPtr(6);

            [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindResourceW(
                IntPtr hModule,
                IntPtr lpName,
                IntPtr lpType);

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr LoadResource(
                IntPtr hModule,
                IntPtr hResInfo);

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr LockResource(
                IntPtr hResData);

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern uint SizeofResource(
                IntPtr hModule,
                IntPtr hResInfo);
        }
    }

    public sealed class SafeModuleHandle : SafeHandle
    {
        private static class NativeMethods
        {
            [DllImport("kernel32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool FreeLibrary(IntPtr hLibModule);
        }

        public SafeModuleHandle()
            : base(IntPtr.Zero, true)
        {
        }

        public SafeModuleHandle(IntPtr handle, bool ownsHandle)
            : base(handle, ownsHandle)
        {
        }

        public override bool IsInvalid => handle == IntPtr.Zero;

        protected override bool ReleaseHandle()
        {
            return NativeMethods.FreeLibrary(handle);
        }
    }
}

HMODULE型のSafeHandleによるラップ

LoadLibraryEx関数の戻り値はHMODULE型のハンドルであり、使用後はFreeLibrary関数で解放する必要があります。C#ではusing構文とSafeHandleの派生クラスにより確実な解放を保証できるため、ここではSafeHandleを継承したクラスを作成しています。

このハンドルを解放しなかった場合、プロセスの終了までライブラリがメモリ存在したままとなります。なお、同じHMODULE型を返す関数でもGetModuleHandle関数の戻り値は基本的に解放してはいけません。

LoadLibraryEx関数とフラグ

LoadLobraryEx関数の呼び出し時にはいくつかのフラグを指定することができます。今回は既定の場所からPEファイルを読み込み、そのリソースのみが必要なので以下のフラグを指定しています。

フラグ 目的
DONT_RESOLVE_DLL_REFERENCES DLL参照の解決を無効化する。
LOAD_LIBRARY_AS_DATAFILE データファイルとして読み込む。
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 既定の場所から検索する。

LoadResource関数とLockResource関数

LoadResource関数とLockResource関数は後方互換性のために別々に存在します。Windows 10ではどちらも同じ値を返します。戻り値はプロセスの終了時に自動的に解放されます(Microsoft Docs)。

ポインタから整数や文字列の読み込み

System.Runtime.InteropServices名前空間のMarshalクラスを使用してポインタから整数や文字列を読み込むことができます。16ビット整数はMarshal.ReadInt16、UTF-16文字列の読み込みはMarshal.PtrToStringUniです。

RT_STRING型リソースの中身

RT_STRING型リソースは各IDに長さ(UInt16、2バイト)とその長さのUTF-16文字列(Char型)のペアが1~32個含まれます。各IDに含まれる文字列の個数は記録されていませんが、SizeofResource関数で取得したバイト数まで上記ペアを読み込むことですべての文字列を取得できます。

RT_STRING型リソースの模式図
#ID 1 <- バイト数はSizeofResource関数で取得
Length(2バイト), String(Length文字 = Length*2バイト)
...
Length(2バイト), String(Length文字 = Length*2バイト) <- 最大32個

#ID 2
Length(2バイト), String(Length文字 = Length*2バイト)
...
Length(2バイト), String(Length文字 = Length*2バイト)

...

#ID N <- EnumResourceNamesで見つかった個数だけ存在
...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DICE#プログラム

 この記事はC#のVisualStudio2019を使用しています。
 実行の際には特に設定は必要ないです。
 入力の必要はありません。

 このプログラムは1~4桁までのサイコロで合わせた数値で割り算を行い、割った出力した数値とその余りを求めるものになっています。

以下はそのソースコードです。

using System;

namespace Dice++++
{
class Program
{
static void Main(string[] args)
{
//1つ目のサイコロは一桁 0~9まで
var random = new Random();
var number = random.Next(0,10);
//2つ目のサイコロの二桁 0~9まで
var number2 = random.Next(0,10)*10;
//3つ目のサイコロの二桁 0~9まで
var number3 = random.Next(0, 10) * 100;
//4つ目のサイコロの二桁 1~9まで
var number4 = random.Next(1, 10) * 1000;

        //一桁、二桁、三桁と四桁のサイコロの数の合計
        var number5 = number + number2 + number3 + number4 ;

        Console.WriteLine("1桁のサイコロの数は" + number +"、");
        Console.WriteLine("2桁のサイコロの数は" + number2 + "、");
        Console.WriteLine("3桁のサイコロの数は" + number3 + "、");
        Console.WriteLine("4桁のサイコロの数は" + number4 + "、");
        Console.WriteLine("4つのサイコロを足した数は" + number5 + "です");

        double x = random.Next(1, 10) * 100;
        var X = number5 / x;
        double X1 = number5 % x;

        Console.WriteLine("合計を3桁の数字である"+x+"で割ると" + X + "になり、余りは"+ X1 +"になります");
    }
}

}

 出力例:
  1回目
1桁のサイコロの数は8、
2桁のサイコロの数は20、
3桁のサイコロの数は200、
4桁のサイコロの数は6000、
4つのサイコロを足した数は6228です
合計を3桁の数字である600で割ると10.38になり、余りは228になります

  2回目
1桁のサイコロの数は2、
2桁のサイコロの数は60、
3桁のサイコロの数は700、
4桁のサイコロの数は1000、
4つのサイコロを足した数は1762です
合計を3桁の数字である900で割ると1.9577777777777778になり、余りは862になります

 以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ASP.NET Core Web APIでソースコードからSwaggerUIを起動させる。ついでにインターフェース定義をJSON/YAMLで出力する。その定義ファイルからClientコードを自動生成する。

TL;DR

以下の手順で作られるコードを以下に置いておきます。お忙しい方はこちらからどうぞ :angel_tone1:
https://github.com/ishiyama0530/worksample-dotnetcore-swagger-axios

モチベーション

  • ソースコードからOpenAPIのインターフェースを自動生成したい
  • SwaggerUIを表示したい
  • OpenAPIの定義ファイルをJSON/YAML形式で出力したい
  • 定義ファイルからAxiosのコードを生成したい
  • すべてを1コマンドで行いたい

参考

プロジェクト構成

最終的にはこんな感じになります。

$ tree ./worksample-dotnetcore-swagger-axios -a -L 1
./worksample-dotnetcore-swagger-axios
├── .config
├── .git
├── .gitignore
├── ClientApp
├── Controllers
├── Pages
├── Program.cs
├── Properties
├── Startup.cs
├── WeatherForecast.cs
├── appsettings.Development.json
├── appsettings.json
├── bin
├── node_modules
├── obj
├── openapi.json
├── openapi.yml
├── package-lock.json
├── package.json
├── scripts
├── worksample-dotnetcore-swagger-axios.csproj
└── wwwroot

プロジェクト作成

vueでもreactでもangularでも何でもいいですが、とりあえずテンプレートが用意されているreactで進めます。

mkdir worksample-dotnetcore-swagger-axios
dotnet new reactredux -o worksample-dotnetcore-swagger-axios
cd worksample-dotnetcore-swagger-axios
dotnet run // localhost:5001でhelloworldが表示されます

SwaggerUIを表示するまで

パッケージ

dotnet add package Swashbuckle.AspNetCore --version 5.4.1

Controller

SampleController.csを作成します。

worksample-dotnetcore-swagger-axios/Controllers/SampleController.cs
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;

namespace worksample_dotnetcore_swagger_axios.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class SampleController : ControllerBase
    {
        /// <summary>
        /// ほげほげ
        /// </summary>
        /// <param name="model">ふがふが</param>
        /// <returns>ほげほげふがふが</returns>
        [HttpPost("{id}")]
        public ResponseModel Post([Required][FromBody]RequestModel model)
        {
            return new ResponseModel();
        }
    }

    public class RequestModel{
        [Required]
        public string Param { get; set; }
    }

    public class ResponseModel{
        [Required]
        public string Value { get; set; }      
    }
}

Startup::ConfigureServices

サービスを追加します。

worksample-dotnetcore-swagger-axios/Startup.cs
services.AddSwaggerGen(c => {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });

    // operationidの設定
    c.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["controller"]}{e.ActionDescriptor.RouteValues["action"]}");

    // コメントも定義に出力するように
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});

Startup::Configure

ミドルウェアは定義する順番によって動作が変わります。
今回はとりあえずConfigureメソッドの一番上に追加します。

worksample-dotnetcore-swagger-axios/Startup.cs
app.UseStaticFiles(); // for wwwroot/swagger/ui
app.UseSwagger();
app.UseSwaggerUI(c => {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

プロジェクトファイル

XMLコメントを有効にするためにプロジェクトファイルに以下を追加します。
bin以下にコメントのxmlファイルを出力するようになります。

worksample-dotnetcore-swagger-axios/worksample-dotnetcore-swagger-axios.csproj
<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

起動

dotnet run

https://localhost:5001/swagger/v1/swagger.json
上記URLにアクセスするとOpenAPIのインターフェース定義を確認できます。

Swagger UIの準備

プロジェクト直下にwwwroot/swagger/uiディレクトリ作成し、GitHub SwaggerUIのdistフォルダの中身をまるっとコピーします。

$ tree ./wwwroot/
./wwwroot/
└── swagger
    └── ui
        ├── favicon-16x16.png
        ├── favicon-32x32.png
        ├── index.html
        ├── oauth2-redirect.html
        ├── swagger-ui-bundle.js
        ├── swagger-ui-bundle.js.map
        ├── swagger-ui-standalone-preset.js
        ├── swagger-ui-standalone-preset.js.map
        ├── swagger-ui.css
        ├── swagger-ui.css.map
        ├── swagger-ui.js
        └── swagger-ui.js.map

wwwroot/swagger/ui/index.htmlの42行目あたりの読み込む定義ファイルのURLを、自分の定義に修正します。

worksample-dotnetcore-swagger-axios/wwwroot/swagger/ui/index.html
const ui = SwaggerUIBundle({
  // url: "https://petstore.swagger.io/v2/swagger.json",
  url: "https://localhost:5001/swagger/v1/swagger.json",
  dom_id: '#swagger-ui',
省略...

起動

dotnet run

https://localhost:5001/swagger/ui/index.html
上記URLにアクセスするとSwaggerUIを確認できます。

インターフェース定義をJSON/YAMLで出力するまで

パッケージ

dotnet add package Swashbuckle.AspNetCore.Newtonsoft --version 5.4.1
dotnet add package Swashbuckle.AspNetCore.Swagger --version 5.4.1

dotnet-tools.json

プロジェクト直下に.configフォルダを作り、その下にdotnet-tools.jsonを作成します。

worksample-dotnetcore-swagger-axios/.config/dotnet-tools.json
{
  "version": 1,
  "isRoot": true,
  "tools": {
    "swashbuckle.aspnetcore.cli": {
      "version": "5.4.1",
      "commands": [
        "swagger"
      ]
    }
  }
}

プロジェクトファイル

今回はビルド後に指定したパスに定義ファイルを自動で出力するようにします。
ビルド後イベントにHookするためにプロジェクトファイルに以下を追加します。

worksample-dotnetcore-swagger-axios/worksample-dotnetcore-swagger-axios.csproj
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
  <Exec Command="dotnet tool restore" />
  <Exec Command="dotnet swagger tofile --output ./openapi.json ./bin/Debug/netcoreapp3.1/worksample-dotnetcore-swagger-axios.dll v1" />
  <Exec Command="dotnet swagger tofile --yaml --output ./openapi.yml ./bin/Debug/netcoreapp3.1/worksample-dotnetcore-swagger-axios.dll v1" />
</Target>

ビルド

.NET Core 2.1 Runtime が入っていない環境だとビルドが通らないようです。
私は以下からダウンロードしました。

Download .NET Core 2.1
https://dotnet.microsoft.com/download/dotnet-core/2.1

dotnet build

プロジェクト直下に以下のファイルが出力されていれば成功です。

  • openapi.json
  • openapi.yml

インターフェース定義ファイルからAxiosのコードを生成するまで

パッケージ

Axios本体とインターフェース定義ファイルからAxiosのコードを生成してくれるopenapi-generator-cliをインストールします。

cd ClientApp/
npm install axios
npm install -D @openapitools/openapi-generator-cli

生成前にディレクトリを削除したいのでrimrafもインストールします

npm install -D rimraf

npm-scripts

./ClientApp/package.jsonに以下を追加します。

worksample-dotnetcore-swagger-axios/ClientApp/package.json
"scripts": {
...省略
  "openapi:regen": "rimraf ./src/openapisdk && openapi-generator generate -g typescript-axios -i ../openapi.json -o ./src/openapisdk"
}

上記のスクリプトを実行するとプロジェクト直下にあるopenapi.jsonをもとに./ClientApp/src/openapisdkにAxiosのコードが生成されます。

npm run openapi:regen

dotnetビルド→openapi:regenを1コマンドで

方法はいろいろあると思いますが、クロスプラットフォームに対応したいのでnpm-scriptsに書いていきます。

プロジェクト直下にpackage.jsonを作ります。

cd ../
npm init

npm-scriptsからdotnetコマンドを実行したいのでshelljsをインストールします。

npm i shelljs

プロジェクト直下にscriptsフォルダを作りその下にdotnetbuild.jsを作りました。

worksample-dotnetcore-swagger-axios/scripts/dotnetbuild.js
var shell = require('shelljs');

if (shell.exec("dotnet build").code !== 0) {
  shell.exit(1);
}

最後にnpm-scriptsに以下を記述します。

worksample-dotnetcore-swagger-axios/package.json
"scripts": {
    "openapi:clientapp": "node ./scripts/dotnetbuild.js && cd ./ClientApp && npm run openapi:regen"
}

これでプロジェクトのビルドからAxiosのコード生成が1コマンドで行われるようになりました。

npm run openapi:clientapp

コードの補完も効くのでまあまあ快適です。
vsc.gif
以上です。

おまけ

Swagger UIのテーマはこちらからどうぞ
Swagger UI Themes
https://ostranme.github.io/swagger-ui-themes/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ASP.NET Coreとフロントエンドの繋ぎこみをOpenAPI(Swagger)定義ファイルを使って効率化する

TL;DR

以下の手順で作られるコードを以下に置いておきます。お忙しい方はこちらからどうぞ :angel_tone1:
https://github.com/ishiyama0530/worksample-dotnetcore-swagger-axios

モチベーション

  • ソースコードからOpenAPIのインターフェースを生成したい
  • SwaggerUIを表示したい
  • OpenAPIの定義ファイルをJSON/YAML形式で出力したい
  • 定義ファイルからAxiosのコードを生成したい
  • すべてを1コマンドで行いたい

参考

プロジェクト構成

最終的にはこんな感じになります。

$ tree ./worksample-dotnetcore-swagger-axios -a -L 1
./worksample-dotnetcore-swagger-axios
├── .config
├── .git
├── .gitignore
├── ClientApp
├── Controllers
├── Pages
├── Program.cs
├── Properties
├── Startup.cs
├── WeatherForecast.cs
├── appsettings.Development.json
├── appsettings.json
├── bin
├── node_modules
├── obj
├── openapi.json
├── openapi.yml
├── package-lock.json
├── package.json
├── scripts
├── worksample-dotnetcore-swagger-axios.csproj
└── wwwroot

プロジェクト作成

vueでもreactでもangularでも何でもいいですが、とりあえずテンプレートが用意されているreactで進めます。

mkdir worksample-dotnetcore-swagger-axios
dotnet new reactredux -o worksample-dotnetcore-swagger-axios
cd worksample-dotnetcore-swagger-axios
dotnet run // localhost:5001でhelloworldが表示されます

SwaggerUIを表示するまで

パッケージ

dotnet add package Swashbuckle.AspNetCore --version 5.4.1

Controller

SampleController.csを作成します。

worksample-dotnetcore-swagger-axios/Controllers/SampleController.cs
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;

namespace worksample_dotnetcore_swagger_axios.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class SampleController : ControllerBase
    {
        /// <summary>
        /// ほげほげ
        /// </summary>
        /// <param name="model">ふがふが</param>
        /// <returns>ほげほげふがふが</returns>
        [HttpPost("{id}")]
        public ResponseModel Post([Required][FromBody]RequestModel model)
        {
            return new ResponseModel();
        }
    }

    public class RequestModel{
        [Required]
        public string Param { get; set; }
    }

    public class ResponseModel{
        [Required]
        public string Value { get; set; }      
    }
}

Startup::ConfigureServices

サービスを追加します。

worksample-dotnetcore-swagger-axios/Startup.cs
services.AddSwaggerGen(c => {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });

    // operationidの設定
    c.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["controller"]}{e.ActionDescriptor.RouteValues["action"]}");

    // コメントも定義に出力するように
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});

Startup::Configure

ミドルウェアは定義する順番によって動作が変わります。
今回はとりあえずConfigureメソッドの一番上に追加します。

worksample-dotnetcore-swagger-axios/Startup.cs
app.UseStaticFiles(); // for wwwroot/swagger/ui
app.UseSwagger();
app.UseSwaggerUI(c => {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

プロジェクトファイル

XMLコメントを有効にするためにプロジェクトファイルに以下を追加します。
bin以下にコメントのxmlファイルを出力するようになります。

worksample-dotnetcore-swagger-axios/worksample-dotnetcore-swagger-axios.csproj
<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

起動

dotnet run

https://localhost:5001/swagger/v1/swagger.json
上記URLにアクセスするとOpenAPIのインターフェース定義を確認できます。

Swagger UIの準備

プロジェクト直下にwwwroot/swagger/uiディレクトリ作成し、GitHub SwaggerUIのdistフォルダの中身をまるっとコピーします。

$ tree ./wwwroot/
./wwwroot/
└── swagger
    └── ui
        ├── favicon-16x16.png
        ├── favicon-32x32.png
        ├── index.html
        ├── oauth2-redirect.html
        ├── swagger-ui-bundle.js
        ├── swagger-ui-bundle.js.map
        ├── swagger-ui-standalone-preset.js
        ├── swagger-ui-standalone-preset.js.map
        ├── swagger-ui.css
        ├── swagger-ui.css.map
        ├── swagger-ui.js
        └── swagger-ui.js.map

wwwroot/swagger/ui/index.htmlの42行目あたりの読み込む定義ファイルのURLを、自分の定義に修正します。

worksample-dotnetcore-swagger-axios/wwwroot/swagger/ui/index.html
const ui = SwaggerUIBundle({
  // url: "https://petstore.swagger.io/v2/swagger.json",
  url: "https://localhost:5001/swagger/v1/swagger.json",
  dom_id: '#swagger-ui',
省略...

起動

dotnet run

https://localhost:5001/swagger/ui/index.html
上記URLにアクセスするとSwaggerUIを確認できます。

インターフェース定義をJSON/YAMLで出力するまで

パッケージ

dotnet add package Swashbuckle.AspNetCore.Newtonsoft --version 5.4.1
dotnet add package Swashbuckle.AspNetCore.Swagger --version 5.4.1

dotnet-tools.json

プロジェクト直下に.configフォルダを作り、その下にdotnet-tools.jsonを作成します。

worksample-dotnetcore-swagger-axios/.config/dotnet-tools.json
{
  "version": 1,
  "isRoot": true,
  "tools": {
    "swashbuckle.aspnetcore.cli": {
      "version": "5.4.1",
      "commands": [
        "swagger"
      ]
    }
  }
}

プロジェクトファイル

今回はビルド後に指定したパスに定義ファイルを自動で出力するようにします。
ビルド後イベントにHookするためにプロジェクトファイルに以下を追加します。

worksample-dotnetcore-swagger-axios/worksample-dotnetcore-swagger-axios.csproj
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
  <Exec Command="dotnet tool restore" />
  <Exec Command="dotnet swagger tofile --output ./openapi.json ./bin/Debug/netcoreapp3.1/worksample-dotnetcore-swagger-axios.dll v1" />
  <Exec Command="dotnet swagger tofile --yaml --output ./openapi.yml ./bin/Debug/netcoreapp3.1/worksample-dotnetcore-swagger-axios.dll v1" />
</Target>

ビルド

.NET Core 2.1 Runtime が入っていない環境だとビルドが通らないようです。
私は以下からダウンロードしました。

Download .NET Core 2.1
https://dotnet.microsoft.com/download/dotnet-core/2.1

dotnet build

プロジェクト直下に以下のファイルが出力されていれば成功です。

  • openapi.json
  • openapi.yml

インターフェース定義ファイルからAxiosのコードを生成するまで

パッケージ

Axios本体とインターフェース定義ファイルからAxiosのコードを生成してくれるopenapi-generator-cliをインストールします。

cd ClientApp/
npm install axios
npm install -D @openapitools/openapi-generator-cli

生成前にディレクトリを削除したいのでrimrafもインストールします

npm install -D rimraf

npm-scripts

./ClientApp/package.jsonに以下を追加します。

worksample-dotnetcore-swagger-axios/ClientApp/package.json
"scripts": {
...省略
  "openapi:regen": "rimraf ./src/openapisdk && openapi-generator generate -g typescript-axios -i ../openapi.json -o ./src/openapisdk"
}

上記のスクリプトを実行するとプロジェクト直下にあるopenapi.jsonをもとに./ClientApp/src/openapisdkにAxiosのコードが生成されます。

npm run openapi:regen

dotnetビルド→openapi:regenを1コマンドで

方法はいろいろあると思いますが、クロスプラットフォームに対応したいのでnpm-scriptsに書いていきます。

プロジェクト直下にpackage.jsonを作ります。

cd ../
npm init

npm-scriptsからdotnetコマンドを実行したいのでshelljsをインストールします。

npm i shelljs

プロジェクト直下にscriptsフォルダを作りその下にdotnetbuild.jsを作りました。

worksample-dotnetcore-swagger-axios/scripts/dotnetbuild.js
var shell = require('shelljs');

if (shell.exec("dotnet build").code !== 0) {
  shell.exit(1);
}

最後にnpm-scriptsに以下を記述します。

worksample-dotnetcore-swagger-axios/package.json
"scripts": {
    "openapi:clientapp": "node ./scripts/dotnetbuild.js && cd ./ClientApp && npm run openapi:regen"
}

これでプロジェクトのビルドからAxiosのコード生成が1コマンドで行われるようになりました。

npm run openapi:clientapp

コードの補完も効くのでまあまあ快適です。
vsc.gif
以上です。

おまけ

Swagger UIのテーマはこちらからどうぞ
Swagger UI Themes
https://ostranme.github.io/swagger-ui-themes/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BlazorでFluxorを使った状態管理

(An English version of this article is also available.)

この記事ではBlazorで状態管理(State Management)を行う手法について紹介していきます。
今回使用したのはFluxorという.NET向けのFlux/Redux実装です。

状態管理とは

状態管理、State Managementとは多くのコンポーネントに散らばりがちなアプリケーション内の状態を集約する形でアプリケーションの状態変化(ユーザーの入力等)に柔軟に対応できるようなシステムです。

使用したプロジェクト

今回使用したプロジェクトは拙作の仙狐ビュワーです。完全なソースは公開していませんが、仕組み自体はシンプルなプロジェクトですので、コードサンプルを交え、その実装の仕組みについて紹介していきます。

プロジェクトの内容

仙狐ビュワーは高解像度の画像を公開するためのシステムで、要はスライドショーアプリケーションです。URLからのパラメーターの受け取りや自動ページ切り替えなどの機能も実装していますが、今回の状態管理とはあまり関係していませんので、説明は省きます。

仙狐ビュワーでは画像情報を示したJSONファイルを読み取り、そのJSONファイルを元にインデックスを作成、それを目次として使い、画像を読み込む、という仕組みになっています。

以前の実装

以前使用していたシンプルな実装においてはただ単に、インデックスを保持し、ボタンの押下に対してそのインデックスを書き換える方法でした。この手のプログラムにおいては教科書的なかなりシンプルな実装です。

<div>
<img src="@imageName"/>
</div>

@code 
{
  public string imageName;
  List<SenkoImage> images
  private int index = 0;

  public class SenkoImage
  {
    public string FileLocation;
    public string FileId;
  }

  public void Next()
  {
    index++;

    // ... 最後のページに達した場合の処理(0に戻す)

    imageName = images[index].FileLocation;
  }

  public void Prev()
  {
    index--;

    // ... 最初のページに達した場合の処理(最大ページに戻す)

    imageName = images[index].FileLocation;
  }

  public void Set(int index)
  {
    // ... indexが正常値かどうかを確認

    imageName = images[index].FileLocation;
  }

  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    // リストの初期化など
  }
}

実際はこの程度の実装であればこのような形で行うのも全く問題ないかと思います。ただ、ここでは状態管理の実装を試してみる、というモチベーションがありますので、敢えて、これを状態管理の手法で実装してみます。

Fluxorを導入

早速Fluxorを導入していきます。
尚、Fluxorは状態管理そのものを実装しているシステムであり、必ずしもBlazorと合わせて使う必要がありません。実際にコマンドラインアプリでこれを使う方法も紹介されています。
今回はBlazorアプリで使用しますので、Fluxor.Blazor.Webパッケージをプロジェクトに追加して下さい。執筆時点でのバージョンは3.1.1です。

有効化の方法

有効化に必要なコードはドキュメントでも紹介されていますが、概ね以下のとおりです。(他のライブラリで使用される方法とあまり違いはありません。)

www/index.html(Blazor Serverの場合Pages/_Host.cshtml)に以下を追加。

<script src="_content/Fluxor.Blazor.Web/scripts/index.js"></script>

Program.Mainメソッドに以下を追加。

var currentAssembly = typeof(Program).Assembly;
builder.Services.AddFluxor(options => options.ScanAssemblies(currentAssembly));

Blazor Serverの場合は次をStartup.ConfigureServicesメソッドに追加します。

var currentAssembly = typeof(Startup).Assembly;
services.AddFluxor(options => options.ScanAssemblies(currentAssembly));

App.razorに以下を追加。

<Fluxor.Blazor.Web.StoreInitializer/>

実装

ここから実装していきます。

状態の定義

先ずは状態の定義を行います。今回の例では単一のシンプルなルールですのでStore/SlideUseCase/SlideState.csを作成し、以下のように定義しました。

namespace SenkoViewer.Store.SlideUseCase
{
    public class SlideState
    {
        public SlideState(int slideCount)
        {
            SlideCount = slideCount;
        }

        public int SlideCount { get; }
    }
}

これが状態を格納するためのクラスになります。

次に、Featureを定義します。こちらはStore/SlideUseCase/Feature.csとして作成しました。

using Fluxor;

namespace SenkoViewer.Store.SlideUseCase
{
    public class Feature : Feature<SlideState>
    {
        public override string GetName()
        {
            return "Slide";
        }

        protected override SlideState GetInitialState()
        {
            return new SlideState(0);
        }
    }
}

状態変移の実装

ここで各機能を実装していきますが、今回の例では大きく三種類の機能を持たせます。以下のとおりです。

  • 次のページに移行するIncrementSlide
  • 前のページに移行するDecrementSlide
  • 任意のページに移行するSetSlide

まずはIncrementSlideを実装します。DecrementSlideに関してはこれと同様ですが、インデックスを減らす処理になります。
Store/SlideUseCase/IncrementSlideAction.csに以下のようにしました。こちらは空で問題ありません。

namespace SenkoViewer.Store.SlideUseCase
{
    public class IncrementSlideAction
    {
    }
}

次にStore/SlideUseCase/Reducers.csに以下のように記述します。

using Fluxor;

namespace SenkoViewer.Store.SlideUseCase
{
    public class Reducers
    {
        [ReducerMethod]
        public static SlideState ReduceIncrementSlideAction(SlideState state, IncrementSlideAction action)
        {
            return new SlideState(state.SlideCount + 1);
        }
    }
}

表示ロジックからの使用

さて、これを使用するためにはロジックから使用することにします。仙狐ビュワーにおいては、まず、画像表示部を専用のコンポーネントに移動させ、以下のような形で画像を呼び出せる形にしました。

<SenkoDisplay Index="0"/>

ですので、razorファイルの中では以下のように定義されています。これにより、画像のインデックスは状態管理のものが読み出されます。

<SenkoDisplay Index="@SlideState.Value.SlideCount"/>

まず、使用するrazorファイルに以下を追加します。

@inherits Fluxor.Blazor.Web.Components.FluxorComponent

その上でDispatcherをインジェクトします。

[Inject] public IDispatcher Dispatcher { get; set; }

また、各状態ごとにインターフェースもインジェクトします。

[Inject] private IState<SlideState> SlideState { get; set; }

次に進めるためのメソッドは以下のようにします。

public async Task Next()
{
  var action = new IncrementSlideAction();
  Dispatcher.Dispatch(action);

  await InvokeAsync(StateHasChanged);
}

これでこのメソッドが呼び出されると次の画像が表示される形になります。

任意の画像を設定できるようにする

仙狐ビュワーの機能として、パラメーターから提示した識別子(画像のハッシュ値)をもとに画像を呼び出したり、また、ランダムに画像を表示する機能も含んでいます。そのため、現在の形ではそれが行なえません。

そのため、SetSlideとして、これを行うためのものを実装していきます。

Store/SlideUseCase/SetSlideAction.csはIncrementやDecrementのものと少し違うものになります。(これはIncrementやDecrementでは空の実装だったものです。)

namespace SenkoViewer.Store.SlideUseCase
{
    public class SetSlideAction
    {
        public SetSlideAction(int index)
        {
            Value = index;
        }

        public int Value { get; set; }
    }
}

これにより、アクションにValueを渡せるようにします。

Reducerに関してはこれから取り出した値を設定する形になります。

[ReducerMethod]
public static SlideState ReduceSetSlideAction(SlideState state, SetSlideAction action)
{
    return new SlideState(action.Value);
}

これにより、以下のような形で任意の画像を表示できるようになります。

var action = new SetSlideAction(5);
Dispatcher.Dispatch(action);

ページのループを設定する

現在の実装のままでは最後のページに次、最初のページに前に進むような形になった場合に、例外が発生してしまいます。

そのため、Store/SlideUseCase/SlideState.csを次のように変更してみます。

using SenkoViewer.AssetAccessor;

namespace SenkoViewer.Store.SlideUseCase
{
    public class SlideState
    {
        public SlideState(int slideCount)
        {
            if (slideCount > Max - 1)
                SlideCount = 0;
            else if (slideCount < 0)
                SlideCount = Max - 1;
            else
                SlideCount = slideCount;
        }

        public int SlideCount { get; }
    }
}

最後に

仙狐ビュワーの場合には機能がさほど複雑ではないところもあり、これを行うことで却ってコード量が反対に増えてしまう形になります。より複雑な状態を管理しないといけない場合において、状態管理はその威力を発揮するかと思います。

Fluxor自体はBlazorアプリ以外でも使用できるようになっている点など、他にも応用することができ、ソリューションを問わず、状態管理機構が必要な場合において有用かと思います。

Fluxorにはこの他、Effectorなど他の使用ケースなども使用することが出来ますが、これはまたの機会に紹介していきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JsonからC#のクラスモデルの作成

C#でJsonをパースする際にクラスモデルを作成すると楽なのですが、
この時にJsonからC#のクラスへ変換してくれるサイトがあります。

よく出てくるのは
http://json2csharp.com/
なのですが、どうも今は閉まっているみたいです。

代わりを探してみたら以下のサイトが使えました。
https://jsonutils.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenCVSharpでマウスを使う

概要

ちょっとした操作をマウスで行いたかった。さらに線や円などのプリミティブな図形も描きたかったので、OpenCVSharpを使ってみることにした。そこで引っ掛かったことを記す。

不具合例

Program.cs
using OpenCvSharp;
  class Program
  {
    public static Mat img;
    static Point mousepos = new Point();
    static Scalar color = new Scalar(0, 255, 255);
    static void Main(string[] args)
    {
      img = new Mat(new Size(640, 480), MatType.CV_8UC3);
      using (Window win = new Window("OpenCV", img))
      {
        Cv2.SetMouseCallback("OpenCV", Win_OnMouseCallback);
        while(true)
        {
          Cv2.ImShow("OpenCV", img);
          int ret = Cv2.WaitKey(1);
          if (ret == 27) { break; }
        }
        Cv2.DestroyAllWindows();
      }
    }//End of Main

    private static void Win_OnMouseCallback(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userdata)
    {
      Console.WriteLine($"event: {@event}, (x, y)= ({x}, {y}), flags: {flags}");
      if (flags==MouseEventFlags.LButton)
      {
        mousepos.X = x; mousepos.Y = y;
        img.Circle(mousepos, 2, color);
      }
    }
  }

何の変哲もない。
しかしリリースモードでビルドして実行して、暫くすると
20200531_err1.png
これは何のことだろう?デバッグモードでは出てこないけど・・・
ググると、C#のリリースモードはいろいろ最適化されてるから別コードと考えた方がよいとか、Releaseでもデバッグ用にログを取るとかいろいろ出てきた。でもこんな簡単なものなのに・・
しかもアンマネージってOpenCVのところ??

解決策

ようやく探し出せた
https://www.ipentec.com/document/csharp-error-delegate-must-be-kept-alive-by-the-managed-application
よく見るとWin_MouseCallBackはdelegate型になってない。staticで宣言されているけど、ガベージコレクタが回収してしまうようだ。ちゃんと確保して回収されないようにしてあげよう。差分だけ書かせてもらうと正しくは

Program.cs
    public static Mat img;
    static Point mousepos = new Point();
    static Scalar color = new Scalar(0, 255, 255);
    static MouseCallback mcb;  //追加
    static void Main(string[] args)
    {
      img = new Mat(new Size(640, 480), MatType.CV_8UC3);
      mcb=new MouseCallback(Win_OnMouseCallback); //追加
      using (Window win = new Window("OpenCV", img))
      {
        Cv2.SetMouseCallback("OpenCV", mcb);//変更 
        while(true)
        //以下略

この「ガベージコレクタが回収する」エラーは、IDEでデバッグしているときは出てこず、使い始めてしばらくすると出てくるのと、再現するのに時間がかかるので、だいぶ厄介だった。わかってみれば「型を合わせてメモリを確保する」をちゃんとするだけなのだが。C++のDLLを使うプログラムでは気を付けよう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# Linqを使ったら超便利だった#1

Linqとは

C#の統合言語型クエリ
コレクションの要素を処理するメソッドがある。

Linqを使用するとfor文やforeach文、if文を使用しなくても
処理を書くことが出来る場合があります。
そのため、全体のライン数とネストを減らして記載することが出来ます。
ライン数やネストが減ると可読性向上にも繋がります。

Linqを使用したと使用しない処理での違いを検証

今回はそれぞれの動作を確認しつつ、Listに格納したデータを処理してみます。
以下に記載しているサンプルソースは以下のリストを宣言しており、以下の宣言はサンプルソースから省いています。

sample.cs
            List<int> source = new List<int>() { 1, 2, 3, 4, 5, 6 };
            List<int> target = new List<int>();

また、foreachはListクラスのForEachメソッドを使用して、以下のように書き換えています。

sample.cs
            foreach(int num in source)
            {
                Console.WriteLine(num);
            }
sample.cs
            source.ForEach(delegate (int num) { Console.WriteLine(num); });

  • Where

WhereはList内から条件に一致したデータを抽出します。

Linqを使わない場合

sample.cs
            foreach (int num in source)
            {
                //sourceから5未満のリストを作成
                if(num < 5)
                {
                    target.Add(num);
                }
            }
            //sourceを出力
            Console.WriteLine("source");
            foreach (int num in source)
            {
                Console.WriteLine(num);
            }
            //targetを出力
            Console.WriteLine("target");
            foreach (int num in target)
            {
                Console.WriteLine(num);
            }

Linqを使った場合

sample.cs
            //sourceから5未満のリストを作成
            target = source.Where(num => num < 5).ToList();

            //sourceを出力
            Console.WriteLine("source");
            source.ForEach(delegate (int num) { Console.WriteLine(num); });

            //targetを出力
            Console.WriteLine("target");
            target.ForEach(delegate (int num) { Console.WriteLine(num); });

出力結果はどちらも以下のようになります。
出力サンプル(where).png

  • Select

SelectはList内の要素を計算した結果を出力できます。
リストの値に

Linqを使わない場合

sample.cs
            List<int> source = new List<int>() { 1, 2, 3, 4, 5, 6 };
            List<int> target = new List<int>();

            foreach (int num in source)
            {
                //5を乗算した値を追加
                target.Add(num * 5);
            }

            foreach (int num in target)
            {
                Console.WriteLine(num);
            }

Linqを使った場合

sample.cs
            target = source.Select(num => num * 5).ToList();

            target.ForEach(delegate (int num) {Console.WriteLine(num); });

出力結果はどちらも以下のようになります。
出力サンプル(select).png

Selectでは以下のようにして、インデックスを抜き出すこともできます。

sample.cs
            source.Select((data, index) => new { Data = data, Index = index })
  • その他の便利な使い方

foreachとwhichを組み合わせるとforeachの中でif文を記載しなくても、同様の処理が出来たりします。
リストの値を出力する処理です。

sample.cs
            foreach (int data in source.Where(tmpData => (tmpData == 1) || (tmpData == 3)))
            {
                Console.WriteLine(data);
            }

組み合わせると以下のような処理もできたりします。
0番目から偶数位置に格納されているリストの値を出力する処理です。
ここまですると逆に複雑な気もします。

sample.cs
            source.Select((data, index) => new { Data = data, Index = index })
                .Where(SelectData => (SelectData.Index == 0 || SelectData.Index % 2 == 0)).ToList()
                .ForEach(num => { Console.WriteLine(num.Data); });

  • 終わりに

Linqのメソッドを紹介してみました。
Linqはぱっと見でわかりにくい場合もあるので、なるべくコメントは書いた方がよさそうですね
他にもLinqの便利なメソッドはあるので、また時間があるときに書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C#] Entity Framework Coreのデータベースファーストでエラーが出た時の対処方法

エラー内容

PM> Scaffold-DbContext "[ConnectionString]" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities
Build started...
Build succeeded.
Error:
  An assembly specified in the application dependencies manifest (project_name.deps.json) was not found:
    package: 'project_name', version: '1.0.0'
    path: 'project_name.dll'

対処方法

dotnet ef dbcontext scaffold "[ConnectionString]" Microsoft.EntityFrameworkCore.SqlServer -o Entities

補足

以下は必須。

dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.Design

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む