Help us understand the problem. What is going on with this article?

ASP.NET Core 入門8 ASP.NET Core + Entity Framework CoreでDB接続

前回までにASP.NET Core MVC を用いて、リクエスト処理や画面描画について学びました。今回はEntity Framework Coreを用いて、データ永続化するためにDB操作について学んでいきます。

前提

1. このコンテンツを扱うこと

  • ASP.NET Core MVC におけるEF Coreの概要説明
  • ASP.NET Core MVC でEF Core + Linq to EntityからSQL Serverを操作する
  • ASP.NET Core MVC でEF Core + 生SQLからSQL Serverを操作する

2. 環境情報

環境/ソフトウェア 内容
オペレーティングシステム Windows 10 1903
.NET Core SDK 2.1.801
IDE Visual Studio Code 1.38.1
Browser Google Chrome 76.0.3809.132
DB SQL Server LocalDB

3. 事前準備

下記サイトにてSQL ServerのExpressをからインストーラーをダウンロードしてください。
インストール時はLocalDBを選択してください。

SQL Server

SQL Serverを操作するため、下記URLよりSQL Server Management Studioを取得して、インストールすることをお勧めします。

SQL Server Management Studio

4. 前提知識

EF Core + SQL Serverの導入&接続準備

EF Core(正式名:Entity Framework Core)はMicrosoftが.NET Core フレームワークのためのORMフレームワークです。もちろん、他のORMフレームワーク(Hibernate)という選択肢がありますが、EF Coreのほうが.NET と親和性が高く、効率と生産性に優れています。

1. EF Core + SQLserver Provide導入

SQL Serverへ接続するためのprovideを導入します。

cd ds.Tutorial.web

# .Net Core 2.1のプロジェクトのため、EF coreのバージョンも2.1系に合わせます
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 2.1.11

2. DBとテーブル作成

SQL Management Studioを用いてDBとテーブルを作成します。

データベース作成

CREATE DATABASE dongsu_tutorial;

テーブル作成

USE dongsu_tutorial;
GO

CREATE TABLE [dbo].[user]
([id]    [INT] IDENTITY(1, 1) NOT NULL, 
 [name]  [NVARCHAR](255) NULL, 
 [age]   [INT] NULL, 
 [hobby] [NVARCHAR](500) NULL, 
 CONSTRAINT [PK_user] PRIMARY KEY CLUSTERED([id] ASC)
 WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
ON [PRIMARY];
GO

3. 接続文字列を作成

ds.Tutorial.web配下にappsettings.jsonファイルを作成して、以下を記述します。

{
    "ConnectionStrings": {
        "tutorial_db": "Server=(localdb)\\mssqllocaldb;Database=dongsu_tutorial;Trusted_Connection=True;"
    }
}

4. EntityとDbContextを作成

Entity作成

Models配下にUserEntity.csを作成します。

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ds.Tutorial.web.Models {
    [Table ("user")]
    [Serializable]
    public class UserEntity {
        // 主キー
        [Key]
        // インクリメント
        [DatabaseGenerated (DatabaseGeneratedOption.Identity)]
        // カラム名
        [Column ("id")]
        public int Id { get; set; }

        [Column ("name")]
        public string Name { get; set; }

        [Column ("age")]
        public int Age { get; set; }

        [Column ("hobby")]
        public string Hobby { get; set; }
    }
}

DbContext作成

ds.Tutorial.web配下Repositoriesのフォルダを作成し、その中にTutorialDbContext.csを作成します。

using ds.Tutorial.web.Models;
using Microsoft.EntityFrameworkCore;

namespace ds.Tutorial.web.Repositories {
    public class TutorialDbContext : DbContext {

        public TutorialDbContext () { }

        public TutorialDbContext (DbContextOptions<TutorialDbContext> options) : base (options) { }

        protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) { }

        public DbSet<UserEntity> Users { get; set; }

    }
}

Startup.csを修正

Startup.csでDB接続文字列からDI注入を用いてDbContextを生成できるように修正します。

using ds.Tutorial.web.Common;
using ds.Tutorial.web.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ds.Tutorial.web {
    public class Startup {

        public Startup (IConfiguration configuration) {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices (IServiceCollection services) {
            // MVCモジュールの導入
            services.AddMvc ();

            // DB接続コンテキスト
            services.AddDbContext<TutorialDbContext> (options =>
                options.UseSqlServer (Configuration.GetConnectionString ("tutorial_db"))
            );

            // 以下省略

EF Core + Linq to Entityの操作方法

1. リポジトリ作成

ds.Tutorial.web配下RepositoriesのフォルダにDB接続クラスを作成します。

using System;
using System.Collections.Generic;
using System.Linq;
using ds.Tutorial.web.Models;

namespace ds.Tutorial.web.Repositories {
    public class TutorialRepository {
        private TutorialDbContext _dbContext;

        public TutorialRepository (TutorialDbContext dbContext) {
            _dbContext = dbContext;
        }

        /// <summary>
        /// 新規作成
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public int Create (UserEntity user) {
            using (_dbContext) {
                _dbContext.Users.Add (user);
                return _dbContext.SaveChanges ();
            }
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public int Update (UserEntity user) {
            using (_dbContext) {
                var userForm = _dbContext.Users.FirstOrDefault (x => x.Id == user.Id);
                userForm.Name = user.Name;
                userForm.Age = user.Age;
                userForm.Hobby = user.Hobby;
                return _dbContext.SaveChanges ();
            }
        }

        /// <summary>
        /// 削除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Delete (int id) {
            using (_dbContext) {
                var userForm = _dbContext.Users.FirstOrDefault (x => x.Id == id);
                _dbContext.Users.Remove (userForm);
                return _dbContext.SaveChanges ();
            }
        }

        /// <summary>
        /// IdからUserを取得する
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public UserEntity GetOneById (int id) {
            using (_dbContext) {
                return _dbContext.Users.FirstOrDefault (x => x.Id == id);
            }
        }

        /// <summary>
        /// 年齢からUser一覧を取得する
        /// </summary>
        /// <param name="age"></param>
        /// <returns></returns>
        public List<UserEntity> GetAllByAge (int age) {
            using (_dbContext) {
                return _dbContext.Users.Where (x => x.Age == age).ToList ();
            }
        }

        /// <summary>
        /// 年齢から名前一覧を取得する
        /// </summary>
        /// <param name="age"></param>
        /// <returns></returns>
        public List<string> GetNamesByAge (int age) {
            using (_dbContext) {
                return _dbContext.Users.Where (x => x.Age == age).Select (x => x.Name).ToList ();
            }
        }

        /// <summary>
        /// ページング
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="page"></param>
        /// <returns></returns>
        public List<UserEntity> GetAllPaging (int pageSize, int page) {
            using (_dbContext) {
                return _dbContext.Users.Skip (pageSize * (page - 1)).Take (pageSize).ToList ();
            }
        }

        /// <summary>
        /// トランザクション処理 0歳以下のUserの年齢を0に変更する
        /// </summary>
        /// <returns></returns>
        public int FixAge () {
            using (_dbContext) {
                using (var transaction = _dbContext.Database.BeginTransaction ()) {
                    try {
                        var userListForm = _dbContext.Users.Where (x => x.Age < 0);
                        foreach (UserEntity user in userListForm) {
                            user.Age = 0;
                        }
                        var count = _dbContext.SaveChanges ();
                        transaction.Commit ();
                        return count;
                    } catch (Exception ex) {
                        transaction.Rollback ();
                        return 0;
                    }
                }
            }
        }

    }
}

2. テスト用のAPI Controllerの作成

ControllersフォルダにEfCoreController.csを作成します。

using ds.Tutorial.web.Models;
using ds.Tutorial.web.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace ds.Tutorial.web.Controllers {
    public class EfCoreController : Controller {
        private TutorialRepository _repository;

        public EfCoreController (TutorialRepository repository) {
            _repository = repository;
        }

        public IActionResult Create (UserEntity user) {
            var message = _repository.Create (user) > 0 ? "ok" : "ng";
            return Json (new { Message = message, User = user });
        }

        public IActionResult Update (UserEntity user) {
            var message = _repository.Update (user) > 0 ? "ok" : "ng";
            return Json (new { Message = message, User = user });
        }

        public IActionResult Delete (int id) {
            var message = _repository.Delete (id) > 0 ? "ok" : "ng";
            return Json (new { Message = message });
        }

        public IActionResult GetOneById (int id) {
            var user = _repository.GetOneById (id);
            return Json (new { User = user });
        }

        public IActionResult GetAllByAge (int age) {
            var users = _repository.GetAllByAge (age);
            return Json (new { User = users });
        }

        public IActionResult GetNamesByAge (int age) {
            var names = _repository.GetNamesByAge (age);
            return Json (new { Names = names });
        }

        public IActionResult GetAllPaging (int pageSize, int page) {
            var users = _repository.GetAllPaging (pageSize, page);
            return Json (new { Users = users });
        }

        public IActionResult FixAge () {
            var count = _repository.FixAge ();
            return Json (new { FixCount = count });
        }

    }
}

3. 疎通テスト

Startup.csにてrepositoryを注入

public void ConfigureServices (IServiceCollection services) {
    // 省略

    // リポジトリ
    services.AddTransient<TutorialRepository> ();

}

アプリ起動&テスト

アプリを起動し、以下のURLに対してアクセスを行う。

API ルート
登録 /efcore/create?name=dongsu&age=18&hobby=coding
削除 /efcore/delete?id=2
更新 /efcore/update?id=1&name=dongsu&age=30&hobby=codingOrWorkout
idからユーザーを取得 /efcore/getonebyid?id=1
年齢からユーザー一覧を取得 /efcore/getallbyage?age=30
年齢からユーザー名一覧を取得 /efcore/getnamesbyage?age=31
該当ページングのユーザー一覧を取得 /efcore/getallpaging?pagesize=3&page=1
年齢がマイナス値のユーザーを0に変更 /efcore/fixage

EF Core + 生SQLの操作方法

EF Coreを使う以上、基本Linqを使ってDB操作したほうが生産性高いですが、
要件によって複雑なSQLを書く場合もあります。

生SQLでの操作方法は以下に記述します。

1. リポジトリ作成

RepositoriesのフォルダにTutorialWithSqlRepository.csを作成します。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using ds.Tutorial.web.Models;
using Microsoft.EntityFrameworkCore;

namespace ds.Tutorial.web.Repositories {
    public class TutorialWithSqlRepository {
        private TutorialDbContext _dbContext;

        public TutorialWithSqlRepository (TutorialDbContext dbContext) {
            _dbContext = dbContext;
        }

        /// <summary>
        /// 新規作成
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public int Create (UserEntity user) {
            using (var connection = _dbContext.Database.GetDbConnection ()) {
                connection.Open ();
                var command = connection.CreateCommand ();
                command.CommandText = "insert into [dbo].[user] (name,age,hobby) values (@name,@age,@hobby)";

                command.Parameters.Add (new SqlParameter () {
                    ParameterName = "@name",
                        DbType = DbType.String,
                        Value = user.Name
                });

                command.Parameters.Add (new SqlParameter () {
                    ParameterName = "@age",
                        DbType = DbType.Int32,
                        Value = user.Age
                });

                command.Parameters.Add (new SqlParameter () {
                    ParameterName = "@hobby",
                        DbType = DbType.String,
                        Value = user.Hobby
                });

                var count = command.ExecuteNonQuery ();
                command.CommandText = "select top(1) id from [dbo].[user] order by id desc";
                user.Id = (int) command.ExecuteScalar ();

                return count;
            }
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public int Update (UserEntity user) {
            using (_dbContext) {
                return _dbContext.Database.ExecuteSqlCommand (
                    "update [dbo].[user] set name={0}, age={1}, hobby={2} where id={3}",
                    user.Name, user.Age, user.Hobby, user.Id
                );
            }
        }

        /// <summary>
        /// 削除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Delete (int id) {
            using (_dbContext) {
                return _dbContext.Database.ExecuteSqlCommand (
                    "delete from [dbo].[user] where id = {0}", id
                );
            }
        }

        /// <summary>
        /// IdからUserを取得する
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public UserEntity GetOneById (int id) {
            using (_dbContext) {
                return _dbContext.Users.FromSql ("select id, name, age, hobby from [dbo].[user] where id={0}", id).FirstOrDefault ();
            }
        }

        /// <summary>
        /// 年齢からUser一覧を取得する
        /// </summary>
        /// <param name="age"></param>
        /// <returns></returns>
        public List<UserEntity> GetAllByAge (int age) {
            using (_dbContext) {
                return _dbContext.Users.FromSql ("select id, name, age, hobby from [dbo].[user] where age={0}", age).ToList ();
            }
        }

        /// <summary>
        /// 年齢から名前一覧を取得する
        /// </summary>
        /// <param name="age"></param>
        /// <returns></returns>
        public List<string> GetNamesByAge (int age) {
            using (_dbContext) {
                return _dbContext.Users.FromSql ("select id, name from [dbo].[user] where age={0}", age).Select (x => x.Name).ToList ();
            }
        }

        /// <summary>
        /// ページング
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="page"></param>
        /// <returns></returns>
        public List<UserEntity> GetAllPaging (int pageSize, int page) {
            using (_dbContext) {
                return _dbContext.Users
                    .FromSql ("select id, name, age, hobby from [dbo].[user] order by id offset {0} rows fetch next {1} rows only", pageSize * (page - 1), pageSize)
                    .ToList ();
            }
        }

        /// <summary>
        /// トランザクション処理 0歳以下のUserの年齢を0に変更する
        /// </summary>
        /// <returns></returns>
        public int FixAge () {
            using (_dbContext) {
                using (var connection = _dbContext.Database.GetDbConnection ()) {
                    connection.Open ();
                    using (var transaction = connection.BeginTransaction ()) {
                        try {
                            var command = connection.CreateCommand ();
                            command.Transaction = transaction;
                            command.CommandText = "update [dbo].[user] set age = @age where age<@age";

                            command.Parameters.Add (new SqlParameter () {
                                ParameterName = "@age",
                                    DbType = DbType.Int32,
                                    Value = 0
                            });

                            var count = command.ExecuteNonQuery ();
                            transaction.Commit ();
                            return count;
                        } catch (Exception ex) {
                            connection.Close ();
                            transaction.Rollback ();
                            return 0;
                        }
                    }
                }
            }
        }

    }
}

2. テスト用のAPI Controllerの作成

ControllersフォルダにEfCoreWithSqlController.csを作成します。

using System;
using ds.Tutorial.web.Models;
using ds.Tutorial.web.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace ds.Tutorial.web.Controllers {
    public class EfCoreWithSqlController : Controller {
        private TutorialWithSqlRepository _repository;

        public EfCoreWithSqlController (TutorialWithSqlRepository repository) {
            _repository = repository;
        }

        public IActionResult Create (UserEntity user) {
            var message = _repository.Create (user) > 0 ? "ok" : "ng";
            return Json (new { Message = message, User = user });
        }

        public IActionResult Update (UserEntity user) {
            var message = _repository.Update (user) > 0 ? "ok" : "ng";
            return Json (new { Message = message, User = user });
        }

        public IActionResult Delete (int id) {
            var message = _repository.Delete (id) > 0 ? "ok" : "ng";
            return Json (new { Message = message });
        }

        public IActionResult GetOneById (int id) {
            var user = _repository.GetOneById (id);
            return Json (new { User = user });
        }

        public IActionResult GetAllByAge (int age) {
            var users = _repository.GetAllByAge (age);
            return Json (new { User = users });
        }

        public IActionResult GetNamesByAge (int age) {
            var names = _repository.GetNamesByAge (age);
            return Json (new { Names = names });
        }

        public IActionResult GetAllPaging (int pageSize, int page) {
            var users = _repository.GetAllPaging (pageSize, page);
            return Json (new { Users = users });
        }

        public IActionResult FixAge () {
            var count = _repository.FixAge ();
            return Json (new { FixCount = count });
        }

    }
}

3. 疎通テスト

Startup.csにてrepositoryを注入

public void ConfigureServices (IServiceCollection services) {
    // 省略

    // リポジトリ
    services.AddTransient<TutorialWithSqlRepository> ();
}

アプリ起動&テスト

アプリを起動し、以下のURLに対してアクセスを行う。

API ルート
登録 /efcorewithsql/create?name=dongsu&age=18&hobby=coding
削除 /efcorewithsql/delete?id=2
更新 /efcorewithsql/update?id=1&name=dongsu&age=30&hobby=codingOrWorkout
idからユーザーを取得 /efcorewithsql/getonebyid?id=1
年齢からユーザー一覧を取得 /efcorewithsql/getallbyage?age=30
年齢からユーザー名一覧を取得 /efcorewithsql/getnamesbyage?age=31
該当ページングのユーザー一覧を取得 /efcorewithsql/getallpaging?pagesize=3&page=1
年齢がマイナス値のユーザーを0に変更 /efcorewithsql/fixage

備考

今回作成したソースコードです。

GitHubリポジトリ

では!!( `ー´)ノ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした