WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

EF Core で SQL Server に渡される SQL 文の確認

by WebSurfer 19. February 2021 16:27

開発時に Entity Framework から SQL Server に渡される SQL 文を確認したいことがあると思います。EF6 と違って EF Core ではそれは少々面倒だったのですが、EF Core 5.0 で導入された「シンプルなログ」という機能を使えば EF6 並みに簡単にできるようになったとのことなので試してみました。

EF Core の SQL 文

その「シンプルなログ」機能を使って EF Core が SQL Server に発行した SQL 文を Visual Studio の出力ウィンドウに表示したのが上の画像です。

基本的には DbContextOptionsBuilder.LogTo メソッドを使ってコンテキストクラスにアクセスして EF Core ログを取得し、指定の場所にログを書き出すという操作になるようです。

NuGet パッケージの追加、構成の変更、コードの大幅な書き換えなどは必要なく、Microsoft のドキュメント「シンプルなログ」に書かれているように、コンテキストクラスの OnConfiguring メソッドに一行追加するだけで EF Core ログの取得が可能になります。

具体的には、例えば先の記事「スキャフォールディング機能 (CORE)」でリバースエンジニアリングで作成したコンテキストクラスを例に取ると、以下のように自動生成された OnConfiguring メソッドの中で LogTo メソッドを使って EF Core ログを取得し、それを Debug.WriteLine メソッドで書き出すという一行を追加すれば OK です。

using Microsoft.EntityFrameworkCore;
using MvcCore5App.Models;

namespace MvcCore5App.DAL
{
    public partial class NorthwindContext : DbContext
    {
        public NorthwindContext(DbContextOptions<NorthwindContext> options)
            : base(options)
        {
        }

        public virtual DbSet<Category> Categories { get; set; }
        public virtual DbSet<Product> Products { get; set; }
        public virtual DbSet<Supplier> Suppliers { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // 出力ウィンドウに EF Core ログを表示
            optionsBuilder.LogTo(msg => System.Diagnostics.Debug.WriteLine(msg));

            // ・・・中略・・・
        }

        // ・・・中略・・・
    }
}

上の LogTo メソッドにより、Visual Studio でデバッグ実行した際に EF Core ログが出力ウィンドウに表示され、その中に EF Core から SQL Server に渡される SQL 文が含まれるようになります。

例えば、以下のような Linq to Entities 文をコントローラーのアクションメソッドに書いて Visual Studio でデバッグ実行し、そのアクションメソッドをブラウザから呼び出してから出力ウィンドウを見ると、この記事の一番上の画像の SQL 文が発行されているのが確認できます。

namespace MvcCore5App.Controllers
{
    public class ProductsController : Controller
    {
        private readonly NorthwindContext _context;

        public ProductsController(NorthwindContext context)
        {
            _context = context;
        }

        public IActionResult Index()
        {
            // どのような SQL 文が投げられるかの検証用
            var list = _context.Products.OrderBy(p => p.ProductId).
                                         Skip(20).Take(10).ToList();

            // ・・・中略・・・
        }

        // ・・・中略・・・
    }
}

上に紹介した「シンプルなログ」は開発時限定でログを取ることを目的としているようで、運用時などでのログの取得には別の機能があるそうです。詳しくは Microsoft のドキュメント「ログとインターセプトの概要」を見てください。


最後に、EF6 で SQL Server に渡される SQL 文を確認する方法もついでに書いておきます。下の画像が EF6 のログを出力ウィンドウに書き出して、その中に含まれる SQL 文の部分を示したものです。

EF6 の SQL 文

基本的には EF Core と同様にコンテキストクラスからログを得て Visual Studio の出力ウィンドウに書き出すのですが、それには DbContext.Database.Log プロパティ を以下のように使います。

namespace Mvc5App.Controllers
{
    public class ProductsController : Controller
    {
        private NORTHWINDEntities db = new NORTHWINDEntities();

        public ActionResult Index()
        {
            // 出力ウィンドウに EF6 ログを表示
            db.Database.Log = msg => System.Diagnostics.Debug.Write(msg);

            var list = db.Products.OrderBy(p => p.ProductID).
                                   Skip(20).Take(10).ToList();

            // ・・・中略・・・
        }
    }
}

元々はログを取るための機能で、SQL 文を確認するだけでなく他にもいろいろ情報が取得できるようです。詳しくは Microsoft のドキュメント「データベース操作のログ記録と受信」を見てください。

Tags: , , ,

CORE

ロールに属するユーザー一覧を表示

by WebSurfer 13. February 2021 11:19

ASP.NET Core MVC アプリでユーザー認証・承認を行うのに ASP.NET Core Identity を利用するケースで、ロールにアサインされたユーザー一覧を表示するサンプルを書きます。

ロールに属するユーザー一覧の表示

先の記事「ASP.NET Identity のロール管理 (CORE)」で、管理者がロールの表示・追加・変更・削除を行うためのサンプルを紹介しましたが、それの Details アクションメソッド / ビューに実装してみます。(先の記事では Details は未実装でした)

(1) ビューモデルの追加

コントローラーからビューに渡すモデルとして UsersInRole クラスを追加します。

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

// 追加
using Microsoft.AspNetCore.Identity;
using MySQLIdentity.Areas.Identity.Data;

namespace MySQLIdentity.Models
{
    // ・・・中略(先の記事のコードと同じ)・・・

    // 追加
    public class UsersInRole
    {
        public IdentityRole Role { set; get; }
        public IList<MySQLIdentityUser> Users { set; get; }
    }
}

(2) Details アクションメソッドを追加

先の記事のコントローラー RoleController にアクションメソッド Details を追加します。

using System.Collections.Generic;
using System.Linq;
// ・・・中略(先の記事のコードと同じ)・・・

namespace MySQLIdentity.Controllers
{
    public class RoleController : Controller
    {
        // ・・・中略(先の記事のコードと同じ)・・・

        // 以下のアクションメソッドを追加
        public async Task<IActionResult> Details(string id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var role = await _roleManager.FindByIdAsync(id);
            if (role == null)
            {
                return NotFound();
            }

            var model = new UsersInRole
            {
                Role = role,
                Users = await _userManager.GetUsersInRoleAsync(role.Name)
            };

            return View(model);
        }

        // ・・・中略(先の記事のコードと同じ)・・・
    }
}

(3) ビュー Details.cshtml を追加

@model MySQLIdentity.Models.UsersInRole

@{
    ViewData["Title"] = "Details";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Details</h1>

<h4>Users in Role "@Model.Role.Name"</h4>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Users.First().UserName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Users.First().HandleName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Users.First().PhoneNumber)
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Users)
        {
            <tr>

                <td>
                    @Html.DisplayFor(modelItem => item.UserName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.HandleName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.PhoneNumber)
                </td>
            </tr>
        }
    </tbody>
</table>

<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.Role.Id }) |
    @Html.ActionLink("Back to List", "Index")
</p>

一覧表示画面 Index の Details リンクをクリックし、Member ロールに属するユーザー一覧を表示したのが上の画像です。

Tags: , , ,

CORE

JSON 文字列から指定した name の value を取得

by WebSurfer 11. February 2021 18:12

JSON 形式の文字列 {"name":"value"} から name を指定して value を取得する C# のプログラムのサンプルを書きます。

JSON 文字列から指定した name の value を取得

先の記事「Json.NET の JToken をパース」と「System.Text.Json の JsonElement をパース」で、Json.NET (Newtonsoft.Json) と System.Text.Json を使って JSON 文字列をパースするサンプルを書きました。

先の記事のサンプルの使い道はなさそうですが、その応用でいろいろできそうということで、まず JSON 文字列を JToken (Json.NET) または JsonElement (System.Text.Json) にデシリアライズしたオブジェクトから、name を指定して再帰的に value 値を取得するコードを書いてみました。

それが何の役に立つかというと、例えば JSON 文字列構造が不定なので特定のクラスにデシリアライズできないが、必要な情報のある name は事前に分かっているのでそれに該当する value を取得できれば目的は果たせるというようなケースです。

レアケースかもしれませんけど、ASP.NET Forum でそういう話があったので考えてみました。

JToken (Json.NET) および JsonElement (System.Text.Json) の両方の場合のサンプルコードを以下にアップしておきます。

(1) JToken (Json.NET)

Json.NET (Newtonsoft.Json) の DeserializeObject メソッドで JSON 文字列を JToken クラスのオブジェクトにデシリアライズし、その中から name が "ObjectArray" の value 値を FindJTokenByName メソッドで取得しています。

その結果を Visual Studio 2019 のデバッガで見たのが上の画像です。元になる JSON 文字列はこの記事の下の方に書いてありますのでそちらを見てください。

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;

namespace NewtonsoftJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Users\...\NewtonsoftJson\";
            string file = "TextFile5.txt";
            string jsonText = "";
            using (StreamReader sr = File.OpenText(path + file))
            {
                jsonText = sr.ReadToEnd();
            }

            JToken jtoken = JsonConvert.DeserializeObject<JToken>(jsonText);
            Parse(0, jtoken);
            JToken token = FindJTokenByName(jtoken, "ObjectArray");
        }

        private static void Parse(int padding, JToken jtoken)
        {
            // ・・・略・・・
        }

        private static JToken FindJTokenByName(JToken jtoken, string name)
        {
            if (jtoken is JObject)
            {
                foreach (KeyValuePair<string, JToken> kvp in (JObject)jtoken)
                {
                    if (kvp.Key == name)
                    {
                        return kvp.Value;
                    }
                    else
                    {
                        JToken retVal = FindJTokenByName(kvp.Value, name);
                        if (retVal != null)
                        {
                            return retVal;
                        }
                    }
                }
            }
            else if (jtoken is JArray)
            {
                foreach (JToken jtokenInArray in (JArray)jtoken)
                {
                    JToken retVal = FindJTokenByName(jtokenInArray, name);
                    if (retVal != null)
                    {
                        return retVal;
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }
    }
}

(2) JsonElement (System.Text.Json)

System.Text.Json の Deserialize メソッドで JSON 文字列を JsonElement 構造体のオブジェクトにデシリアライズし、その中から name が "ObjectArray" の value 値を FindElementByName メソッドで取得しています。

using System;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.IO;

namespace ConsoleAppJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Users\surfe...\ConsoleAppJson\";
            string file = "TextFile5.txt";
            string jsonText = "";

            using (StreamReader sr = File.OpenText(path + file))
            {
                jsonText = sr.ReadToEnd();
            }

            JsonElement jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonText);
            Parse(0, jsonElement);
            var jelem = FindElementByName(jsonElement, "ObjectArray");
        }

        private static void Parse(int padding, JsonElement jelem)
        {
            // ・・・略・・・
        }

        // JsonElement は構造体。見つからないと null が返ってくるので
        // 戻り値は JsonElement? として null 許容にした
        private static JsonElement? FindElementByName(JsonElement jelem, string name)
        {
            if (jelem.ValueKind == JsonValueKind.Object)
            {
                foreach (JsonProperty jprop in jelem.EnumerateObject())
                {
                    if (jprop.Name == name)
                    {
                        return jprop.Value;
                    }
                    else
                    {
                        JsonElement? retVal = FindElementByName(jprop.Value, name);
                        if (retVal != null)
                        {
                            return retVal;
                        }
                    }
                }
            }
            else if (jelem.ValueKind == JsonValueKind.Array)
            {
                foreach (JsonElement jelemInArray in jelem.EnumerateArray())
                {
                    JsonElement? retVal = FindElementByName(jelemInArray, name);
                    if (retVal != null)
                    {
                        return retVal;
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }
    }
}

上のコードで使った TextFile5.txt の JSON 文字列は以下の通りです。

{
  "Title": "This is my title",
  "Response": {
    "Version": 1,
    "StatusCode": "OK",
    "Result": {
      "Profile": {
        "UserName": "SampleUser",  
        "IsMobileNumberVerified": false,
        "MobilePhoneNumber": null
      },
      "ObjectArray" : [
          {"Code": 2000,"Description": "Fail"},
          {"Code": 3000,"Description": "Success"}
      ],
      "lstEnrollment": "2021-2-5"
    },
    "Message": {
      "Code": 1000,      
      "Description": "OK"
    }
  },
  "StringArray" : ["abc", "def", "ghi"]
}

Tags: , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  February 2021  >>
MoTuWeThFrSaSu
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

View posts in large calendar