System.Text.Jsonネームスペース

.netでJSONを扱う場合、Newtonsoft.JSONを使用するのが一般的であったが、dotnet core 3から、System.Text.Jsonネームスペースのライブラリが使用できるようになったので、試してみた。まず、シリアライズ(POCOからJSONへの変換)を試してみる。
↓のようなクラスインスタンスをJSONへ変換してみよう。

class Person {
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
    public List<MailAddress> Mail {get; set;}
    [JsonIgnore] // シリアライズ時に無視する(JSONに吐き出さない)プロパティ
    public int Age {
        get => Period.Between(LocalDate.FromDateTime(Birthday),LocalDate.FromDateTime(DateTime.Now)).Years;
    }
    public override string ToString() {
        string retstr = $"Name={Name}, Birthday={Birthday.ToString("yyyy/MM/dd")}({Age})";
        foreach(var m in Mail) {
            retstr += $"\r\n\t{m}";
        }
        return retstr;
    }
}
class MailAddress {
    public string Address {get; set;}
    public string DisplayName { get; set; }
    public override string ToString() {
        return $"{DisplayName}<{Address}>";
    }
}

インスタンスに値を設定して・・・

List<Person> personel = new List<Person>() {
    new Person() {
            Name = "T.Sumomo",
            Birthday = DateTime.Parse("1964/02/03"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "t.sumomo@momo.com", DisplayName = "Taro Sumomo" },
                new MailAddress() { Address = "t-sumomo@gmail.com", DisplayName = "すももから生まれたすもも太郎" }
            }
    },
    new Person() {
            Name = "J.Sumomo",
            Birthday = DateTime.Parse("1976/12/13"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "j.sumomo@momo.com", DisplayName = "Jiro Sumomo" },
                new MailAddress() { Address = "j-sumomo@live.com", DisplayName = "ジロー・チェインジ" }
            }
    },
    new Person() {
            Name = "S.Sumomo",
            Birthday = DateTime.Parse("1989/06/25"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "s.sumomo@momo.com", DisplayName = "Sabro Sumomo" },
                new MailAddress() { Address = "s-sumomo@hotmail.com", DisplayName = "すももサブロ~" }
            }
    }
};

シリアライズをする。

Console.WriteLine("Serialize");
// 結果にインデントを付けよう
var opt = new JsonSerializerOptions() {
    WriteIndented = true
};
// シリアライズ
string jsonstr = JsonSerializer.Serialize(personel,opt);
Console.WriteLine($"JSON String={jsonstr}");

結果は

Serialize
JSON String=[
  {
    "Name": "T.Sumomo",
    "Birthday": "1964-02-03T00:00:00",
    "Mail": [
      {
        "Address": "t.sumomo@momo.com",
        "DisplayName": "Taro Sumomo"
      },
      {
        "Address": "t-sumomo@gmail.com",
        "DisplayName": "\u3059\u3082\u3082\u304B\u3089\u751F\u307E\u308C\u305F\u3059\u3082\u3082\u592A\u90CE"
      }
    ]
  },
・・・
]

と、こんな形にシリアライズされる。

この、シリアライズされたJSONをデシリアライズ(JSON⇒POCO)してみる。

Console.WriteLine("Deserialize");
// 先ほどシリアライズしたJSONをデシリアライズ
var person = JsonSerializer.Deserialize<List<Person>>(jsonstr);
foreach(var p in person) {
    Console.WriteLine(p);
}

結果は

Deserialize
Name=T.Sumomo, Birthday=1964/02/03(56)
        Taro Sumomo<t.sumomo@momo.com>
        すももから生まれたすもも太郎<t-sumomo@gmail.com>
Name=J.Sumomo, Birthday=1976/12/13(43)
        Jiro Sumomo<j.sumomo@momo.com>
        ジロー・チェインジ<j-sumomo@live.com>
Name=S.Sumomo, Birthday=1989/06/25(30)
        Sabro Sumomo<s.sumomo@momo.com>
        すももサブロ~<s-sumomo@hotmail.com>

このように、結構簡単にJSONを扱うことができる。
なお、シリアライズ/デシリアライズ以外にも、JsonDocumentを使用することで、DOMのようなアクセス方法も可能。

ちなみに、今回試したのは.NET 5 Preview 3。MSのドキュメントを見る限りでは.net core 3.0から使用できるはず。

後、下記usingを忘れずにね。

using System.Text.Json;
using System.Text.Json.Serialization;

細かいところまでは見ていないけど、簡単な使い方なら、Newtonsoft.JSONを置き換えることはできそうだね。

参考サイト JSON serialization and deserialization (marshalling and unmarshalling) in .NET – overview

カテゴリー: C#, dotnetcore, 技術系 | コメントする

blazor wasm 3.2.0 Preview 3-構成ファイル読み取り

blazor wasm 3.2.0 Preview 3では、アプリケーション構成ファイルを標準で読み込めるようになった。

構成ファイルはappsettings.jsonと言う名前で、wwwroot下に置く必要がある。内容的には下記のような形。

{
    "MyString": "Hello World!!",
    "MySection": {
        "String":"String",
        "Number":500
    }
}

値を取得するには、

@using Microsoft.Extensions.Configuration
@inject IConfiguration Config

で、構成ファイル取得用のライブラリをusingし、IConfigurationをインジェクトする。

実際に値を取得するには下記のように記述。

// 単体のキーから値を取得(MyStringの値を取得)
string myString = Config["MyString"];
// セクション中の値をキー指定で取得
string strValue = Config.GetSection("MySection")["String"];
int iValue = Convert.ToInt32(Config.GetSection("MySeciton")["Number"]);

通常のConfigurationExtensionと異なり、<T>GetValue(string)が無いのが残念だね。

カテゴリー: asp.net core, Blazor, C#, dotnetcore, 技術系 | コメントする

Entity Framework Core

Entity Framework Coreは.NET Entity Frameworkのdotnet core版。

必要パッケージ

  1. Microsoft.EntityFrameworkCore
  2. 各DBのEntityFrameworkプロバイダ
  3. Microsoft.EntityFrameworkCore.Design
  4. 各DBのEntityFrameworkデザイナ

1,2は必須で、2は実際にデータベースにアクセスするためのドライバ。例えば、MS SQL Serverなら、Microsoft.EntityFrameworkCore.SqlServer,MySQLなら、Pomelo.EntityFrameworkCore.MySql(本家Oracleのドライバは実行時エラーが出た-もう、直ったかな?)等。

3,4はC#コードからデータベーステーブルをジェネレートするのに必要。

なお、Entity Framework関連のMigration等を行う場合、最初に下記コマンドを実行して、dotnet-efツールをインストールしておく必要がある。

dotnet tool install --global dotnet-ef

EntityやDbContext等の定義は.NET Framework版とほぼ同様だが、DBにアクセスするためのコードが必要。
通常はDbContextから派生させたクラスの、OnConfiguringメソッドをオーバーライドして
接続先などを設定する。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.SqlServer;
・・・
    public class PersonDb : DbContext {
      ・・・
        protected override void OnConfiguring(DbContextOptionsBuilder builder) {
            builder
                .UseSqlServer(<接続文字列>);
        }
    }
}

上記は、MS SQL Serverに接続するための設定。
UseSqlServerの部分が拡張メソッドで、使用するプロバイダにより異なる。

DbContextとEntity定義のコードを作成して、下記コマンドを実行することにより、指定データベース上にテーブルが作成される。

dotnet ef migrations add <バージョン等分かりやすい名前>
dotnet ef database update

※データベースは作成しておく必要あり(?)

ちなみに、既存のテーブルから、DbContextとEntityコードを作成する事も可能。

dotnet ef dbcontext scaffold <接続文字列> <プロバイダ>
例)
dotnet ef dbcontext scaffold “Data Source=・・・” Microsoft.EntityFrameworkCore.SqlServer

上記によりDbContextを含んだソース,Entity毎のソースが作成される。

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする

.net coreでの設定ファイル読み込み

.net coreで設定ファイルを読み込むには通常、下記パッケージを使用する。

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.Json

上記はJSON形式で定義された設定ファイルを読み込むもの。
JSON形式の設定ファイルは下記のような感じで定義する。

{
    "ConnectionStrings": {
        "MyConnection" : "Data Source=..."
    },
    "MySettings": {
        "String" : "Hello",
        "NUmber" : 100
    }
}

設定値を参照するには、ConfigurationBuilderクラスを使用して、IConfiguraionRootのインスタンスを作成し、GetSectionメソッドおよびGetValueメソッドを用いて値を取得することが可能。
なお、接続文字列(ConnectionStrings)に関しては上記を用いずにGetConnecitonStringsを使用することが可能。

// 設定値の取得
var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
// MySettingsセクションの"String"の値を取得
string s = config.GetSection("MySettings").GetValue<string>("String");
// MySettingsセクションの"Number"の値を取得
int i = config.GetSection("MySettings").GetValue<int>("Number");
// 接続文字列"MyConnection"の値を取得
string strCon = config.GetConnectionString("MyConnection");

上記では、実行ファイルと同じ場所にappsettings.jsonというファイル名で定義ファイルが置いてあるという想定。フォルダが実行ファイルと異なる場所にあれば、AddJsonFileメソッドを呼び出す前に、    SetBasePathメソッドでディレクトリを指定する。

以上、忘れないようにメモ。

カテゴリー: C#, dotnetcore, 技術系 | コメントする

blazor wasm(hosted)とSignalR

以前、同じようなタイトルで、asp.net core blazor wasm版とSingalRの話を投稿した様な気がするが、blazor wasm版から既存のHubに接続できず、色々試したのだが、結局解決しなかった。

やはり、現状ではhostedプロジェクトとして作成し、同一ドメインでのみしかSignalRを利用できないようだ。

なので、とりあえず、hostedプロジェクトを試してみた。
まず、下記コマンドで、クライアントとサーバプロジェクトを作成する。

dotnet new blazorwasm --hosted -o <ソリューション名>

ソリューションフォルダ下に下記プロジェクトフォルダが作成される。

  • Client-blazor wasm プロジェクト
  • Server-asp.netプロジェクト
  • Shared-共通DLLプロジェクト

ClientプロジェクトにはSharedプロジェクトの参照が含まれ、ServerプロジェクトにはSharedプロジェクトとClientプロジェクトの参照が含まれる。つまり、共通クラスやInterface等はSharedプロジェクトに定義しておけば、Client/Server双方から参照出来るようになっている。

ServerプロジェクトにHUB本体を作成し、HubのEndpoint定義をして、ClientプロジェクトにSignalRクライアントライブラリを追加し、

dotnet add Client package Microsoft.AspNetCore.SignalR.Client

下記要領でHubに接続するコードを書くことで、ServerのHubに接続できるようになる。

    private HubConnection con;
          ・・・
            con = new HubConnectionBuilder()
                .WithUrl(NavigationManager.ToAbsoluteUri("/[ハブのエンドポイント]"))
                .Build();
           ・・・
            // 接続
            await con.StartAsync();

後はServerプロジェクトを起動して、ブラウザからアクセスするだけ。
もちろん、blazor server side版やWindows FormsなどからもこのHubに接続できる。

詳しくは、MSのサイト「Blazor WebAssembly で ASP.NET Core SignalR を使用する」をどうぞ。

正式リリース版では是非とも、既存サイトのHUBに接続できるようになって欲しいな。

カテゴリー: asp.net core, Blazor, C#, dotnetcore, SignalR, 技術系 | コメントする

blazor serverside apache httpdメモ

まぁ、当たり前の話だが、apache上でサーバのサブディレクトリとして、blazor server sideを動作させるには、それ用のサービスと、リバースプロキシ設定が必要。

また、WebSocketのためのリライトルールが必要となる。
以下のような感じ。

http://・・・/blazorappをhttp://localhost:5010/ にマッピングする。

<VirtualHost *:80>
        DocumentRoot /var/www/html
        ServerName ・・・
    
    ProxyPass /blazorapp http://localhost:5010/
    ProxyPassReverse /blazorapp http://localhost:5010/
    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
    RewriteRule /blazorapp/(.*) ws://localhost:5010/$1 [P]
</VirtualHost>

※kestrelサービスはlocalhost:5010で口を開けて待っているという想定。(appsettings.jsonにて指定するとかネ。)

[[Service定義ファイル]]

[Unit]
Description=<サービスの説明>
[Service]
WorkingDirectory=/<working directory path>
ExecStart=/usr/bin/dotnet /<dllパス>/<アプリケーション名>.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=<アプリケーション名>
User=apache
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
[[appsettings.json]]

{
  "Kestrel" : {
        "EndPoints" : {
                "Http" : {
                        "Url" : "http://localhost:5010"
                 }
         }
  },
     ・・・
}

一応、動作確認済み。でも、Websocketの箇所は少々、自信無し・・・
後で別のサービスを追加して確認してみよう・・・

カテゴリー: asp.net core, Blazor, dotnetcore, 技術系 | 6件のコメント

asp.net core webapi

ASP.NET CoreにはRESTful Web APIを作成するためのテンプレートが用意されている。
作成するには以下のように、プロジェクトテンプレートとして、webapiを指定する。

dotnet new webapi -o プロジェクトフォルダ

ASP.NET CoreのWeb APIはhttp要求を受け取って、結果をJSON形式で返す。

APIの実装にはhttp要求のメソッド毎に呼び出すインスタンスメソッドを定義するだけで良い。

簡単な例

プロジェクトフォルダ下のControllersフォルダに<コントローラ名>Controller.csと言う形でファイルを作成すると、”http://・・・/UserInfo”というURLを持つWeb APIが作成される。

UserInfoController.cs

namespace WebAPITest.Controllers
{
    [ApiController] // APIコントローラであることを宣言
    // ルーティングを設定
    // この例では、http://・・・/UserInfo [controller]はクラス名から
    // Contrllerを取り除いたものに置換される
    // 仮想ディレクトリの階層を変える事も可能。例えば、apiの下
    // (http://・・・/api/UserInfo)としたければ、
    // [Route("api/[controller]")]
    // コントローラ名と全く異なるURLにする事も可能
    [Route("[controller]")]
    public class UserInfoController : ControllerBase
    {
        // パラメータ無しGETの処理
        // http://・・・/UserInfo/
        [HttpGet]
        public List<UserInfoItem> GetUserList()
        {
            // ユーザー情報リストを取得
            var lst = ・・・.ToList();
            return lst; // 自動的にJSONに変換される
        }
        // パラメータidありの処理。
        // 例 GET http://・・・/UserInfo/5
        [HttpGet("{id}")]
        public UserInfoItem GetUserInfo(int id)
        {
              // 指定されたIDを持つユーザー情報を取得
              var u = ・・・・.FirstOrDefault();
              if (u == null) {
                  // ControllerBaseに定義されている。
                  // HTTPヘッダ Status 404: Not Foundを返す
                return NotFound();
              } else {
                  return u; // 自動的にJSONに変換される
              }
          }
    }
}

この例ではGETリクエストのみだが、POSTやPUT,DELETEも実装可能。

例えば、[HttpDelete(“{id}”)]のような属性を付けると、

DELETE /UserInfo/5

のようなURLをハンドリングするメソッドとなる。
(通常は削除要求処理を実装だよね)

以上のようにかなり簡単にRESTful Web APIが実装できる。

詳しくは下記URLを参照
https://docs.microsoft.com/ja-jp/aspnet/core/web-api/?view=aspnetcore-3.1

カテゴリー: asp.net core, C#, dotnetcore, 技術系 | コメントする

blazor wasmとSignalR

blazor wasm版でSignalRを使用する場合に注意しなくてはならない事。
wasm版はバックエンドとしてブラウザのhttp送受信機能を使用するので、HUB側とドメインが異なる場合にHUB側でCORS設定をしておかないとHubConnection.StartAsyncを呼び出した時点で実行時エラーとなる。

どうやら、ヘッダに関しての物らしく、以下のようにAllowAnyHeader()を追加したところ、接続が完了し、クライアント側が正常に動作するようになった。(Originだけではエラー回避できない)

            services.AddCors(options => {
                options.AddPolicy("AllowAnyHostsPolicy",
                    builder => {
                        builder.WithOrigins(・・・)
                        .AllowAnyHeader();
                    });
            });

動かない理由を把握するの少々手こずったので、メモ。

カテゴリー: asp.net core, Blazor, C#, dotnetcore, SignalR, 技術系 | 3件のコメント

blazor WASM版をIISへ公開

blazor WASM版のIISへ公開するには、まず、URL Rewriteモジュールの導入が必要。導入されていない場合はWeb Platform Installer等でURL Rewrite 2.0モジュールをIISへ追加する。

後は、dotnet publishコマンドで作成された、bin\Relase\<フレームワーク>\publish\*.*をIISが認識できるフォルダーへコピーし、アプリケーションとして登録するだけ。

.wasm等のMIME設定はweb.configに含まれておりまた、URLリダイレクトの設定もこのファイルに含まれているため、URLを指定するだけでWASMアプリが動作する。

カテゴリー: asp.net core, Blazor, dotnetcore, Windows | 1件のコメント

asp.net coreアプリをIISへ公開する

asp.net coreアプリをWindows Serverの標準HTTPDである、IIS(Internet Information Server)へ公開する手順を調べて、実際に公開してみた。(Visual Studioではなく、手動)

まず、IISが動作するサーバに Windows Hosting Bundleをインストールする必要がある。dotnet core 3.1の場合は、このページのWindows Hoting Bundleをダウンロードし、インストール。

次に、asp.net core用のアプリケーションプールを作成。
「マネージコードなし」に指定するのを忘れずに。

後は、IISがアクセス可能なディレクトリを作成して、そこに、

dotnet publish <プロジェクト名> --configuration Release

で作成したファイル(bin\Release\<フレームワーク>\publish\*.*)をコピー。

そのディレクトリをIISのアプリケーションとして登録。
この時、先程作成した、アプリケーションプールを指定する。

これで、公開は完了。
ただし、一度アプリが動作すると、一部ファイルがオープンされっぱなしになるようで、再度コピーしようとすると、netcoreが使用中とのメッセージが出力される事があるが、runtime dllなので無視しても大丈夫だと思うけど、心配ならば、一度アプリケーションプールを再起動してからコピーすれば良い。

カテゴリー: asp.net core, Windows, 技術系 | コメントする