第12号(2024年04月12日)

Entity Framework CoreとJSON取込

はじめに

今回は、Entity Framework Core を使用し、JSONファイルの取込を行いました。
JSONファイルの読込、データベースへの登録方法について紹介いたします。

Entity Framework Core について

Entity Framework Core は、Microsoft社によって開発されたオブジェクトリレーショナルマッパー(O/RM)です。
オープンソースで、プラットフォームに依存しておらず、Windows、macOS、Linuxで動作するアプリケーションの開発を行えます。
SQL Server、MySQL、PostgreSQL、SQLite などさまざまなデータベースシステムをサポートしています。アプリケーション開発時にはNuGet
パッケージでそれぞれのデータベースプロバイダーをインストールする必要があります。
また、LINQ を使用し、データベースにデータを問い合わせます。LINQ はSQLデータベース、配列、List、XML文書などさまざまデータソースに
対する操作を最小限のコードで実行できます。C#の場合、「using System.Linq;」で参照することで、LINQ を使用できます。

環境について

Windows 10
Visual Studio 2022
Microsoft SQL Server Management Studio 2019
言語:C#

Entity Framework Core の入手

Entity Framework Core を使用するために、NuGetパッケージをインストールする必要があります。
Visual Studio 2022 を起動し、.NETアプリケーションのプロジェクトを作成します。
[ツール]>[NuGetパッケージマネージャー]>[パッケージ マネージャー コンソール]を開きます。
今回は、SQLServerを使用するので、以下のコマンドを実行します。
        Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.Tools
    

JSONファイル取込

⓪登録するデータベースについて
テーブル項目は以下の通りとします。

①Modelの作成
クラスを使用し、Modelにデータベースの内容を定義します。
今回は、「ProductList.cs」とします。
        public class ProductList
{
    [Key]
    public required string ID { get; set; }
    public string ProductName { get; set; }
    public int Stock { get; set; }
    public DateTime? updatetime { get; set; }
}
    
②データコンテキストの作成
System.Data.Entity.DbContext から継承し、データコンテキストを作成します。
データベースとのセッションを表し、データベースとのやり取りができるようになります。
今回は、「DataContext.cs」とします。
        public class DataContext : DbContext
{
    public DbSet<ProductList> T_ProductList { get; set; }

    public DataContext(DbContextOptions<DataContext> options) : base(options)
    {
    }
}
    
③JSONファイル読込準備
以下のコマンドを実行し、「Newtonsoft.Json」をインストールします。
「using Newtonsoft.Json.Linq;」を参照してJArrayクラスを使い、ファイル内のデータを配列に変換するためです。
        Install-Package Newtonsoft.Json
    
④ファイルデータの読込とデータベースへの登録
今回は、以下のようなJSONファイルを取り込むものとします。
        [
{
    "ID": "000001",
    "ProductName": "商品AAA",
    "Stock": "100"
},
{
    "ID": "000002",
    "ProductName": "商品BBB",
    "Stock": "200"
},
{
    "ID": "000003",
    "ProductName": "商品CCC",
    "Stock": "300"
},
{
    "ID": "000001",
    "ProductName": "商品AAA",
    "Stock": "500"
},
{
    "ID": "000004",
    "ProductName": "商品CCC",
    "Stock": "200"
},
{
    "ID": "000005",
    "ProductName": "商品AAA",
    "Stock": "200"
}
]
    
なお、ファイル内に同じデータがある場合は最上位のデータを登録、データベースに既に登録されている場合はデータを更新するものとします。
取込結果は以下のようになります。

取込部分のソース全体は以下の通りです。
        var builder = new DbContextOptionsBuilder<DataContext>();
builder.UseSqlServer("接続文字列");
builder.EnableSensitiveDataLogging();
builder.EnableDetailedErrors();

using (var _context = new DataContext(builder.Options))
{
    List<ProductList> lstProductList = new List<ProductList>();

    string DirectoryPath = "C:\\Import";
    string FileName = "importfile.json";
    string FilePath = Path.Combine(DirectoryPath, FileName);

    Encoding encording = Encoding.UTF8;

    using (StreamReader reader = new StreamReader(FilePath, encording))
    {
        string allLine = reader.ReadToEnd();

        JArray jArray = JArray.Parse(allLine);
        int intImportCnt = 0;
        bool bolAdd;
        foreach (JObject jsondata in jArray)
        {
            ProductList? PLfile = lstProductList.FirstOrDefault(x => x.ID == jsondata["ID"].ToString());

            if (PLfile == null)
            {
                bolAdd = false;

                ProductList? PL = _context.T_ProductList.Find(jsondata["ID"].ToString());
                if (PL == null)
                {
                    PL = new ProductList()
                    {
                        ID = jsondata["ID"].ToString()
                    };

                    bolAdd = true;
                }

                PL.ProductName = jsondata["ProductName"].ToString();
                PL.Stock = int.Parse(jsondata["Stock"].ToString());
                PL.updatetime = DateTime.Now;

                if (bolAdd)
                {
                    _context.Add(PL);
                }
                else
                {
                    _context.Update(PL);
                }

                lstProductList.Add(PL);
                intImportCnt++;
            }
        }
    }
    _context.SaveChanges();
}
    
以下の部分では、データベースへの接続・データコンテキストのインスタンスを生成しています。
アプリケーションの種類などによりさまざまな書き方があるので、適宜変更してください。
        var builder = new DbContextOptionsBuilder<DataContext>();
builder.UseSqlServer("接続文字列");
builder.EnableSensitiveDataLogging();
builder.EnableDetailedErrors();

using (var _context = new DataContext(builder.Options))
{
    //取込処理
}
    
以下の部分では、ファイルデータの読込を行うための設定をしています。
System.IO.StreamReaderクラスを使用し、ファイルデータの読込をします。
今回は、「C:\\Import」に「importfile.json」ファイル(文字コード:UTF-8)が入っているものとします。
        List<ProductList> lstProductList = new List<ProductList>();   //登録済データの格納用

string DirectoryPath = "C:\\Import";                        //取込ファイルがあるフォルダ(任意)
string FileName = "importfile.json";                        //取込ファイル名(任意)
string FilePath = Path.Combine(DirectoryPath, FileName);    //取込ファイルパス

//ファイルの文字コードを設定
Encoding encording = Encoding.UTF8;

//ファイルのデータ読込
using (StreamReader reader = new StreamReader(FilePath, encording))
{
    //データチェックや登録処理
}
    
以下の部分では、ファイルデータの読込とデータベースへの登録をしています。
リストやデータベースへの問い合わせにはLINQを使用しています。データコンテキストは使い終わったら、破棄する必要があります。
        //ファイルのデータ読込
using (StreamReader reader = new StreamReader(FilePath, encording))
{
    string allLine = reader.ReadToEnd();    //すべての入力データを読込

    JArray jArray = JArray.Parse(allLine);  //配列に変換
    int intImportCnt = 0;                   //取込数
    bool bolAdd;                            //追加か更新か

    foreach (JObject jsondata in jArray)
    {
        //ファイル内に重複するデータがあるか
        ProductList? PLfile = lstProductList.FirstOrDefault(x => x.ID == jsondata["ID"].ToString());

        if (PLfile == null)     //重複データなし
        {
            bolAdd = false;     //初期化

            //データベース内に重複するデータがあるか
            ProductList? PL = _context.T_ProductList.Find(jsondata["ID"].ToString());
            if (PL == null)
            {
                //新規登録
                PL = new ProductList()
                {
                    ID = jsondata["ID"].ToString()
                };

                bolAdd = true;  //追加フラグ
            }

            //ID以外のデータをセット
            PL.ProductName = jsondata["ProductName"].ToString();
            PL.Stock = int.Parse(jsondata["Stock"].ToString());
            PL.updatetime = DateTime.Now;

            if (bolAdd)
            {
                    //追加
                    _context.Add(PL);
            }
            else
            {
                //更新
                _context.Update(PL);
            }
            //登録データを格納
            lstProductList.Add(PL);

            intImportCnt++; //取込数カウントアップ
        }
    }
}

//データベースを変更する
_context.SaveChanges();
    
追加のとき、「_context.T_ProductList.Add(PL);」のようにテーブル名を指定していません。これは直前でデータベース内の重複チェックを行う
とき「_context.T_ProductList.Find(…);」を実行しているためです。データコンテキスト内で「T_ProductList」が追跡されている状態なので、
「_context.Add(PL)」とすることができます。
また、追加の「_context.Add(PL)」をした段階では、データコンテキスト内(_context)に「PL]が追加された状態です。
「_context.SaveChanges()」を実行することで、INSERT文が発行され、データベースに反映されます。トランザクションもこのときに開始されま
す。更新や削除なども同様です。

まとめ

今回は、Entity Framwork Coreを使用し、JSONファイルの取り込みを行いました。Entity Framework CoreとLINQを使用することで、データ
の取得や追加・更新・削除などのデータベースへの問い合わせをSQL文を直接書かずに、C#のコードのみで容易にできます。コードも少ないコードで
書くことができ、見やすくなります。
しかし、非常に複雑なSQLの処理を行うときはコードが複雑になることや発行されるSQL文も複雑になり、正しいSQL文が発行されているかの確認が
わかりにくくなる可能性があるので、直接SQL文を書いた方が効率的なこともあります。
また、JSONファイルの読込については、Jarrayクラスを使用することで、データクラスを作ることなくJSON文字列を配列にすることができました。
ただし、この場合JSONファイル内のKeyがなんでも配列に変換されるので、Keyのチェックをする場合は、処理を別で入れることが必要になります。
一覧へ戻る