WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

.NET Framework での Dependency Injection

by WebSurfer 13. April 2023 15:12

ASP.NET Core で Dependency Injection (DI) に使われている Microsoft.Extensions.DependencyInjection 名前空間にあるクラス類は .NET Framework でもバージョン 4.6.1 以降であれば利用できるそうですので、.NET Framework 4.8 のコンソールアプリで試してみました。

先の記事「.NET Core での Dependency Injection」でターゲットフレームワーク .NET 5.0 のコンソールアプリに DI 機能を実装してみましたが、その .NET Framework 4.8 版です。

まず、Visual Studio 2022 のテンプレートを使ってターゲットフレームワーク .NET Framework 4.8 でコンソースアプリを作成し、NuGet から Microsoft.Extensions.DependencyInjection をインストールします。この記事を書いた時点での最新版 7.0.0 をインストールしました。

NuGet でインストール

他の Micosoft.Bcl.AsyncInterfaces などのパッケージは Microsoft.Extensions.DependencyInjection をインストールした時に同時に自動的にインストールされたものです。

検証に使ったコードは以下の通りです。先の記事「.NET Core での Dependency Injection」のものと同じです。

using Microsoft.Extensions.DependencyInjection;
using System;

namespace ConsoleAppDependencyInjection
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();

            services.AddTransient<IOrderRepository, SqlOrderRepository>();
            services.AddSingleton<ILogger, Logger>();
            services.AddScoped<IEventPublisher, EventPublisher>();
            services.AddTransient<CancelOrderHandler>();

            var provider = services.BuildServiceProvider();
            var handler = provider.GetRequiredService<CancelOrderHandler>();

            var orderId = Guid.NewGuid();
            var command = new Order { OrderId = orderId };
            handler.Handle(command);
        }
    }

    public class CancelOrderHandler
    {
        private readonly IOrderRepository repository;
        private readonly ILogger logger;
        private readonly IEventPublisher publisher;

        // Use constructor injection for the dependencies
        public CancelOrderHandler(IOrderRepository repository,
                                  ILogger logger,
                                  IEventPublisher publisher)
        {
            this.repository = repository;
            this.logger = logger;
            this.publisher = publisher;
        }

        public void Handle(Order command)
        {
            this.logger.Log($"Cancelling order {command.OrderId}");
            var order = this.repository.GetById(command.OrderId);
            order.OrderStatus = "Cancelled";
            this.repository.Save(order);
            this.publisher.Publish(order);
        }
    }

    public interface IOrderRepository
    {
        Order GetById(Guid orderId);

        void Save(Order order);
    }

    public class SqlOrderRepository : IOrderRepository
    {
        private readonly ILogger logger;

        // Use constructor injection for the dependencies
        public SqlOrderRepository(ILogger logger)
        {
            this.logger = logger;
        }

        public Order GetById(Guid orderId)
        {
            this.logger.Log($"Getting Order {orderId}");

            // Retrieve from db.・・・のつもり
            var order = new Order
            {
                OrderId = orderId,
                ProductName = "911-GT3",
                OrderStatus = "Ordered"
            };

            return order;
        }

        public void Save(Order order)
        {
            this.logger.Log($"Saving order {order.OrderId}");
            // Save to db.
        }
    }

    public interface ILogger
    {
        void Log(string log);
    }

    public class Logger : ILogger
    {
        public void Log(string log)
        {
            Console.WriteLine(log);
        }
    }

    public interface IEventPublisher
    {
        void Publish(Order order);
    }

    public class EventPublisher : IEventPublisher
    {
        public void Publish(Order order)
        {
            Console.WriteLine($"Publish order {order.OrderId}, " +
                $"{order.ProductName}, {order.OrderStatus}");
        }
    }

    public class Order
    {
        public Guid OrderId { get; set; }

        public string ProductName { get; set; }

        public string OrderStatus { get; set; }
    }
}

実行結果は以下の画像のようになります。先の記事の .NET 5.0 版と同様に期待した結果になっています。

実行結果

Tags: ,

.NET Framework

異種の要素を含む JSON 配列のデシリアライズ

by WebSurfer 5. March 2023 13:14

JSON の配列で string, number, true/false, null, array, object など異種の要素が混ざったものを、デシリアライザに Newtonsoft.Json および System.Text.Json を使って .NET のオブジェクトに変換するとどうなるかを書きます。

デシリアライズ結果

上の画像の前者 result1.Data が Newtonsoft.Json を使った結果、後者 result2.Data が System.Text.Json を使った結果です。

Data は両方とも object[] 型で、その各要素も object 型ですが、その中身が違うところに注目してください。それがこの記事で書きたかったことです。

この例ほど多種の要素が混じっている配列というケースは実際にはないかもしれませんが、"NaN" という文字列と number ぐらいならあるかもしれません。なので、違いを覚えておくと役に立つかもしれないと思って備忘録として残しておくことにしました。

上の画像を表示するのに具体的のどのようにしたかを以下に書いておきます。

まず、元になる JSON 文字列ですが、以下の通りです。

{
  "Header": {
    "Id": 10,
    "Name": "Test Data"
  },
  "Data": [
    "NaN",
    -0.010565,
    true,
    10,
    1.02e2,
    null,
    [ "abc", "def", "ghi" ],
    { "Object": "test" }
  ]
}

Visual Studio の機能を使って上の JSON 文字列から C# のクラス定義を生成すると以下のようになります。やり方は先の記事「JSON 文字列から C# のクラス定義生成」を見てください。

public class Rootobject
{
    public Header Header { get; set; }
    public object[] Data { get; set; }
}

public class Header
{
    public int Id { get; set; }
    public string Name { get; set; }
}

"Data" は object[] 型にデシリアライズするという点に注目してください。

Newtonsoft.Json および System.Text.Json を使って JSON 文字列を上の C# のオブジェクトにデシリアライズします。コードは以下の通りです。ターゲットフレームワーク .NET 7.0 のコンソールアプリを使いました。

string filepath = @"C:\Users\...\json1.json";
string jsonString = "";
using (StreamReader sr = File.OpenText(filepath))
{
    jsonString = sr.ReadToEnd();
}

// Newtonsoft.Json
var result1 = Newtonsoft.Json.JsonConvert
              .DeserializeObject<Rootobject>(jsonString);

// System.Text.Json
var result2 = System.Text.Json.JsonSerializer
              .Deserialize<Rootobject>(jsonString);

上のコードを Visual Studio 2022 でデバッグ実行し、ブレークポイントで止めて変数 result1 と result2 の中身を表示したのがこの記事の一番上の画像です。

Tags: , ,

.NET Framework

.NET の多次元配列の JSON シリアル化

by WebSurfer 14. December 2022 18:06

Newtonsoft.Json のシリアライザなら .NET の多次元配列を JSON 文字列にシリアライズできるという話を書きます。何を今さらと言われそうですが。(汗)

Newtonsoft.Json

System.Text.Json 名前空間のシリアライザは多次元配列の JSON シリアル化をサポートしていないようで、試してみましたが System.NotSupportedException がスローされ "The type 'System.String[,,]' is not supported." というエラーメッセージが出ます。(ジャグ配列ならサポートしていることは確認しました)

なので、調べもしないで Newtonsoft.Json も同じだと思っていたのですが、Json.NET 4.5 Release 8 – Multidimensional Array Support, Unicode Improvements によると 10 年も前から多次元配列のシリアライズをサポートしているとのことです。

Microsoft のドキュメント「Newtonsoft.Json と System.Text.Json を比較して、System.Text.Json に移行する」にもいろいろ書いてありますが、多次元配列のサポートについては言及してないです。「さまざまな型のサポート ⚠」に含むのでしょうか?

実際にコードを書いて、Newtonsoft.Json が多次元配列とジャグ配列の両方のシリアライズ/デシリアライズをサポートしていることは確認しました。以下に検証に使ったコードを載せておきます。.NET Framework 4.8.1 のコンソールアプリです。

using Newtonsoft.Json;
using System;

namespace ConsoleAppArrayJson
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 多次元配列
            var mdArray = new string[30, 12, 31];
            for (int i = 0; i < 30; i++)
            {
                for (int j = 0; j < 12; j++)
                {
                    for (int k = 0; k < 31; k++)
                    {
                        mdArray[i, j, k] = $"e-{i}-{j}-{k}";
                    }
                }
            }

            // ジャグ配列
            var jArray = new string[30][][];
            for (int i = 0; i < 30; i++)
            {
                jArray[i] = new string[12][];
                for (int j = 0; j < 12; j++)
                {
                    jArray[i][j] = new string[31];
                    for (int k = 0; k < 31; k++)
                    {
                        jArray[i][j][k] = $"e-{i}-{j}-{k}";
                    }
                }
            }

            // 多次元配列を JSON 文字列にシリアライズ
            var json1 = JsonConvert.SerializeObject(mdArray);

            // ジャグ配列を JSON 文字列にシリアライズ
            var json2 = JsonConvert.SerializeObject(jArray);

            // json1 == json2 は true になる
            Console.WriteLine($"json1 == json2: {json1 == json2}");

            // JSON 文字列を多次元配列にデシリアライズ
            var array1 = JsonConvert.DeserializeObject<string[,,]>(json1);
            Console.WriteLine($"array1[0,1,2]: {array1[0, 1, 2]}");

            // JSON 文字列をジャグ配列にデシリアライズ
            var array2 = JsonConvert.DeserializeObject<string[][][]>(json2);
            Console.WriteLine($"array2[0][1][2]: {array2[0][1][2]}");
        }
    }
}

上のコードの実行結果は以下のようになります。

実行結果

Tags: , , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  November 2023  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar