DLinqで頑張る - かずきのBlog@Hatenaの続きです。
DLinqはちょとした癖がある。
それは、LINQのあるステートメントが実行されたときにSQL文を発行するわけじゃないということです。
その実例については、過去に書いたから置いといて…
はまったらどうなるかを書いてみる。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using DLinqSample2.Properties; using DLinqSample; namespace DLinqSample2 { class Program { static void Main(string[] args) { IEnumerable<Employees> emps = null; using (SqlConnection conn = new SqlConnection(Settings.Default.Edu)) { conn.Open(); EDU edu = new EDU(conn); edu.Log = Console.Out; emps = from emp in edu.Employees select emp; } // ここでコネクションは閉じられる foreach (var emp in emps) // NG { Console.WriteLine(emp.Name); } } } }
これを実行すると下のような結果になる。
ハンドルされていない例外: System.InvalidOperationException: ConnectionString プ ロパティは初期化されていません。 場所 System.Data.SqlClient.SqlConnection.PermissionDemand() 場所 System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection) 以下略
これは、
emps = from emp in edu.Employees
select emp;
この行でSQL文が発行されるんじゃなくて
foreach (var emp in emps) // NG
ここで初めてSQL文が発行されるためです。
この時点では、すでにコネクションが閉じてしまってるので例外になってしまいます。
じゃぁ軽く解決してみます。
empsにアクセスすればSQLが発行されるみたいなので、ToArrayで配列にしちゃいます。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using DLinqSample2.Properties; using DLinqSample; namespace DLinqSample2 { class Program { static void Main(string[] args) { Employees[] emps = null; using (SqlConnection conn = new SqlConnection(Settings.Default.Edu)) { conn.Open(); EDU edu = new EDU(conn); edu.Log = Console.Out; emps = (from emp in edu.Employees select emp).ToArray(); } // ここでコネクションは閉じられる foreach (var emp in emps) // OK { Console.WriteLine(emp.Name); } } } }
これを実行すると下のようになります。
SELECT [t0].[ID], [t0].[Name], [t0].[DeptID] FROM [Employees] AS [t0] SqlProvider\AttributedMetaModel ほげほげ 二郎 三郎 四郎 五郎
ばっちり!
ただ、これで安心してるとまたはまっちゃうかもしれません。
例えば、さっきのプログラムをちょっと変えて部署名も出すようにすると…
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using DLinqSample2.Properties; using DLinqSample; namespace DLinqSample2 { class Program { static void Main(string[] args) { Employees[] emps = null; using (SqlConnection conn = new SqlConnection(Settings.Default.Edu)) { conn.Open(); EDU edu = new EDU(conn); edu.Log = Console.Out; emps = (from emp in edu.Employees select emp).ToArray(); } // ここでコネクションは閉じられる foreach (var emp in emps) // OK { Console.WriteLine(emp.Name); Console.WriteLine(emp.Departments.Name); // NG!! } } } }
部署名表示の部分で例外が飛びます。
理由は、今までとまったく同じです。
ちょっと厄介な感じですね。
これを解決するには…???
こんな手段くらいしか思いつかない…
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using DLinqSample2.Properties; using DLinqSample; namespace DLinqSample2 { class Program { static void Main(string[] args) { Employees[] emps = null; using (SqlConnection conn = new SqlConnection(Settings.Default.Edu)) { conn.Open(); EDU edu = new EDU(conn); edu.Log = Console.Out; emps = (from emp in edu.Employees select emp).ToArray(); foreach (var emp in emps) { // コネクションが生きてるうちにアクセスしておく! Departments dept = emp.Departments; } } // ここでコネクションは閉じられる foreach (var emp in emps) // OK { Console.WriteLine(emp.Name); Console.WriteLine(emp.Departments.Name); } } } }
なんかなぁ。
Open Session in Viewみたいなことをしないといけないのかな?
それは嫌だ!!
結局理想論的には、必要なら必要なデータだけとっとけやっていうアプローチになるのかなぁとか思ったり思わなかったり。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using DLinqSample2.Properties; using DLinqSample; namespace DLinqSample2 { class Program { static void Main(string[] args) { EmpInfo[] emps = null; using (SqlConnection conn = new SqlConnection(Settings.Default.Edu)) { conn.Open(); EDU edu = new EDU(conn); edu.Log = Console.Out; // 必要なもんは必要なだけとっちゃう emps = (from emp in edu.Employees select new EmpInfo { Name = emp.Name, DeptName = emp.Departments.Name }).ToArray(); } // ここでコネクションは閉じられる foreach (var emp in emps) { Console.WriteLine(emp.Name); Console.WriteLine(emp.DeptName); } } } class EmpInfo { public string Name { get; set; } public string DeptName { get; set; } } }