2010年8月24日火曜日

Microsoft SQL ServerのWindows認証

2010年8月24日
Microsoft SQL Serverでは、SQL Server認証とWindows認証という2種類のセキュリティ認証のいずれかを使用することができます。

・Windows認証の設定
1.ActiveDirectoryを構成している場合、
サーバ:SqlServerではセキュリティ→ログインではWindows認証ユーザを追加(ドメインユーザと同じユーザ名)
クライアント: SqlServerで登録されたドメインユーザでWindowsにログオンし、SqlServerにWindows認証で接続できるようになる
2. ActiveDirectoryを構成していない場合(Workgroup場合)
 サーバ:SqlServerではセキュリティ→ログインではWindows認証ユーザを追加(Windowsユーザ)
クライアント: SqlServerで登録されたWindowsユーザ(アカウントとパスワード一致必要)でWindowsにログオンし、SqlServerにWindows認証で接続できるようになる

注:サーバとクライアントでユーザのパスワードまで同期する必要ある、クライアント・サーバーと紐付けしなければなりません。 ワークグループ環境の場合、一般的にはSQL Server認証を選択することが多いと思います。

2010年8月23日月曜日

新たな機能Visual Studio

2010年8月23日
1.C# 4.0 の dynamic キーワードの使用(VS2010)
参照URL
2.テキストボックスでオートコンプリート(VS2005から)
TextBoxコントロールに文字の一部を入力すると、その入力内容を予測して、
入力される可能性のある項目(=補完候補)の一覧を表示したり、文字の続きを自動的に補完したりする機能である。
AutoCompleteModeプロパティとAutoCompleteSourceプロパティの2つを設定する
AutoCompleteMode:Suggest、Append、SuggestAppend
AutoCompleteSource:FileSystem、HistoryList、RecentlyUsedList、AllUrl、AllSystemSources、FileSystemDirectories、CustomSource、ListItems

2010年8月19日木曜日

エラーシューティング

2010年8月19日
1.制約を有効にできませんでした。行に入力できるのは、Null 以外の値、一意な値、あるいは外部キーですが、この制約の違反が 1 つ以上の行で発生しています。
・メッセージ通りチェックする
・つまりDataSet作成後DBのMaxLengthなんかの設定が変わったときに、DBのMaxLengthより小さいと発生する

2010年8月13日金曜日

SQL文を最速にする11のポイント

2010年8月13日

 たとえ最終的な結果が同じでも,SQL文は書き方一つでパフォーマンスがずいぶんと変わってきます。ここでは,速いSQL文を記述するためのポイントや注意点をいくつか紹介しておきましょう。
●WHEREの左辺で算術演算子や関数を使わない

 WHERE句の左辺に算術演算や関数を指定すると,インデックスが使われません。例えば,
SELECT NAME FROM CUSTOMERS
WHERE SAL - TAX > 1000

とすると,たとえSALフィールドにインデックスが定義されていてもテーブル全体を走査してしまいます。こうした場合は,
SELECT NAME FROM CUSTOMERS
WHERE SAL > TAX + 1000

のように記述すれば良いでしょう。
●「後方一致」検索はなるべく避ける

 インデックスが付加されているフィールドであっても,LIKE '%AAA' のような「後方一致」を指定すると,インデックスを検索せずにデータ部の全表走査が行われます。したがって「後方一致」の使用はなるべく避けるようにしましょう。どうしても必要であるなら,

・何らかの,少量まで絞り込める条件とAND条件で組み合わせる
・複数のフィールドに分割し,少しでも前方・完全一致できる範囲を広げる

といった方法を検討して下さい。
●IS NULL,IS NOT NULLを単独で使わない

 条件を表すWHERE句にIS NULL/IS NOT NULLを指定したときは,インデックスを定義したフィールドであっても,全表走査が行われます。したがって,これらの条件を指定するときは,単独で指定するのではなく,何らかのかなり絞り込める条件を合わせて指定してください。例えば,問い合わせの結果を変更せずに「B = 10」の条件を付加できるなら
…WHERE A IS NULL

とする代わりに
…WHERE A IS NULL AND B = 10

とします。
●SELECT文で「*」を使わない

 レコード長が長いときや,フィールド数が多いときには,すべてのフィールドを表す「*」を指定するのはできるだけ避けて,使用するフィールドだけを指定するようにします。「*」を指定すると,参照系のSQL文では,すべてのフィールドを繰り返してコピーするため,リソースを無駄に使うことになります。最低限度必要なフィールドだけを指定するのが基本です。
●ORはある程度絞り込んでから使う

 論理演算子ORを使用した場合,一応インデックスが使用されるものの,個々の条件が抽出する件数が少ない(数%程度)状態でないと,あまり効果がありません。
●DISTINCTの代りにEXISTSを使う

 SELECT文にDISTINCTを指定すると処理に非常に時間がかかります。DISTINCTを使用するのは極力避けましょう。DISTINCTと同等の結果を得ることのできるSQL文にEXISTSがあります。例えば,
SELECT DISTINCT a.ID1, a.NAME1 FROM
TABLE1 a, TABLE2 b WHERE a.ID1 = b.ID2

のSQL文は,副問い合わせの条件としてEXISTSを指定して
SELECT a.ID1, a.NAME1 FROM TABLE1 a
WHERE EXISTS ( SELECT 'X' FROM
TABLE2 b WHERE a.ID1 = b.ID2)

と書き換えることができます。同様に,NOT INからNOT EXISTSに代替することによってパフォーマンスが向上することもあるので,これも検討してみてください。
●GROUP BY,ORDER BY,HAVINGは注意する

  GROUP BY句,ORDER BY句,HAVING句は,余分なディスク入出力が発生したりディスク領域を使うので,自分もしくはほかのプログラムのパフォーマンスに悪影響を及ぼします。このことを念頭において,使わずに済むならなるべく使わないようにしましょう。
●演算子の組み合わせで速度が変わる

 検索条件に,「>」「<」「=」をANDで組み合わせるときは,指定の仕方によってインデックスの使われ方が異なります。等号と不等号の組み合わせは,等号のみインデックスが使われます。例えば,
SELECT NAME FROM CUSTOMERS
WHERE JOB = 'MANAGER'
AND SAL > 1000

とすると,「JOB = 'MANAGER'」にはインデックスが使われますが,「SAL > 1000」には使われません。また,不等号同士の組み合わせでは,先に指定した条件だけにインデックスが使われます。つまり
SELECT NAME FROM CUSTOMERS
WHERE TAX > 100
AND SAL > 1000

のSQL文では,RDBMSは「TAX > 100」だけにインデックスを使い「SAL > 1000」には使いません。
●テーブルの別名を利用する

 テーブルに別名をつけて,フィールド名にはその別名をつけると,SQL文の解析処理を減らすことができます。例えば,
SELECT ID, NAME FROM CUSTOMERS
WHERE SAL < 1000
よりも,
SELECT a.ID, a.NAME FROM CUSTOMERS a
WHERE SAL < 1000
のほうが高速になります。
●SQL文の表現を統一する

 本文中で述べたように,RDBMSは実行計画をキャッシュに保存しておいて再利用します。ところが,SQL文に定数を直接記述してしまうと,RDBMSは定数値だけが異なるSQL文を別のものと解釈するため,再利用されません。バインド変数を使用して,できる限りSQL文を統一するようにします。また,文字の大小や記述の仕方なども統一しておかないと別のSQL文だと認識されてしまうので,気を付けてください。
●SQL文を簡潔に記述する

 SQL文はなるべく簡潔に記述するようにします。そうすることで,SQL文の処理時間を短縮することができます。

LINQ to SQL備忘録

Entity オブジェクトのトラッキングに関する状態に関しては、次のリンクが参考になります。
オブジェクトの状態と変更の追跡 (LINQ to SQL)
http://msdn.microsoft.com/ja-jp/library/bb386982.aspx

動作確認環境は次の通りです。

  • 実行OS: Windows Vista Enterprise
  • 開発環境 : Visual Studio 2008 Professional
  • .NET 3.5
  • 使用データベース: AdventureWorks

1. 使用する Entity クラスと AdventureWorksDataContext の定義

DataContext に対する処理を簡単にするために、DataContext を継承して、AdventureWorksDataContext というクラスを作成します。Entity クラスとして SalesReason, SalesOrder, Customer を定義しています。今回のサンプルで主に使用するのは SalesReason クラスです。

  1. namespace LINQ2SQL.Entity
  2. {
  3. ///
  4. /// AdventureWorks用のDataContextクラスの作成
  5. ///
  6. public class AdventureWorksDataContext : DataContext
  7. {
  8. public AdventureWorksDataContext(string fileOrServerOrConnection)
  9. : base(fileOrServerOrConnection) { }
  10. public AdventureWorksDataContext(IDbConnection connection)
  11. : base(connection) { }
  12. public AdventureWorksDataContext(string fileOrServerOrConnection, MappingSource mapping)
  13. : base(fileOrServerOrConnection, mapping) { }
  14. public AdventureWorksDataContext(IDbConnection connection, MappingSource mapping)
  15. : base(connection, mapping) { }

  16. ///
  17. /// エンティティの定義
  18. /// 初期時にベースクラス内でリフレクションを介して
  19. /// 初期化される。
  20. ///
  21. public Table SalesOrders;
  22. public Table Customers;
  23. public Table SalesReasons;

  24. }
  25. ///
  26. /// TableアトリビュートにはSystem.Data.Linqへの参照が必要
  27. ///
  28. [Table(Name = "Sales.SalesOrderHeader")]
  29. public class SalesOrder
  30. {
  31. [Column(IsPrimaryKey = true)]
  32. public int SalesOrderID;
  33. [Column]
  34. public DateTime OrderDate;
  35. [Column]
  36. public DateTime DueDate;
  37. [Column(CanBeNull = true)]
  38. public DateTime? ShipDate;
  39. [Column]
  40. public decimal SubTotal;
  41. [Column]
  42. public decimal TotalDue;

  43. [Column]
  44. public int CustomerID;
  45. private EntityRef _customer;
  46. [Association(Storage = "_customer", ThisKey = "CustomerID", IsForeignKey = true)]
  47. public Customer Customer
  48. {
  49. get { return _customer.Entity; }
  50. set { _customer.Entity = value; }
  51. }

  52. }
  53. [Table(Name = "Person.Contact")]
  54. public class Customer
  55. {
  56. public Customer()
  57. {
  58. _salesOrders = new EntitySet();
  59. }
  60. [Column(Name = "ContactID", IsPrimaryKey = true, IsDbGenerated = true)]
  61. public int CustomerID;
  62. [Column]
  63. public string FirstName;
  64. [Column]
  65. public string LastName;
  66. private EntitySet _salesOrders;
  67. [Association(OtherKey = "CustomerID", Storage = "_salesOrders")]
  68. public EntitySet SalesOrders
  69. {
  70. get { return _salesOrders; }
  71. set { _salesOrders.Assign(value); }
  72. }
  73. }
  74. [Table(Name="Sales.SalesReason")]
  75. public class SalesReason
  76. {
  77. public SalesReason() { }
  78. private int _salesReasonID = 0;
  79. [Column(Name="SalesReasonID", Storage="_salesReasonID",IsPrimaryKey=true, IsDbGenerated=true)]
  80. public int SalesReasonID
  81. {
  82. get { return _salesReasonID; }
  83. set { _salesReasonID = value; }
  84. }
  85. private string _name;
  86. [Column(Name="Name", Storage="_name")]
  87. public string Name
  88. {
  89. get { return _name; }
  90. set { _name = value; }
  91. }
  92. private string _reasonType;
  93. [Column(Name = "ReasonType", Storage = "_reasonType")]
  94. public string ReasonType
  95. {
  96. get { return _reasonType; }
  97. set { _reasonType = value; }
  98. }
  99. private DateTime _modifiedDate;
  100. [Column(Name = "ModifiedDate", Storage = "_modifiedDate", IsVersion=true )]
  101. public DateTime ModifiedDate
  102. {
  103. get { return _modifiedDate; }
  104. set { _modifiedDate = value; }
  105. }
  106. }
  107. }

前述したとおり、今回は SalesReason クラスを使用して、SalesReason テーブルにデータの登録、更新、削除を行ってみます。

2. Entity クラスの新規作成、更新、削除

LINQ to SQL では変更の反映は、 DataContext.SubmitChanges() メソッドを呼び出すことで行います。 SubmitChangtes() を呼び出すと、トラッキングサービスにより管理されていた Entity オブジェクトの更新、削除、新規作成処理がまとめて処理されデータベースのテーブルにクエリが発行されます。トラッキングサービスによって認識されている変更された Entity オブジェクトは DataContext.ChangeSet() メソッドを使用することにより、変更、削除、挿入対象の Entity オブジェクトを取得できます。 取得されたオブジェクトの変更内容が SubmitChanges を呼び出したときにデータベースにすべて反映されます ( Unit of Works )。

2.1 新規作成処理

新規作成する場合は、Table.InsertOnSubmit() を使用します。以下がサンプルメソッドです。

  1. ///
  2. /// Insert を行うテスト
  3. ///
  4. public static void InsertSalesReason()
  5. {
  6. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);
  7. SalesReason sr = new SalesReason() { Name = "Reason1", ReasonType = "type1", ModifiedDate = DateTime.Now };
  8. dc.Log = Console.Out;
  9. dc.SalesReasons.InsertOnSubmit(sr);
  10. dc.SubmitChanges();
  11. Console.WriteLine(sr.SalesReasonID.ToString());
  12. }

実行結果は次のようになります。下図で、input enter の上部に SalesReasonID が表示されています。この値は、SalesReason にレコードを新規作成するときに自動で採番される値です。登録時に設定される値は、ColumnAttribute.IsDbGenerated を true に設定しておくことで、登録後に値が設定されます。

2.2 更新処理

更新をする場合は、Entity クラスにデータを更新するだけで、トラッキングサービスにより、更新対象のオブジェクトとして認識されます。以下にサンプルメソッドを定義します。

  1. ///
  2. /// Update を行うテスト. Entity の列に ColumnAttribute.IsVersion=true の
  3. /// プロパティ、フィールドがあるかによって、楽観的排他制御用の where 句の生成方法が変わる
  4. ///
  5. public static void UpdateSalesReason()
  6. {
  7. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);

  8. SalesReason sr = dc.SalesReasons.Single(s => s.SalesReasonID == 11);
  9. sr.Name = "change with isversion";

  10. dc.Log = Console.Out;
  11. dc.SubmitChanges();
  12. }

実行結果は下図のようになります。下図の実行結果は、 SalesReason.ModifiedData に設定されているColumnAttribute.IsVersion を false にした際の実行結果です。作成されたSQL を見ると、WHERE 句にプライマリキー列だけではなく、他のすべての列を条件として列挙していることが確認できます。これは、IsVersion が設定されたプロパティ、フィールドがないと、他のユーザなどによりすでに更新されたレコードに対して、最新ではないデータで更新をさせないようにするための、排他制御を行う際に、すべての列に対して比較を行い、変更が加わっていない場合のみ更新を行うようにSQL が作成されるためです。

1. 使用する Entity クラスと AdventureWorksDataContext の定義 で掲載しているサンプルのように、SalesReason.ModifiedDataにアノテートした、ColumnAttribute.IsVersion を true にした場合は次のようになります。WHERE 句の排他制御用の条件がModifiedDate のみになっていることが確認できます。

2.3 削除処理

削除処理を行う場合は、 DataContext.DeleteOnSubmit() メソッドを使用します。サンプルプログラムを以下のように定義します。

  1. ///
  2. /// Delete を行うテスト
  3. /// Update と同様に Entity クラスのプロパティ、フィールドに
  4. /// ColumnAttribute.IsVersion = true がアノテートされたもの
  5. /// があるかにより、楽観的排他制御用の Where 句が変わる
  6. ///
  7. public static void DeleteSalesReason()
  8. {
  9. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);

  10. SalesReason sr = dc.SalesReasons.Single(s => s.SalesReasonID == 12);
  11. dc.SalesReasons.DeleteOnSubmit(sr);
  12. dc.Log = Console.Out;

  13. dc.SubmitChanges();
  14. }

Entity クラスのリレーションを更新する場合は、次のように直接参照を設定しなおすか、1対多のリレーションがある場合は、Add を呼び出します。

  1. ///
  2. /// リレーションのプロパティ、フィールドを変更する場合
  3. ///
  4. public static void UpdateRelation()
  5. {
  6. DataContext dc = new DataContext(ConnectionString);
  7. Table orders = dc.GetTable();

  8. var query = from o in orders
  9. select o;

  10. SalesOrder s1 = null;
  11. SalesOrder s2 = null;
  12. foreach (var item in orders.Take(2))
  13. {
  14. if (s1 == null)
  15. {
  16. s1 = item;
  17. }
  18. else
  19. {
  20. s2 = item;
  21. }
  22. }
  23. // 関係を更新
  24. Customer c = s1.Customer;
  25. s1.Customer = s2.Customer;
  26. s2.Customer = c;

  27. // 関係を追加
  28. SalesOrder added = new SalesOrder(){ DueDate=DateTime.Now, OrderDate=DateTime.Now.Add(new TimeSpan(1000))};
  29. c.SalesOrders.Add(added);

  30. dc.SubmitChanges();
  31. }

2.4 カスケーディングによる削除、更新

注文書と注文明細のように、注文書を削除した場合に注文明細を削除する(Cascading Delete) 場合や、複数の顧客をもつ担当営業にたいして、別の営業員に変更する(Cascading Update) などを行いたい場合があります。LINQ to SQL では、カスケーディングによる削除、更新はサポートしていませんので、開発者が自身で行う必要があります。

以下が、サンプルです。

  1. ///
  2. /// カスケードデリートを行う場合
  3. ///
  4. public static void CascadeDelete()
  5. {
  6. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);

  7. var query = from c in dc.Customers
  8. where c.CustomerID == 2
  9. select c;

  10. var customer = query.Single();
  11. dc.Customers.DeleteOnSubmit(customer);
  12. dc.SalesOrders.DeleteAllOnSubmit(customer.SalesOrders);
  13. ChangeSet set = dc.GetChangeSet();
  14. // SubmitChanges を呼び出すと削除処理が実行される
  15. //dc.SubmitChanges();
  16. }
  17. ///
  18. /// カスケード更新を行う場合
  19. /// CustomerのIDを更新する場合は、
  20. /// LINQ to SQL はプライマリキーの更新を許可していないので、新しい Customer
  21. /// クラスを作成して関連する SalesOrder の集合を設定しなおす必要がある。
  22. ///
  23. public static void CascadeUpdate()
  24. {
  25. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);

  26. var query = from c in dc.Customers
  27. where c.CustomerID == 2
  28. select c;
  29. Customer oldCustomer = query.Single();
  30. Customer newCustomer = new Customer() { FirstName = "First", LastName = "Last" };
  31. newCustomer.CustomerID = oldCustomer.CustomerID();
  32. newCustomer.SalesOrders = oldCustomer.SalesOrders;
  33. // 省略..そのほかプロパティを設定
  34. dc.Customers.DeleteOnSubmit(oldCustomer);
  35. dc.Customers.InsertOnSubmit(newCustomer);
  36. // DB に反映
  37. //dc.SubmitChanges();
  38. }

2.5 SubmitChanges 後の、Entity オブジェクトのプロパティ、フィールド値のテーブルのカラム値との自動同期について

SubmitChanges メソッドにより更新や新規作成をデータベースに反映する場合、レコードの値がトリガー処理によって変更される場合や自動採番列が存在してデータが新規作成される場合、レコードからデータを読み直すひつようがあります。新規作成、更新処理時にレコードから特定の列を読み直すかは、ColumnAttribute.AutoSync の値のよって指定します。AutoSync は System.Data.Linq.Mapping.AutoSync 列挙型の値を指定します。既定では Default で、その場合、IsDbGenerated が true の場合は、Insert 処理時に列の値が Entity のプロパティ、フィールドに設定されます。ColumnAttribute.IsVersion が true の場合は 更新、新規作成処理時にプロパティ、フィールドに値が設定されます。他の値は Always, Never, OnInsert, OnUpdate があります。詳細な内容については MSDN ドキュメントを参照して下さい。

2.6 Insert, Update, Delete 処理をカスタマイズする

DataContext の SubmitChanges(ConflictMode failureMode) をオーバーライドし、GetChangeSet() メソッドで変更対象の Entity オブジェクトを取得し、カスタム処理を記述すると、データベースへの反映処理をカスタマイズできます。この場合、全体のロジックを変更することになります。特定の Entity クラスに対して、動的に作成されたクエリではなく、ストアドプロシージャを呼びだしたり、監査ログを出力するなどの処理をすることで、新規作成、更新、削除処理をカスタマイズする場合は、各 Entity の名前に対応する特別なシグネチャをもつメソッドを DataContext を継承したクラスで定義することで行えます。

Insert 処理のシグネチャは、 public void InsertType(Type inserted){}, 更新処理のシグネチャは、 public void UpdateType(Type current){}, 削除処理のシグネチャは public void DeleteType(Type deleted){} となります。ここで、Type は Entity クラスの名前となります。SalesReason エンティティクラスに対して、カスタム処理を記述した場合のサンプル処理を以下に掲載します。各メソッドは DataContext を継承した AdventureWorksDataContext に定義されていとします。更新処理時に DataContext からリフレクションを使用して各シグネチャにマッチするメソッドが呼び出されます。

  1. ///
  2. /// 新規作成のカスタマイズ。
  3. /// DataContext からリフレクションを介して呼び出される。
  4. ///
  5. ///
  6. public void InsertSalesReason(SalesReason inserted)
  7. {
  8. // 監査ログ、プロパティの変更等を行える
  9. // 既定の処理ではなく、ストアドを実行する
  10. // ことも可能

  11. // デフォルトの新規作成処理を実行する場合
  12. this.ExecuteDynamicInsert(inserted);
  13. }
  14. ///
  15. /// 更新処理のカスタマイズ
  16. /// DataContext からリフレクションを介して呼び出される。
  17. ///
  18. ///
  19. public void UpdateSalesReason(SalesReason current)
  20. {
  21. // GetOriginalEntityState メソッドを使用しても変更前の状態の
  22. // オブジェクトにアクセス
  23. SalesReason original = SalesReasons.GetOriginalEntityState(current);

  24. int affectedRows = ExecuteCommand("exec dbo.usp_UpdateSalesReason @id={0}, @name={1}"
  25. , original.SalesReasonID, current.Name);
  26. if (affectedRows <>
  27. throw new ChangeConflictException();

  28. // デフォルトの更新処理を実行することもできる
  29. //this.ExecuteDynamicUpdate(current);
  30. }
  31. ///
  32. /// 削除処理のカスタマイズ実行
  33. /// DataContext からリフレクションを介して呼び出される。
  34. ///
  35. ///
  36. public void DeleteSalesReason(SalesReason deleted)
  37. {
  38. // 既定の削除処理
  39. this.ExecuteDynamicDelete(deleted);
  40. }

サンプルプログラムでも記載していますが、既定の新規作成、更新、削除を実行する場合は、それぞれ、 DataContext クラスの ExecuteDynamicInsert, ExecuteDynamicUpdate, ExecuteDynamicDelete メソッドを呼び出します。

カスタムInsert, Update, Delete 処理を実装した場合、排他制御の処理も開発者が責任を持つ必要があります。

2.7 排他制御発生と解決方法

DataContext.SubmitChanges() を呼び出したときに他ユーザによる更新などで、排他例外が発生すると、既定の処理では ChangeConflictException がスローされます。このとき発生したコンフリクトは、DataContext.ChangeConflicts に格納されています。 ChangeConflicts は ObjectChangeConflict インスタンスのコレクションです。ObjectChangeConflict.MemberConflicts には関係するメンバーにリストが格納されています。MemberConflicts プロパティはMemberChangeConflict クラスのインスタンスで、現在の Entity オブジェクトのプロパティ、フィールドの値CurrentValueと、 変更前の値 OriginalValue および、データベースの列値 DatabaseValue のプロパティを保持しています。排他例外が発生したテーブル名を調べる場合は、DataContext.Mapping.GetTable(objectChangeConflict.Object.GetType()) を呼び出すことで、MetaTable クラスにオブジェクトにアクセスして、テーブル名などのメタ情報にアクセスできます。ここで、objectChangeConflict はObjectChangeConflict クラスのインスタンスとします。

排他制御例外が発生した場合は、開発者はどのように例外を解決するかを決定する必要があります。排他制御を解決する場合は、DataContext を破棄してもう一度データを読み直すか、 DataContext.Refresh() メソッドを使用します。DataContext.Refresh メソッドには、 RefreshMode の列挙値を指定します。列挙値によってメモリ上にロードされている Entity オブジェクトを変更します。RefreshMode はKeepChanges, KeepCurrentValue, OverwriteCurrentValues があります。詳細は MSDN ドキュメントを参照して下さい。

排他制御のコンフリクト発生の(Where 句に含めるかの判定)判定に使用されるプロパティ、フィールドとするかどうかは ColumnAttribute の UpdateCheck の値によって決定されます。指定できる値は Always, Never, WhenChanged です。例えば Never を指定すると、排他制御のチェック条件として Where 句に現れなくなります。各値の詳細はMSDN ドキュメントを参照して下さい。ただし、ColumnAttribute.IsVersion に true が設定されているプロパティ、フィールドがある場合は、UpdateCheck の指定は無効になります。

3 トランザクション処理

トランザクション処理は従来の DbTransaction を使用する方法と TransactionScope を使用する方法があります。TransactionScope は DTS (Data Transaction Coordinator) を使用するので、DbTransaction と較べればパフォーマンスが劣る可能性がありますが、オンライン処理で使用する少量のデータの場合はプログラムが比較的単純になる TransactionScope を使用しても問題がないと思います。

  1. ///
  2. /// DbTransaction を使用したトランザクション
  3. ///
  4. public static void DbTransactionVersion()
  5. {
  6. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);

  7. SalesReason sr = dc.SalesReasons.Single(s => s.SalesReasonID == 13);
  8. sr.Name = "change with Transaction";

  9. dc.Log = Console.Out;
  10. using (dc.Transaction = dc.Connection.BeginTransaction())
  11. {
  12. dc.SubmitChanges();
  13. dc.Transaction.Commit();
  14. }
  15. }
  16. ///
  17. /// TransactionScope を使用したトランザクション
  18. /// System.Transactions.dll の参照の追加が必要
  19. ///
  20. public static void TransactionScopeVersion()
  21. {
  22. AdventureWorksDataContext dc = new AdventureWorksDataContext(ConnectionString);
  23. SalesReason sr = dc.SalesReasons.Single(s => s.SalesReasonID == 13);
  24. sr.Name = "change with TransactionScope";

  25. dc.Log = Console.Out;
  26. using (TransactionScope ts = new TransactionScope())
  27. {
  28. dc.SubmitChanges();
  29. ts.Complete();
  30. }
  31. }

4. その他(非接続環境の対応)

SOA 環境でLINQ to SQL を使用するときに、Entity クラスをシリアライズする場合は、DataContractSerializer を使用するようにします。SQL Metal や Visual Studio 2008 のデザイナを使用すれば、DataContract がアノテートされた Entity クラスを自動生成してくれるようになります。シリアライズされた Entity オブジェクトをトラッキングサービスにアタッチするには、 Table.Attach メソッドを使用します。サンプルで掲載した例を使用するとAdventureWorksDataContext.SalesReasons.Attach( salesReasonObject ) のように呼び出します。 Attach のオーバーライド版を使用すると、変更されたオブジェクトとしてシリアライズされた Entity オブジェクトを DataContext にアタッチできます。

Entity クラスからテーブルを作成することができます。AdventureWorksDataContext.CreateDatabase(), DeleteDatabase() を使用してテーブルの作成、削除を行えます。テーブルの作成、削除を行うためには、Entity クラスのプロパティ、フィールドに付与された ColumnAttribute の DbType を設定しておく必要があります。

2010年8月12日木曜日

データベース操作ソース

2010年8月12日

using System;
using System.Data;
using System.Data.OleDb;

class OleDbWrapper : IDisposable {

/// コネクション
OleDbConnection _connection;
/// トランザクション
OleDbTransaction _transaction;
bool _disposed;

///
/// コンストラクタ
///

/// 接続文字列
public OleDbWrapper(string connectString) {
_connection = new OleDbConnection(connectString);
}

///
/// リソースを解放します。
///

///
private void Dispose(bool disposing) {

if (_disposed == false) {
if (disposing) {
//
}
_disposed = true;
}

if (_transaction != null) {
_transaction.Dispose();
_transaction = null;
}

if (_connection != null) {
this.Close();
_connection.Dispose();
_connection = null;
}
}

///
/// リソースを解放します。
///

public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}

///
/// トランザクションを開始します。
///

public OleDbTransaction BeginTransaction() {

try {
_transaction = _connection.BeginTransaction();
} catch (Exception) {
throw;
}
return _transaction;
}

///
/// データベースへの接続を閉じます。
///

public void Close() {
try {
if (_connection != null &&
_connection.State == ConnectionState.Open) {
_connection.Close();
}
} catch (Exception) {
throw;
}
}

///
/// トランザクション処理をコミットします。
///

public void Commit() {
try {
if (_transaction != null) {
_transaction.Commit();
}
} catch (Exception) {
throw;
}
}

///
/// 接続に対して SQL ステートメントを実行し、影響を受けた行数を返します。
///

/// OleDbCommand オブジェクト
/// 影響を受けた行数
public int ExecNonQuery(OleDbCommand command) {

int ret = 0;
try {
command.Connection = _connection;
if (_transaction != null) {
command.Transaction = _transaction;
}
ret = command.ExecuteNonQuery();
} catch (Exception) {
throw;
}
return ret;
}

///
/// クエリを実行し、そのクエリが返す結果セットの最初の行にある最初の列を返します。
///

/// OleDbCommand オブジェクト
/// 結果セットの最初の行の最初の列
public Object ExecScaler(OleDbCommand command) {

Object ret = null;
command.Connection = _connection;
try {
if (_transaction != null) {
command.Transaction = _transaction;
}
ret = command.ExecuteScalar();
} catch (Exception) {
throw;
}
return ret;
}

///
/// データテーブル取得処理
///

/// OleDbCommand オブジェクト
/// DataTableオブジェクト
///
/// パラメータに渡されたコマンドを実行し、データテーブルを取得します。
///

public DataTable GetDataTable(OleDbCommand command) {

DataTable ret = null;
try {
command.Connection = _connection;
if (_transaction != null) {
command.Transaction = _transaction;
}
OleDbDataAdapter adapter = new OleDbDataAdapter(command);
OleDbCommandBuilder builder = new OleDbCommandBuilder(adapter);
ret = new DataTable();
adapter.Fill(ret);

} catch (Exception) {
ret = null;
throw;
}
return ret;
}

///
/// データベース接続を開きます。
///

public void Open() {
try {
_connection.Open();
} catch (Exception) {
throw;
}
}

///
/// トランザクション処理をロールバックします。
///

public void Rollback() {

try {
if (_transaction != null) {
_transaction.Rollback();
}
} catch (Exception) {
throw;
}
}

///
/// データテーブル更新処理
///

/// 更新対象となるデータテーブル
/// OleDbCommand オブジェクト
/// 更新に成功した行数
public int Update(DataTable source, OleDbCommand command) {

int ret = 0;
try {
command.Connection = _connection;
if (_transaction != null) {
command.Transaction = _transaction;
}
OleDbDataAdapter adapter = new OleDbDataAdapter(command);
OleDbCommandBuilder builder = new OleDbCommandBuilder(adapter);
ret = adapter.Update(source);

} catch (Exception) {
throw;
}
return ret;
}
}

2010年8月11日水曜日

SQLに要注意

2010年8月11日
1.外部結合
Oracleでは(+)を使い、SQL2005以前は*=、=*を使いますが、SQL2005以後はサポートしませんので、INNER、LEFT、RIGHT、FULLしかサポートしない。
2.decode
Oracleではdecodeを使いますが、SQL Serverrではサポートしません、代わりにCASEを使う。
例:
SELECT
CompanyInfo.CompanyId,
CompanyInfo.CompanyName,
Contract.ContractContents,
CASE WHEN ChargeInfo.CompanyId IS NULL THEN '未記入' ELSE '担当者情報' END AS ChargeFlg
FROM CompanyInfo INNER JOIN
Contract ON CompanyInfo.ContractId = Contract.ContractId
LEFT JOIN
ChargeInfo ON CompanyInfo.CompanyId = ChargeInfo.CompanyId
WHERE (CompanyInfo.Del IS NULL) OR
(CompanyInfo.Del <> 1)

3.SQL Server 2005 からは、TOP 句の 件数指定部分にパラメータが使えます
DECLARE @cnt INT
SET @cnt = 2

SELECT TOP (@cnt) [No] FROM (
SELECT 1 AS [No]
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
) AS [TMP]

開発際に要注意点

2010年8月11日
1.Formの派生クラスでLoadイベントを使わずにOnLoadイベントをOverrideする

class
MyForm : System.Windows.Forms.Form {
    private void MyForm_Load(object sender, EventArgs e) {
// DoSomething
}
}
ではなく
class MyForm : System.Windows.Forms.Form {
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);

// DoSomething
}
}
これにはいくつかの理由があって、

1. .NETのクラスライブラリのガイドラインにそうしろと書いてある。
2. イベントは基本的に外部に知らせるためのものであり、内部で使うものではない。
3. OnLoadメソッドがLoadイベントに登録されたイベントハンドラを呼び出している。

2.is演算子の否定
if (!(obj is MyObject))

3.画面レイアウトを動的変えたいのは、複数のパネルを使って、実行際にパネル、コントロール
の表示、非表示を変えて達成できる。
パネルの最前面、後ろにするのは:
panel.SendToBack();
panel.BringToFront();
4.Windowsフォームサイズの固定
this.FormBorderStyle=FixedDialog;
MaximumSize、MinimumSizeプロパティの組み合わせて制御できる。

5.コントロールにツールチップを表示する(ToolTip)
参照URL

6.datagridviewのcurrentCellを常に[0,0]にする
gvData.CurrentCell = gvData[0, 0];

7.System.Nullable構造体は.NET 2.0から追加された構造体です

これは、intなどのnullを受け入れる事が出来ない型に対して

nullを許容させるために使用します。

int? id;

値がnullの場合にデフォルト値を割り当てること出来るようになっています。

int realId = id ?? -1;

8.Linq to SQL

・LINQ to SQL データモデルの作成(.dbml)

・データベースからエンティティクラスを作成

・遅延ローディング

・ストアドプロシジャーの使用

・データ更新

LdsBusinessDataContext db = new LdsBusinessDataContext();
int? nullmaxId = (from info in db.ChargeInfo
           select info.CompanyId).Max();
int maxId = nullmaxId ?? 0;
CompanyInfo companyInfo = new CompanyInfo();
companyInfo.CompanyId = maxId + 1;
companyInfo.CompanyName = txtCompanyName.Text;
companyInfo.ContractId = int.Parse(cmbCompanyStatus.SelectedValue.ToString());
db.CompanyInfo.InsertOnSubmit(companyInfo);
db.SubmitChanges();

参照URL

9.TextBoxに数字しか入力できないようにする

・さらにバックスペース文字も許可するには

・IMEのオン、クリップボードからの貼り付けを防ぐ

参照URL

10.データセットにパラメータの設定

参照URL

11.複数のパネル中のコンテナのタブ順を動的設定するには

・パネルでもタブ順を設定する必要ある

・コンテナのタブ順を設定する(TabIndex)

12.注意すべきコントロール
・MaskedTextBox
・DateTimePicker
・MonthCalendar
・NumermicUpDown
・WebBrowser
・RichTextBox
・SplitContainer
・TableLayoutPanel


2010年8月10日火曜日

Microsoft Visual Studio 2008 要注意

2010年8月10日
1.SQLサーバ2005とVS2008の組み合わせで、データベースにアクセスしようとするとエラーが発生してしまい。
エラー:ファイルまたはアセンブリ'Microsoft.SqlServer.ManageMent.Sdk.Sfc, Version=10.0.0.0,・・・'またはその依存関係の1つが読み込めませんでした。指定されたファイルが見つかりません。
解決策:VS2008がSQL Server2008のコンポーネントを使っているので、SQL2008をインストールしないと動かないと。
ダウンロードURL
・Microsoft SQL Server2008 管理オブジェクト
・Microsoft SQL Server2008 Native Client
・Microsoft SQL Server2008 システムCLR型
そして、Visual Studio2008でデータソースを作成しようとすると'Microsoft.VisualStdio.DataDesign.SyncDesigner'エラーが発生し、
解決策:
①SQL Srever 2008 Expresswith Advanced Services のインストール
②Visual Studio2008の再インストール(基本はSQL Serverのバージョン関係なく動くはず。)
原因:SQL Server 2008 関連のアンインストールの際に、Visual Stuido 2008 が必要とするアセンブリが一部損失したのかもしれません
2.接続文字列セキュリティ
・Windows認証の使用(お勧め)
Integrated Security=true/SSPI;
・Persist Security Info=false;->接続を開いた後、そうした情報が破棄される。

2010年8月6日金曜日

Tools紹介

2010年8月6日

1.Tera Term(さくらレンタルサーバ)
Windows向けのターミナルソフト。telnetやsshでネットワーク上の別のコンピュータに接続して操作したり、RS-232C端子などに接続した装置とCOMポートを介して通信することができる。現在はオープンソースとして公開されている。
使い方:crontab -e/-l

2.FFFTP(さくらレンタルサーバ)
とはFTP(ファイル転送)を行うためのツールです。
※現在も高頻度で新バージョンが発表されているFileZillaWinSCPなどのクライアントソフトウェアに乗り換えることが奨めです。

3.Oracle Database SQL Developer(11gでは一緒に搭載する)
Oracle SQL Developerでは、データベース・オブジェクトの参照、作成、編集と削除、SQL文およびスクリプトの実行、PL/SQLコードの編集とデバッグ、データの操作とエクスポート、レポートの表示と作成を行うことができます。
MySQL、Microsoft SQL Server、Microsoft Accessなど、特定の(Oracle以外の)サード・パーティ・データベースのスキーマに接続して、これらのデータベースのメタデータおよびデータを 表示できます。また、サード・パーティ・データベースをOracleに移行できます。

4.Common SQL Environment(CSE)
Oracle、PostgreSQL、MySQLではネイティブに接続することが出来ます。
又、ODBCが利用可能なデータベースはODBC経由で接続することが可能です。

Linuxシステム管理者

2010年8月6日
  1. crontabコマンド:シェルを定期的または指定日時に実行する(backup.sh)
参照URL

.NETマルチスレッドプ

2010年8月6日

1:スレッドの実行と同期
  • 競合状態
  • Monitor.Enter・Monitor.Exitメソッド
  • lock・SyncLockステートメント
  • 静的メソッドの同期
  • ロック専用のオブジェクト
  • MethodImplOptions.Synchronized
  • デッドロック
参照URL

2:マルチスレッド プロシージャのパラメーターと戻り値
参照URL