WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

VS2015 でユーザーインスタンス利用

by WebSurfer 2018年9月24日 13:57

SQL Server Express のユーザーインスタンスを利用して、Visual Studio Community 2015 の TableAdapter 構成ウィザードで型付 DataSet + TableAdapter を自動生成する際の注意点を書いておきます。

Visual Studio 2010 Professional と同じ手順で作業すると、作業の過程で以下のように、"ファイル xxx.mdf の自動的に名前が付けられたデータベースをアタッチできませんでした。同じ名前のデータベースが既に存在するか、指定されたファイルを開けないか、UNC 共有に配置されています。" というエラーが出て先に進めません。

エラーメッセージ

原因はここまでの作業で生成された接続文字列に User Instance=True が付与されないからです。

接続文字列には AttachDbFilename でアタッチする .mdf ファイルは指定されるのですが、その場合 User Instance=True で明示的にユーザーインスタンスを使うようにする必要があるようです。

(想像ですが、User Instance=True が付与されていないので、既定の(または名前付き)インスタンスにアタッチしようとして、権限の問題で失敗していると思われます。先の記事「DB のアタッチ」参照)

TableAdapter 構成ウィザードで作業を進めていく際、「接続の追加」ダイアログで[詳細設定(V)...]ボタンをクリックすると「詳細プロパティ」ダイアログが表示されますので、そこでユーザーインスタンスを使用するように設定する必要があります。

詳細プロパティ

「詳細プロパティ」で[Data Source]を LocalDB から SQL Server Express に変更するところは気が付きましたが、もう一つ[User Instance]がデフォルトで False になっていて、これを True に変更する必要があることに気が付きませんでした。

ちなみに、Visual Studio 2010 Professional では「詳細プロパティ」の[User Instance]は True に設定されています。

「詳細プロパティ」の設定後、先に進んで TableAdapter 構成ウィザードに戻ったときに接続文字列を確認できます。

接続文字列

上の画像のように User Instance=True となっていれば OK です。後は従前の手順で進めていけば問題なく型付 DataSet + TableAdapter が完成するはずです。

ユーザーインスタンスはすでに非推奨になっていて、最近の開発には LocalDB を利用するので、このような問題に悩むケースはほとんどなさそうですが、忘れないよう備忘録として残しておくことにしました。(笑)

Tags: ,

ADO.NET

Linq to Entities で型の初期化

by WebSurfer 2018年4月30日 16:31

以下のコードを実行すると foreach (var item in list2) のところで System.NotSupportedException がスローされ、

1 つの LINQ to Entities クエリに含まれる構造的に互換性のない 2 つの初期化に、型 'ConsoleAppJoinByLinq2.JoinedList' が指定されています。1 つの型を同じクエリ内の 2 つの場所で初期化することはできますが、両方の場所で同じプロパティが同じ順序で設定されている必要があります。

・・・というエラーメッセージが表示されます。その理由と解決策を書きます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleAppJoinByLinq2
{
  public class JoinedList
  {
    public int ProductID { set; get; }
    public string ProductName { get; set; }
    public int CategoryID { get; set; }
    public string CategoryName { set; get; }
  }

  class Program
  {
    static void Main(string[] args)
    {
      NORTHWINDEntities db = new NORTHWINDEntities();

      var list1 = from c in db.Categories
                  where c.CategoryID == 1
                  select new JoinedList
                  {
                    CategoryID = c.CategoryID,
                    CategoryName = c.CategoryName
                  };

      var list2 = from c in list1
                  join p in db.Products
                  on c.CategoryID equals p.CategoryID
                  select new JoinedList
                  {
                    ProductID = p.ProductID,
                    ProductName = p.ProductName,
                    CategoryName = c.CategoryName
                  };

      foreach (var item in list2)
      {
        Console.WriteLine("{0}, {1}, {2}",
          item.ProductID, item.ProductName, item.CategoryName);
      }
    }
  }
}

上のコードでは、SQL Server のサンプルデータベース Northwind をベースに Visual Studio Communirt 2015 のウィザードを使って作った Entity Data Model (EDM) を使っています。以下の画像を見てください。

Entity Data Model

NORTHWINDEntities は EDM を作ると一緒に自動生成される DbContext クラスを継承したコンテキストクラスです。

Categories と Products は NORTHWINDEntities コンテキストクラスのプロパティで、データベースの当該テーブルを表すエンティティのコレクションを取得・設定するものです。

そして、肝心の話の何故エラーになるかの理由ですが、エラーメッセージの「両方の場所で同じプロパティが同じ順序で設定」という条件が満たされてない、即ち、list1 と list2 のクエリで JoinedList を初期化する際のプロパティの設定が異なるからです。

その前のエラーメッセージの条件「1 つの型を同じクエリ内の 2 つの場所で初期化」には該当しないように見えますが、list1 と list2 のクエリは両方 foreach のところで遅延評価されて、結局「同じクエリ内」ということになるようです。

解決策は、

  1. list1 のクエリに ToList() を適用する(遅延評価されないように)、または、
  2. JoinedList を初期化する際のプロパティの設定を、並び順序を含めて list1 / list2 のクエリで同じになるようにする

・・・です。そうすれば、以下の通り期待した結果が得られます。

実行結果

Tags:

ADO.NET

NULL 条件でのレコード抽出

by WebSurfer 2017年6月18日 18:02

Visual Studio のデータソース構成ウィザードを使って、@IT の記事「Microsoft Visual Studio 2005 による Web アプリケーション構築技法」の「D. テーブルアダプタへのクエリ追加」のセクションに書いてあるように、特定の条件で DataSet / DataTable を生成するメソッドを作ることができます。

以下の画像はサンプルデータベース Northwind の Orders テーブルから ShippedDate が NULL のレコードを抽出して DataGridView に表示したものですが、このようなことができるメソッドを作るにはどうすればよいかということを書きます。

ShippedDate が NULL のレコードを抽出

SELECT クエリで WHERE ShippedDate = @ShippedDate という条件で TableAdapter にメソッドを追加すると、そのメソッドの引数に null が渡された場合は、メソッド内部でパラメータ @ShippedDate に DBNull.Value が代入されるコードが生成されます。

しかしながら、それでは ShippedDate が NULL のレコードを抽出することはできません。ShippedDate が NULL のレコードを抽出するためには WHERE ShippedDate IS NULL とする必要があります。

ではどうすればいいかですが、クエリビルダで元となる SELECT クエリを作成する際、@IsNull_ShippedDate(名前は任意)というような引数に null が渡されたか否かを判定するパラメータを追加し、以下の画像のように WHERE 句を組み立ててそれをベースにメソッドを生成します。

クエリビルダ

これにより、DataTable を返す GetDataByNullableShippedDate メソッド(名前は任意)の例ですが、以下のようなコードが生成されます。(注:自動生成されたコードそのものではなく、読みやすくするため改行等を行っています)

public virtual OrdersDataTable GetDataByNullableShippedDate(
               int? IsNull_ShippedDate, DateTime? ShippedDate)
{
  this.Adapter.SelectCommand = this.CommandCollection[1];
  if (IsNull_ShippedDate.HasValue == true) 
  {
    this.Adapter.SelectCommand.Parameters[0].Value = 
                                (int)IsNull_ShippedDate.Value;
  }
  else
  {
    this.Adapter.SelectCommand.Parameters[0].Value = 
                                                DBNull.Value;
  }

  if (ShippedDate.HasValue == true)
  {
    this.Adapter.SelectCommand.Parameters[1].Value = 
                                 (DateTime)ShippedDate.Value;
  }
  else
  {
    this.Adapter.SelectCommand.Parameters[1].Value = 
                                                DBNull.Value;
  }
  OrdersDataTable dataTable = new OrdersDataTable();
  this.Adapter.Fill(dataTable);
  return dataTable;
}

この GetDataByNullableShippedDate メソッドを以下のように呼び出して DataTable を取得し DataGridView に表示したのが一番上の画像です。

public partial class Form14 : Form
{
  private OrdersTableAdapter adapter = new OrdersTableAdapter();
  private BindingSource bindingSource1 = new BindingSource();
  private NorthwindDataSet.OrdersDataTable table;
        
  public Form14()
  {
    InitializeComponent();

    DateTime? shippedDate = null;

    table = adapter.GetDataByNullableShippedDate(
                shippedDate.HasValue ? 0 : 1, shippedDate);

    bindingSource1.DataSource = table;
    this.dataGridView1.DataSource = bindingSource1;
  }
}    

もちろん、NULL でない特定の日付の ShippedDate のレコードも上のコードで抽出できます。

Tags: ,

ADO.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年12月  >>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar