Entity Framework 4.1 Code Firstで接続先をコードで指定する

普通の人は9分9厘Code Firstでの接続先データベースはApp/Web.Configで変更すれば問題ないと思いまが、しかしながら残りの1厘ぐらいはどうしようもなく、コードで接続先を変更しなければいけなかったりすると思います。

ということで、そういう人向けの記事です。以下にある今回の例でやりたいことは指定したSQL Serverのインスタンスに接続し、指定したデータベースがそのインスタンスになければ作成し、存在すればそこにデータアクセしたいということです。

まずはじめにやることはDbContextを継承したクラスにベースのコンストラクタに、データベース名を引き渡すためにコンストラクタを追加することです。以下のコード例は今まで使用してきたコードに対する改造になります。また、今回例のコードを動かす場合はApp/Web.configに書いた接続文字列は消すか、コメントアウトしておいてください。

//コンテキストオブジェクト
public class LogContext : DbContext
{
    public LogContext() : base() { }
    //①ベースに接続先データベース名を渡せるようにコンストラクタを追加
    public LogContext(string dbConnectionString) : base(dbConnectionString) { }
        
    public DbSet Logs { get; set; }
    public DbSet Machines { get; set; }
}

①のコメントの部分がそのコードです。

次にやることは今までのADO.NETでのそれと同様にSqlConnectionStringBuilderを使って接続文字列を作り、SqlConnctionFactoryを作ったら、それをDatabaseクラスのDefaultConnectionFactoryプロパティに登録します。

コードはメインメソッドで追加します。

//②SQL Serverへの接続文字列を作成する。
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "(local)";
builder.InitialCatalog = "EF2";
builder.IntegratedSecurity = true;
//③DatabseクラスにSqlServerのConnectionFactoryを登録します。
SqlConnectionFactory factory = new SqlConnectionFactory(builder.ConnectionString);
Database.DefaultConnectionFactory = factory;

注意点ですが、SqlConnectionStringBuilderで作った接続文字列中のInitialCatlogの設定はSqlConnctionFactoryのコンストラクタに接続文字列を渡した段階では無視されています。どのデータベースに接続するかはファクトリークラスのCreatCollectionメソッドの引数で指定する必要があります。そして、このCreateCollectionメソッドはDbContextのコンストラクタ中で間接的に呼び出されます。

従って、Code Firstの標準でないデータベースに接続する場合には以下のようにDbContextのコンストラクタに接続するデータベースの名前を渡します。

//④接続データベースを"EF2"にする。
using (var context = new LogContext(builder.InitialCatalog)) {
//..
}

以上のようにコードの変更を加えていくことで、コードで接続先を変更することができます。

SQL CEを使用する場合には、SqlConnectionFactoryにかえてSqlCeConnectionFactoryを使用してください。

まぁめんどうなので、素直にApp.Configで。。。

全体コードは以下の通りです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.ComponentModel.DataAnnotations;

namespace EF1
{
    //Logクラスの定義
    public class Log
    {
        //主キーの属性
        [Key]
        public int LogId { get; set; }
        public DateTime time { get; set; }
        public string description { get; set; }
        //こうしておくとリレーションが設定される
        public virtual Machine Machine { get; set; }
    }
    //Machineクラスの定義
    public class Machine
    {
        //主キーの属性
        [Key]
        public int MachineId { get; set; }
        public string name { get; set; }
        //こうしておくとリレーションが設定される。こちらの主キーがLogの外部キーに設定される。
        public virtual ICollection Logs { get; set; }
    }

    //コンテキストオブジェクト
    public class LogContext : DbContext
    {
        public LogContext() : base() { }
        //①ベースに接続先データベース名を渡せるようにコンストラクタを追加
        public LogContext(string dbConnectionString) : base(dbConnectionString) { }
        
        public DbSet Logs { get; set; }
        public DbSet Machines { get; set; }
    }

    
    class Program
    {
        static void Main(string[] args) {

            //②SQL Serverへの接続文字列を作成する。
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
            builder.DataSource = "(local)";
            builder.InitialCatalog = "EF2";
            builder.IntegratedSecurity = true;
            //③DatabseクラスにSqlServerのConnectionFactoryを登録します。
            SqlConnectionFactory factory = new SqlConnectionFactory(builder.ConnectionString);
            Database.DefaultConnectionFactory = factory;
            
            //コードでスキーマ変更が行われた場合に変更をDBに反映するように設定する。
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges());
            //④接続データベースを"EF2"にする。
            using (var context = new LogContext(builder.InitialCatalog)) {
                
                //必要ならばMachineの更新をする。
                Machine Alis;
                Machine Ellen;
                var alis = context.Machines.LongCount(e => e.name == "Alis");
                if (alis == 0) {
                    Alis = new Machine { name = "Alis" };
                    context.Machines.Add(Alis);
                }
                else {
                    Alis = context.Machines.Where(e => e.name == "Alis").First();
                }
                var ellen = context.Machines.LongCount(e => e.name == "Ellen");
                if (ellen == 0) {
                    Ellen = new Machine { name = "Ellen" };
                    context.Machines.Add(Ellen);
                }
                else {
                    Ellen = context.Machines.Where(e => e.name == "Ellen").First();
                }
                int recordsAffected = context.SaveChanges();

                Console.WriteLine("Saved {0} entities to the database.", recordsAffected);
                //Linq式でクエリーもできる。
                var allAlis = from p in context.Machines
                                where  p.name == "ALis"
                                orderby p.name
                                select p;

                foreach (var mac in allAlis) {
                    Console.WriteLine(" - {0}", mac.name);
                }
                //Logの追加
                Console.WriteLine("** Create Log Data **");
                int i = 0;

                for (i = 0; i < 10; i++) {
                    if ((i % 2) == 0) {
                        context.Logs.Add(new Log { Machine = Alis, 
                            time = DateTime.Now, 
                            description = "Description : " + i.ToString() });
                    }
                    else {
                        context.Logs.Add(new Log { Machine = Ellen, 
                            time = DateTime.Now, 
                            description = "Description : " + i.ToString() });
                    }
                }

                context.SaveChanges();
                //追加したLogの表示
                foreach (var log in context.Logs) {
                    Console.WriteLine("{0:yyyy/MM/dd hh:mm:ss.fff} : {1}\t: {2}", 
                        log.time, log.Machine.name, log.description);
                }

                Console.WriteLine("Press any key to exit.");
               
                Console.ReadKey();
            }

        }
    }

}

もし、コンパイラにEF 4.0のアセンブリに参照がねーよばーかって言われたら、とりあえず思いついた好みの呪いの言葉を吐いた後に参照を追加してあげてやってください。

コメントを残す