redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

ASP.NET MVC で inputタグの type属性を指定する

redwarrior.hateblo.jp

上記を行うと表示は変えられるのですが、実はinputタグの type属性が全て「text」になってしまいます。

これだと日付項目に bootstrap-datepicker を使用する時にinputタグをtype="datetime"で絞り込めないため、その解決方法を調べました。

以下のように、class属性やstyle属性を設定する時と同様にTypeプロパティに値を指定すれば良いようです。

@* EditorForの場合 *@
@Html.EditorFor(model => model.Published, new { htmlAttributes = new { @class = "form-control", Type = "datetime" } })

@* TextBox,TextBoxForの場合 *@
@Html.TextBox("Published", null, new { @class = "form-control", Type = "datetime" })

以上です。

(小ネタ)Entity Framework でタイムアウトの設定方法まとめ

ExecuteSqlCommandでストアドプロシージャを実行した時にタイムアウトが発生してエラーになってしまったので、タイムアウトの時間を変更する方法を調べてみました。

また、デバック実行時にDB接続でタイムアウトがぼちぼち発生して面倒なので、コネクションタイムアウトの方法も調べました。

DBはSQL Serverです。

コマンドタイムアウト

ストアドプロシージャ実行時のタイムアウトの時間を変更する方法。

DbContextクラスを継承したクラスのDatabaseプロパティに設定する。(既定値は30秒)

//コンストラクタで指定する場合
this.Database.CommandTimeout = 60;

EntityFramework(24):落穂ひろい:Gushwell's Dev Notes

SqlCommand.CommandTimeout プロパティ (System.Data.SqlClient)

接続タイムアウト

デバック実行時のタイムアウトの時間を変更する方法。

Web.configのconnectionString属性に追加する。(既定値は15秒)

Connection Timeout=30

[SQL Database] 接続タイムアウトの発生を改善したい – Microsoft SQL Server Japan Support Team Blog

以上です。

SQL Server データベース プロジェクトを使用してDBのリファクタリングを行う

はじめに

明けましておめでとうございます。本年もよろしくお願いいたします。去年はあまり更新できなかったので、今年はもっと増やしていきたいですね。
さて、Visual Studio で作成できるプロジェクトの中には、「SQL Server データベース プロジェクト」なんてものがあります。

これを利用すると、SQLDDL文(Create Table等)やストアドプロシージャをプロジェクトとして管理することが出来ます。
プロジェクトをバージョン管理システムに追加すれば、変更履歴の管理も可能。
DDLの新規追加・編集・削除などプロジェクトに加えた変更を、画面操作でDBに反映する!なんてことも出来てしまう。

以下のブログで、画面つきの詳しい説明がされています。

kkamegawa.hatenablog.jp

2014年の記事だけど、Visual Studio 2017 でも同様に操作可能です。

上記記事でも取り上げていますが、以下の点がとても便利です。

  • プロジェクトを公開して、データベースを作成・更新する

  • テーブルスキーマの比較を行い、同期する

  • 複数の発行先を切り替えることが出来る

これがあれば、Code First Migrations をわざわざ使う必要は無いですね。

DBのリファクタリングを行う

これだけだとタイトルに偽りありになってしまうので、自分の行っているリファクタリング手順を載せておきます。既にDBが存在する前提です。

  1. モデリングツールなどでDBの内容を取り込み、ER図上で編集し、SQL文を出力する

  2. ローカルPCでSQL Serverを実行し、DBを作成してSQLを実行してテーブルを作成する

  3. ローカルPCのDBとデータベースプロジェクトのスキーマ比較を行って、データベースプロジェクトを更新

Entity Framework を使用した開発を行っている場合は、さらに以下を行います。

  1. データベースからのCode First を行い、Models配下のクラス、DBContextクラスを更新する

  2. アプリケーションを起動し、LocalDB の再作成を行う

動作確認を行った後、バージョン管理システムに登録し、テスト環境に反映します。

  1. 変更内容をコミットして、プルリクエストを作成し、レビューを行う

  2. マージされたら、データベースプロジェクトを公開し、テスト環境のDBに反映する

  3. アプリケーションプロジェクトを公開し、テスト環境へデプロイする

以上です。

(小ネタ)Entity Framework でdbo以外のスキーマを使用する

Entity FrameworkのCode FirstでDBを作成すると、テーブルはdboスキーマで作成されます。今回スキーマを分ける必要が出てきたので調べました。非常に簡単に設定できたので小ネタです。

Entity Frameworkでdbo以外のスキーマを使用するには、モデルクラスのクラス宣言で設定するTable属性のSchemaプロパティにスキーマ名を設定する。これだけです。

ソースコードで表現するとこんな感じです。testスキーマに"雑誌"テーブルが作成されます。

[Table("雑誌", Schema = "test")]
public class Journal {
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int ID { get; set; }
}

以上です。

データベースからドメインモデルを作るのも有りな気がしてきた

Entity Framework 6が発表された当時は、Code Firstが盛んに宣伝されていたと思う。Code First以前はデザイナーで操作できると言っても自動生成されるxmlファイルの差分を見るのは辛そうだった。

そこでEntity Frameworkを使い始めた頃はCode Firstを使用していたのだが、ここ最近のプロジェクトではデータベースを先に設計して、そこからドメインモデルを作るやり方で進めていて困らなかったので、それで良い気がしてきた。

データベースからドメインモデルを作るやり方だが、ADO.NET Entity Data Modelの作成時に「データベースからのCode First」を使うと、テーブルに対応したCode Firstのモデルクラスを作成してくれる。

DB設計をしっかりとやり出すと、Entity Framework 6のCode Firstはデータベース構造を100%再現できているわけではないことがわかったので、気になった点を以下に書く。(もしかしたら設定があるのかもしれないが見つけられなかった)

  • カラムの初期値が設定できない。(日付カラムとかで使いたい場合がある)
  • DB初期化の際にモデルのstringに空文字を設定しても、nullと扱われてしまいNot Nullのカラムに設定できない。(設計上、Not Nullカラムに空文字を入れるのはどうなの?というのはひとまず置いておく)
    Required属性にAllowEmptyStringsプロパティがあった。これをtrueにすれば良かった。
  • テーブルに設定されている主キー以外のインデックスは、ドメインモデル作成時に無視される。作成したソースコードを修正して、後から設定することは可能。
  • DBのビューからドメインモデルを作ると、ナビゲーションプロパティが作られない。元がビューなので正しい動きなのだろうが、気になった。

テーブルが変更になったら同じ名前で作成することでクラスが上書きされる。ソースコードのバージョン管理をしていれば、前のバージョンとの比較や元に戻すことも容易である。「データベースからのCode First」を使ったことがない方は一度試してみるのをおススメする。

開発環境:

Windows 10 Pro

VisualStudio 2017 Professional

Entity Framework 6.1.3

ユニットテストでのDBトランザクションのロールバックを共通化する

以前こんな記事を書きましたが、

redwarrior.hateblo.jp

毎回テストメソッドの中で using 文やデータ投入処理を書くのが面倒になってきたので、テストフレームワーク(MSTest)の機能を使って TestInitialize 属性でDB接続、データ投入を行い、TestCleanup 属性でDB切断、ロールバックを行うようにします。

ソースコードは以下のようになりました。

[TestClass]
public class DatabaseAccessTest
{
    private TestContext context;
    private DbContextTransaction transaction;

    [TestInitialize]
    public void SetUp()
    {
        context = new TestContext();
        transaction = context.Database.BeginTransaction(); // トランザクションを使用する

        // Usersテーブルの初期化,他のテーブルは残す
        context.Users.RemoveRange(context.Users); 
        context.SaveChanges();

        // テストデータの追加を記述する
        ...
    }

    [TestCleanup]
    public void TearDown()
    {
        transaction.Dispose(); // 終了時にロールバックする
        context.Dispose();
    }

    [TestMethod]
    public void ConnectionTest()
    {
        // テストの実施を記述する
        ...
    }
}

テストメソッドがスッキリして良い感じです。

開発環境:

Windows 10 Pro

VisualStudio 2017 Professional

Entity Framework 6.1.3

MSTest 1.2

Entity Framework で楽観的同時実行制御

基本的な内容は以下の書籍に書かれているので、今回はもう少し踏み込んだ話です。 www.shuwasystem.co.jp

上記書籍の5-4-4(236ページ)では、バージョン管理用のプロパティ名と付与する属性名の両方をTimestampとしていますが、どちらを指しているのかわかりにくいので以下ではプロパティ名を「RowVersion」としています。

書籍の通りに、コントローラーの引数にRowVersionプロパティを指定して、Models配下のクラス(データモデル)にBindすると楽観的同時実行制御が行われます。

しかし、データモデルとは別に画面表示用クラス(View Model)を用意していて、オブジェクトの詰め替え作業を行っている場合は、中身を理解して行わないと正しく動作しないため、調べた内容を記述します。

newしたオブジェクトは、Updateで自動判定

プログラム上でnewした(Entity Frameworkによって管理されていない)オブジェクトの場合は、rowversion型のプロパティに値を設定しておけば、Update時に自動的に判定して処理してくれます。

Entity Frameworkから取得したオブジェクトは工夫が必要

Entity FrameworkからFind等で取得したオブジェクトの場合は、以下のようにEntryメソッドの返り値のオブジェクトに対して値を設定する必要があります。

var departmentToUpdate = context.Departments.Find(id); //Entity Frameworkで管理されたオブジェクト
context.Entry(departmentToUpdate).OriginalValues["RowVersion"] = rowVersion; //rowVersionはbyte[]型のパラメータ

当然ですが、どちらの場合でもSaveChangesメソッドが呼ばれた時に更新処理が行われます。

以上です。

参考URL: Handling Concurrency with the Entity Framework 6 in an ASP.NET MVC 5 Application (10 of 12) | Microsoft Docs