WebSurfer's Home

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

TableAdapter と Transaction

by WebSurfer 2011年12月18日 20:33

Visual Studio を利用してウィザードベースで自動生成する型付 DataSet + TableAdapter を使ってデータベースを更新する際、手動トランザクション処理するにはどうしたらいいでしょうか?(MS-DTC を使う自動トランザクションではなくて)

Visual Studio 2008 から TableAdapter に Transaction プロパティが追加されたので、それと、それ以前からある Connection プロパティを使えば、Visual Studio 2005 以前と比較して手動トランザクションが容易に設定できます。

でも、Visual Studio 2008 以降を使っているなら、そもそも手動トランザクションをかけるコードを自力で書く必要はなくて、自動生成される TableAdapterManager クラスの UpdateAll メソッドを使用すればいいです。

実は知らなかったのですが、TableAdapterManager.UpdateAll メソッドのコードをよく見てみると、そのメソッドの中でトランザクションがかかるようになってました。

もともと、TableAdapterManagerは 階層更新 を実現する ため(即ち、複数のテーブルを、データベース内の制約によって定義される一貫性規則を守って関連レコードの挿入、更新、および削除を制御するため)のものということらしいですが、単一テーブルの場合もトランザクションは有効に働きます。

という訳で、Visual Studio 2008 以降なら TableAdapterManager の UpdateAll メソッドを使うのが正解だと思います。

ちなみに、Visual Studio 2005 以前の場合は Transaction プロパティは自動生成されたコードには定義されません。TableAdapter は partial クラスとして定義されているので、自動生成されたファイルとは別に、Visual Studio 2008 以降で生成されるコードを参考に、自力で Transaction プロパティを定義して使うのがいいと思います。SqlClient の場合は以下のような感じです。

private System.Data.SqlClient.SqlTransaction _transaction;

internal System.Data.SqlClient.SqlTransaction Transaction {
  get {
    return this._transaction;
  }
  set {
    this._transaction = value;
    for (int i = 0; i < this.CommandCollection.Length; i++) {
      this.CommandCollection[i].Transaction = this._transaction;
    }
    if (((this.Adapter != null) 
        && (this.Adapter.DeleteCommand != null))) {
      this.Adapter.DeleteCommand.Transaction = this._transaction;
    }
    if (((this.Adapter != null) 
        && (this.Adapter.InsertCommand != null))) {
      this.Adapter.InsertCommand.Transaction = this._transaction;
    }
    if (((this.Adapter != null) 
        && (this.Adapter.UpdateCommand != null))) {
      this.Adapter.UpdateCommand.Transaction = this._transaction;
    }
  }
}

どうも、Visual Studio 2008 から TableAdapter に Transaction プロパティが追加された理由は、TableAdapterManager.UpdateAll メソッドで手動トランザクションをかけるためのようです。

Tags: ,

ADO.NET

SQL Server Express 更新時のトラブル

by WebSurfer 2011年12月17日 16:01

SQL Server 2008 Express を利用していますが、Service Pack を適用すると、毎回以下のようなエラーメッセージが表示され、接続できなくなるというトラブルに悩まされてきました。

"ユーザー インスタンスのプロセスを起動中のエラーにより、Sql Server のユーザー インスタンスを生成できませんでした。接続は閉じられます。"

次に Service Pack を適用する際また同じ問題が出ると思いますが、そのころには解決策を忘れてしまうので、自分のブログに備忘録として書いておきます。

SQL Server Express のデータファイル

SQL Server Express は上の画像に表示された mdf, ldf ファイルを自動的に作ります。具体的なことは分かりませんが、Service Pack の適用によってこれらのファイルとの整合が取れなくなることが原因のようです。これらを全部削除してしまえば問題は出なくなります。

Vista の場合、フォルダの場所は以下の通りです。

C:\Users\アカウント名\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS

ユーザーインスタンスを使わない(接続文字列の User Instance を False にする)ことでも問題を回避できるそうです。ただ、それは解決にはなっていないですよね。

Tags:

SQL Server

Web アプリケーションプロジェクトと Profile

by WebSurfer 2011年12月4日 16:34

Web アプリケーションプロジェクトで新規ユーザー登録をする際に、ユーザーの漢字の氏名、住所などのユーザープロファイル情報を取得し、ASP.NET プロファイルを使用して保存する方法の紹介です。

プロファイル情報の取得

先の記事 ユーザー登録時のプロファイル情報取得 では、Web サイトプロジェクトを利用しました。その場合は、どのページでも Profile プロパティでプロファイル・データにアクセスできるようになります。

例えば、以下のように、String 型の KanjiName を profile の properties の add 要素 に定義したとします。

<profile>
  <properties>
    <add name="KanjiName" />
  </properties>
</profile>

すると、ProfileBase クラス を継承する ProfileCommon クラスが自動的に作成され、profile の properties の add 要素 に定義されているそれぞれのプ���パティの get/set アクセサが ProfileCommon クラスに追加されます。以下のような感じになると思います。(実際に自動生成されたコードを見たわけではないので想像ですが)

public class ProfileCommon : ProfileBase
{
  public virtual string KanjiName 
  {
    get
    {
      return (String) HttpContext.Current.Profile.
                        GetPropertyValue("KanjiName");
    }

    set
    {
      HttpContext.Current.Profile.
        SetPropertyValue("KanjiName", value);
    }
  }
}

さらに、各ページに、ProfileCommon クラスのインスタンスを取得するため、以下のような Profile プロパティが自動的に追加されます。

protected ProfileCommon Profile
{
  get
  {
    return (ProfileCommon)this.Context.Profile;
  }
}

それゆえ、Web サイトプロジェクトでは、コードは一行も書かなくても、どのページでも Profile.KanjiName プロパティを使ってプロファイル・データにアクセスできるようになります。

しかしながら、Web アプリケーションプロジェクトではそうはいきません。(Web アプリケーションプロジェクトと Web サイトプロジェクトの違いについては MSDN ライブラリ を参照してください)

何故なら ProfileCommon クラスや Profile プロパティは自動的に定義されないからです。

そのあたりのことは、MSDN ライブラリの チュートリアル : Visual Studio の Web サイト プロジェクトから Web アプリケーション プロジェクトへの変換 の「プロファイル オブジェクト コードの変換」のセクションに説明があります。

KanjiName の場合を例に取ると、以下のように手動で ProfileCommon を定義する必要があります。この場合、ProfileCommon は ProfileBase を継承する必要はありません。

public class ProfileCommon
{
  public virtual string KanjiName 
  {
    get
    {
      return (String) HttpContext.Current.Profile.
                        GetPropertyValue("KanjiName");
    }

    set
    {
      HttpContext.Current.Profile.
        SetPropertyValue("KanjiName", value);
    }
  }
}

次に、ProfileCommon クラスのインスタンスの 1 つである Profile を、次のコード例のように、プロファイル システムを使用する必要があるページに追加します。

public partial class _Default : System.Web.UI.Page
{
  ProfileCommon Profile = new ProfileCommon();

  protected void Page_Load(object sender, EventArgs e)
  {
    string kanjiName = Profile.KanjiName;
    if (String.IsNullOrEmpty(kanjiName))
    {
      Label1.Text = "漢字名は設定されていません。";
    }
    else
    {
      Label1.Text = Server.HtmlEncode(kanjiName);
    }            
  }

  protected void Button1_Click(object sender, EventArgs e)
  {
    if (User.Identity.IsAuthenticated)
    {
      Profile.KanjiName = TextBox1.Text;
      Label1.Text = Server.HtmlEncode(Profile.KanjiName);
    }
    else
    {
      Label2.Text = "ログインしていないと設定できません。";
    }
  }
}

なお、web.config に、データベースとのプロファイル・データのやり取りのためプロバイダの設定は必要です。以下の providers の設定は、Visual Studio 2010 が自動的に生成する profile 要素の例です。profile の properties の add 要素に KanjiName を設定する必要はありませんの設定も必要です(2011/12/6 誤記訂正。下の追記参照)。anonymousIdentification の設定は不要です。

<profile>
  <providers>
    <clear/>
    <add name="AspNetSqlProfileProvider" 
      type="System.Web.Profile.SqlProfileProvider" 
      connectionStringName="ApplicationServices" 
      applicationName="/"/>
  </providers>
  <!-- やはり KnajiName の設定は必要 -->
  <properties>
    <add name="KanjiName" />
  </properties>
</profile>

以上の手順で、認証済みユーザーのプロファイルの取得、設定はできるようになります。

ただし、新規ユーザー登録をする際に、ユーザーの漢字の氏名などの情報を取得し、プロファイルを使用して保存する場合は Profile.KanjiName = TextBox1.Text; のようにしてもうまくいきません。

何故なら、その時点ではまだユーザーが認証されていないので、SetPropertyValue メソッドで例外がスローされてしまうからです。

以下のように、ProfileBase.Create メソッド を使って指定したユーザー名のプロファイルのインスタンスを作成し、それを操作するとうまくいきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Profile;

namespace WebApplication1.Account
{
  public partial class CreateUser : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      CreateUserWizard1.ContinueDestinationPageUrl = 
        Request.QueryString["ReturnUrl"];
    }

    protected void CreateUserWizard1_CreatedUser(
      object sender, EventArgs e)
    {
      ProfileBase profile =
        ProfileBase.Create(CreateUserWizard1.UserName, true);
      profile.SetPropertyValue(
        "KanjiName", lastName.Text + " " + firstName.Text);            
      profile.Save();

      FormsAuthentication.SetAuthCookie(
        CreateUserWizard1.UserName, false);

      string continueUrl = 
        CreateUserWizard1.ContinueDestinationPageUrl;
      if (String.IsNullOrEmpty(continueUrl))
      {
        continueUrl = "~/";
      }
      Response.Redirect(continueUrl);
    }
  }
}

---------- 2011/12/6 追記 ----------

先に作ったプロジェクトでは、何故か KanjiName の設定がなくても問題ありませんでした。

しかし、新たに別プロジェクトを作って、同様なコードを試してみたところ、KanjiName の設定がないと Profile.KanjiName プロパティで以下の例外がスローされます。

"System.Configuration.SettingsPropertyNotFoundException: 設定プロパティ 'KanjiName' が見つかりませんでした。"

理由は不明です(調査中)。違いは、先に作ったプロジェクトは .ASPXANONYMOUS クッキーが発行されること(anonymousIdentification は設定してないのに)と、開発マシンの Vista の IIS7 上で動かしていること(新たに作った方は開発サーバー)の 2 点ぐらいです。後者は関係なさそうですが、前者が怪しい感じです。

Tags:

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar