|
 |
| テクニカルライター
大澤 文孝 |
連載第1回目では、ASP.NETの基礎を簡単に説明しながら、ADO.NETを使ってデータベースに書き込む方法を説明します。
|
|
 |
 |
構築するWebアプリケーション |
 |
Webフォーム |
 |
Webフォームの処理 |
 |
データベースへの書き込み処理 |
 |
まとめ |
| |
|
|
|
|
| 【前書き】 |
| Visual Studio .NETでは、Windowsアプリケーションと同様に、フォーム(Webフォーム)に各コントロールを貼り付けて、ビジュアルな操作で、容易にWebアプリケーションを構築することができます。
しかしビジュアルな操作で、すべてがうまくいくとは限らず、コードが重要になってくる場面も多々あります。
とくにADO.NETを使ってデータベースにアクセスするWebアプリケーションを構築する場合には、その方法が何通りもあり、常にビジュアルな開発が好ましいとは限りません。
そこで、この連載では、Webアプリケーションを実際に作りながら、Webアプリケーションを構築する上での注意点も交えつつ、ADO.NETでのデータベースへのアクセス方法を説明します。
<動作環境>
この連載では、SQL Server 2000と.NET Framework 1.1(Visual
Studio .NET 2003)を用います。
一部、.NET Framework 1.1固有のプロパティやメソッドを利用しているため、.NET
Framework 1.0環境(Visual Studio .NET 2002)で動作させるには、若干のプログラムの変更が必要となることがあります。 |
|
|
| ■構築するWebアプリケーション
この連載では、実際にデータベースを使ったWebアプリケーションを作っていきます。作成するのは、商品の登録をし、その閲覧をするという、ごく簡単なWebアプリケーションです。
●データを格納するデータベースを作る
連載をはじめるにあたり、この連載で用いるデータベースをSQL Server上に作っておくものとします。
データベース名やテーブル名は、どのようなものでもかまいませんが、ここでは、Productsテーブルをもつ、ProductDBというデータベースを作成することにします。
Productsテーブルの列は、表1-1のように定義するものとします。参考までに、表1-1のテーブルを作成するSQL文をリスト1-1に示します。
表1-1 Productsテーブル
| 列名 |
型 |
長さ |
Nullの許容 |
解説 |
| id |
int |
― |
しない |
IDENTITY列。商品ID。主キー |
| productname |
nvarchar |
64 |
しない |
商品名 |
| price |
money |
― |
しない |
価格 |
| stock |
int |
― |
しない |
在庫情報。0=なし、1=あり、2=僅少、3=要問い合わせ |
| comment |
nvarchar |
256 |
しない |
表形式で一覧表示する場合の概説文 |
| detail |
nvarchar |
2048 |
しない |
1つの商品の詳細を表示する場合の解説文 |
| imgdata |
image |
― |
する |
商品画像 |
| createdate |
datetime |
― |
しない |
商品登録日時 |
| lastupdate |
datetime |
― |
しない |
商品最終更新日時 |
|
|
|
リスト1-1 ProductDBデータベースを作成するSQL文
表1-1に示したように、Productsテーブルには、画像イメージを格納するimage型の列を用意しています。
Webアプリケーションの場合には、パフォーマンスを考えると、データベースのテーブル上に画像イメージを格納するのではなく、ファイルとして保存したほうがよいのですが、この連載では、ADO.NETを使ったBLOB型の列へのアクセス例を示すため、敢えて、テーブル上に画像イメージを格納することにします。
|
[TIPS]
画像イメージをファイルとして保存しておけば、クライアントは、“http://サーバー名/画像ファイル名”というURLで直接アクセスできます。すなわちデータベースにアクセスする必要なく、そのまま取り出せるため、パフォーマンスが良いということです。
|
|
●ASP.NETからSQL Serverにアクセスできるようにする
ASP.NETでは、プログラムはASPNETユーザーとして実行されます。そこで、いま作成したProductDBデータベースに対して、ASPNETユーザーがアクセスできるようにしておきます。
具体的には、SQL ServerのEnterprise Managerなどを使って、ASPNETユーザーにProductDBデータベース内のProductsテーブルに対して読み書きの権限を与えます(図1-1)。
図1-1 ASPNETユーザーがProductsテーブルを読み書きできるようにする
|
[TIPS]
図1-1の設定は、SQL ServerにてWindows認証を使う場合のみ必要です。データベースに接続するときに明示的にデータベース接続文字列でユーザー名とパスワードを指定するSQL Server認証(混合モード)を利用する場合には、必要ありません。 |
|
[TIPS]
IISの仮想ディレクトリにおいて、統合Windows認証を使ってユーザー認証をしている場合(Windowsのアカウントを使ってWebアプリケーションにアクセス制限を課している設定をしている場合)には、ASPNETユーザーではなく、実際にアクセスしてきたユーザーのアカウントでWebアプリケーションが実行されます。 |
|
■Webフォーム
では、Visual Studio .NETでWebアプリケーションのプロジェクトを作成し、Webフォームを作っていきます。
ここではまず、データベースに商品を登録するためのproductadd.aspxというWebフォームを作ることにします。
productadd.aspxファイルは、図1-2のように用意することにします(リスト1-2)。
図1-2 productadd.aspxで示されるWebフォーム

※個々のコントロールのプロパティなどの詳細は、リスト1-2を参照してください。
リスト1-2 productadd.aspxファイル
基本的に、図1-2に示したWebフォームは、表1-1に示した「商品名」「価格」「在庫情報」「概説」「解説」「商品画像」のそれぞれの項目を入力できるコントロールを配置したに過ぎませんが、いくつか特別な処理をしています。
●Panelコントロールを使った表示の切り替え
商品を追加するWebフォームでは、(1)ユーザーに入力させるフォームの表示、(2)結果の表示、の2種類の出力が必要となります。
そこでこれらの表示の切り替えをするために、SubmitPanel、InputPanelと名付けた2つのPanelコントロールを用いています。
リスト1-2を参照するとわかりますが、これらは、次のようになっています。
|
<asp:Panel id="SubmitPanel" runat="server"> <!-- SubmitPanelの中身(図1-1の上部)。 結果のメッセージなどが入っている--> </asp:Panel> <asp:Panel id="InputPanel" runat="server"> <!--InputPanelの中身(図1-1の下部)。 各種入力のためのコントロールなどが入っている--> </asp:Panel>
|
ASP.NETでは、WebコントロールのVisibleプロパティを使って、そのWebコントロールをクライアントに送信するかしないかを決めることができます。
たとえば、次のようにすると、InputPanelパネルの中身だけが表示されます。つまり、ユーザーには、図1-3に示すように、入力フォームだけが表示されます。
|
InputPanel.Visible = true SubmitPanel.Visible = false
図1-3 InputPanelパネルの中身だけを表示したとき
それに対して、次のようにすると、SubmitPanelパネルの中身だけが表示されます(図1-4)。
|
InputPanel.Visible = false SubmitPanel.Visible = true
|
図1-4 SubmitPanelパネルの中身だけを表示したとき
このように、Panelコントロールを使って出力したい内容を分け、Visibleプロパティで表示の有無を設定すれば、出力する内容を容易に切り替えることができます。
|
[TIPS]
Panelコントロールの出力は、クライアントに送信するとき、<div>要素に置き換えられます。 |
|
|
●テキストボックスやリストボックス
図1-2に示したように、このWebフォームでは、ユーザーが商品情報を入力できるようにするため、次のコントロールを用いています。
・ProductNameTextテキストボックス
商品名を入力するテキストボックスです。
・PriceTextテキストボックス
価格を入力するテキストボックスです。
・StockListドロップダウンリスト
在庫情報を選択するドロップダウンリストです。
本連載で扱うアプリケーションでは、在庫情報は、表1-2のいずれかの値を使います。
|
表1-2 在庫情報がとりうる値
| 値 |
意味 |
| 0 |
なし |
| 1 |
あり |
| 2 |
僅少 |
| 3 |
要問い合わせ |
そこで、このドロップダウンリストのItemsプロパティに、図1-5に示すように、それぞれの値を設定しておきます。
ちなみに、図1-5に示したように、SelectedプロパティをTrueにすると、それがデフォルトの選択項目となります。
図1-5 StockListドロップダウンリストのItemsプロパティ
|
[TIPS]
ドロップダウンリストのItemsプロパティをGUIで設定するのが望ましいかどうかは状況によります。値を配列としてもち、プログラムから設定したほうがよいこともあります。たとえば、都道府県を選択するドロップダウンリストの場合、GUIで設定するのは数が多く大変なので、配列として都道府県名をもち、プログラムからループ処理でItemsプロパティに追加したほうが簡単でしょう。 |
|
・CommentTextテキストボックス
概要を入力するテキストボックスです。
・DetailTextテキストボックス
解説を入力するテキストボックスです。複数行入力を許したいので、TextModeプロパティをMultilineに設定しています。
・ImgFileファイルフィールド
画像ファイルをアップロードするためのフィールドです。このコントロールは、[ツールボックス]の[HTML]にあります(図1-6)。
図1-6 [ツールボックス]の[HTML]
[ツールボックス]の[HTML]にあるコントロールは、HTMLの要素に1対1で対応します。ここで利用しているファイルフィールド(File
Field)は、“<INPUT type="FILE">”というHTML要素に対応します。
ここでは、ファイルフィールドのname属性(プロパティ)にImgFileという文字列を設定し、ImgFileという名前でプログラムからアクセスできるようにしておきます(図1-7)。
|
[TIPS]
[ツールボックス]の[Webフォーム]内のコントロール群は、id属性(プロパティ)を使ってコントロールに名前を付けるのに対し、[HTML]内のコントロール群に名前を付けるときには、name属性(プロパティ)を使って名前を付けます。 |
|
図1-7 ファイルフィールドのプロパティ
●ファイルのアップロードを使うときのWebフォーム
ファイルをアップロードするためのファイルフィールドを含めた場合には、HTMLソースを手作業で編集し、<form>要素で、encodetype属性に“multipart/form-data”を指定しなければなりません(図1-8)。
|
[TIPS]
この操作は、Webフォームでファイルのアップロードをサポートするときにだけ必要です。ファイルのアップロードをしない場合――Webフォームにファイルフィールド(File
Field)を置かない場合――には、必要ありません。 |
|
図1-8 <form>要素を編集する
●エラー処理で不正な入力を弾く
連載で扱うのは、あくまでもサンプルにすぎないので、詳細なエラーチェックをするとプログラムをわかりにくくするだけなので省く傾向で考えています。
しかしエラーチェックの方法がわからないと、実務アプリケーションを構築するのは不可能に近いので、簡単に触れておきます。
アプリケーションでは、さまざまなエラー処理が考えられますが、ユーザーインタフェースを備えるアプリケーションの場合、まず第一段階のエラー処理として、「ユーザーが入力した文字列が不正ではないことの確認」が必須と言えます。
ASP.NETでは、検証コントロールを使うと、入力エラーのチェックができます。
検証コントロールには、次の5種類があります(表1-3)。
表1-3 検証コントロール
| 検証コントロール |
用途 |
| RequiredFieldValidatorコントロール |
未入力を許さないようにするための検証コントロール |
| CompareValidatorコントロール |
入力された値が、他のコントロールの入力値や定数などと合致または、以上、以下を比較する検証コントロール。たとえば、「メールアドレス」と「メールアドレスの確認」の2つのテキストボックスを用意し、両者に入力された値が合致しないとエラーとするような場合に用いる。 |
| RangeValidatorコントロール |
入力された値が、特定の範囲の値であることを調査する検証コントロール |
| RegularExpressionValidatorコントロール |
正規表現を使い、入力された値が、その正規表現と合致するかどうかを調査する検証コントロール |
| CustomValidatorコントロール |
開発者が、入力値をチェックするメソッドをあらかじめ作っておき、そのメソッドの戻り値の真偽でエラーをチェックする検証コントロール |
このWebアプリケーションでは、「商品名(ProductNameText)」「価格(PriceText)」「在庫情報(StockList)」「概要(CommentText)」「解説(DetailText)」は、未入力を許したくないので、RequiredFieldValidatorコントロールを使います。
RequiredFieldValidatorコントロールは、図1-9に示すように、ControlToValidateプロパティに、未入力を許したくないコントロールの名前を、ErrorMessageプロパティにエラーがあったときに表示したエラーメッセージを、それぞれ指定します。
図1-9 RequiredFieldValidatorコントロールのプロパティの設定
※上記の図は、ProductNameTextテキストボックスに関する設定です。RequiredFieldValidatorコントロールは、ひとつにつきひとつのコントロールしかチェックできないので、他の「価格(PriceText)」「在庫情報(StockList)」「概要(CommentText)」「解説(DetailText)」についても、RequiredFieldValidatorコントロールを配置して、同様に設定します。
エラーがあったときに、ErrorMessageプロパティに指定したエラーメッセージがどのように表示されるのかは、Displayプロパティの設定に依存します(表1-3)。
表1-3 Displayプロパティの意味
| 値 |
意味 |
| Static |
デフォルトの設定。検証コントロールを設置した場所にエラーを表示する。エラーがあってもなくても、ErrorMessageプロパティで指定した領域だけはとられ、エラーの有無によってレイアウトが崩れるのを避けることができる |
| Dynamic |
検証コントロールを設置した場所にエラーを表示する。エラーがない場合には、領域はとられないので、エラーの有無によってレイアウトが崩れる可能性がある |
| None |
エラーメッセージを検証コントロールを設置した場所には表示しない。別途ValidateSummaryコントロールを置いて、そこに集中的にエラーを表示したい場合に用いる |
今回は、図1-2に示したように、ValidationSummaryコントロールを置き、エラーメッセージを一カ所に表示するようにしているので、DisplayプロパティにはNoneを指定することにします。
続いて、「在庫情報(StockList)」は、表1-2に示したように、0〜3までの値しかとらせたくありません。そこで、RangeValidatorコントロールを配置し、図1-10のように設定します。
RangeValidatorコントロールでは、ControlToValidateプロパティで検証の対象となるコントロールを設定し、Typeプロパティで型の種類を選択します。型の種類は、表1-4に示す値を指定できます。
表1-4 Typeプロパティの意味
| 値 |
意味 |
| String | 文字列 |
| Integer | 整数 |
| Double | 浮動小数 |
| Date | 日時 |
| Currency | 金銭 |
ここでは整数として判定したいのでIntegerを選択することになります。
許容する値の範囲は、MinimumValueプロパティとMaximumValueプロパティで指定します。すると、許容する最小値より小さな値や許容する最大値より大きな値が入力されたときに、エラーとして検出できるようになります。
|
[TIPS]
RangeValidatorコントロールは、ユーザーが値を未入力だった場合には、エラーとはしません。未入力のチェックは、RangeValidatorコントロールではなく、先に説明したRequiredFieldValidatorコントロールを使います。次に説明するRegularExpressionValidatorコントロールも同様です。 |
|
図1-10 在庫情報に関するRangeValidatorコントロールの設定

「価格(PriceText)」も、価格として正しい数値しか入力させたくありません。そこで同様にRangeValidatorコントロールを使って、図1-11のように設定します。
図1-11 価格に関するRangeValidatorコントロールの設定

図1-11では、TypeプロパティにCurrencyを指定しています。Currencyを指定すると、Integerに指定したときと違い、桁取りのカンマも許容するようになります。
ところで表1-1を見るとわかるように、Productsテーブルでは、商品名は最大64文字、概説は最大256文字、解説文は最大2048文字までとしてあります。そのため、これらの文字数以上の文字が入力されるとレコードを書き込むときにエラーとなってしまいます。
そこで、商品名を入力するProductNameTextテキストボックス、概説を入力するCommentTextテキストボックス、解説を入力するDetailTextテキストボックスには、それぞれ文字数の上限設定が必要です。
上限を設定する方法は、いくつかありますが、ここでは、RegularExpressionValidatorコントロールを使う方法を紹介します。
RegularExpressionValidatorコントロールは正規表現を使って検証するコントロールです。
ここでは正規表現についての詳細な解説は避けますが、正規表現では、任意の文字数以下の文字を「.{最小文字数, 最大文字数}」として表現できます。たとえば正規表現として「.{1,64}」というパターンを指定すると、「1文字以上64文字以下の文字列」ということを表現できます。
そこで図1-12のように、RegularExpressionValidatorコントロールを指定します。
図1-12 商品名に関するRegularExpressionValidatorコントロールの設定

図1-12にあるように、正規表現のパターンは、ValidationExpressionプロパティに指定します。
ここでは省略しますが、図1-12と同様にして、概要(CommentText)には「.{1,256}」を指定したRegularExpressionValidatorコントロールを用意します。
解説(DetailText)にも同様にしてRegulatExpressionValidatorコントロールを用意しますが、このときのパターンは、「(.|\n){1,2048}」とします。というのは、解説を入力するテキストボックスは、図1-2に示したように複数行としてあり、改行(\n)を含むことを許すためです。
|
[TIPS]
テキストボックスに入力される長さを制限するには、TextBoxコントロールのmaxlengthプロパティを使えばよいと思われるかも知れません。しかし、TextBoxコントロールのmaxlengthプロパティは、クライアントに出力する、<INPUT
type="text">要素のmaxlength属性に相当するもので、指定した長さ以上の文字列をWebブラウザ上で「入力できなくする」ということしかできません。そのため悪意のある者が、Webフォームで用意したフォームを使わずに、直接このWebフォームを呼び出してくるなどして、maxlengthプロパティで指定した以上の長さのデータを送信してくる可能性があります。よってmaxlengthプロパティに頼ることはできません。 |
|
■Webフォームの処理
一通りのWebフォームができたところで、コーディングに入ります。
●イベントの発生順序
Webフォームは、Pageクラスから継承したクラスとして構成されます。すなわちPageクラスのイベントを駆使して、さまざまな処理をしていくことになります。
Pageクラスのイベントはさまざまで、Webフォーム上にどのようなコントロールを配置したかにもよりますが、その発生順序は、概ね、図1-13のようになります。
図1-13 Webフォームにおけるイベントの発生順序

図1-13に示したように、Webフォームにおける処理は、Loadイベント、TextChangedイベントなどの変更系イベント、Clickedイベントなどのボタン系イベントで処理するのが主な流れとなります。
●ポストバック状態とLoadイベントの処理
Webフォームには、「ポストバック状態」と「非ポストバック状態」があります。
ポストバック状態とは、ユーザーがWebフォームにてSUBMITボタンを押して何らかのデータを送信してきた状態、非ポストバック状態とは、それ以外の状態――たとえば、Webブラウザのアドレス欄に、このWebフォームのURLを入力してきたときなど――を指します。
わかりやすく言えば、1回目にユーザーがこのページを表示したときが「非ポストバック状態」、ユーザーがSUBMITボタンを押して再度同じWebフォームが呼び出されたときがポストバック状態です(図1-14)。
図1-14 非ポストバック状態とポストバック状態
ユーザーがWebフォームに入力したデータをアプリケーションから参照できるのは、ポストバック状態のときだけです。
Loadイベントの処理では、ポストバック状態か非ポストバック状態であるかを調べ、次の処理をするのが一般的です。
・ポストバック状態のとき
ユーザーがSUBMITボタンを押したときの処理を実装する。たとえば、今回のアプリケーションならば、検証コントロールを使ってエラーチェックをし、データベースに書き込む処理をし、結果を表示する処理を実装する。
・非ポストバック状態のとき
ユーザーがデータを入力できるようなWebページを構成する。たとえば、必要があれば、デフォルト値などをコントロールに設定する処理などを施す。
ポストバック状態であるか非ポストバック状態であるかは、IsPostBackプロパティを使って調べられます。
if (this.IsPostBack)
{
// ポストバック
}
else
{
// 非ポストバック
}
今回のアプリケーションでは、Loadイベントの処理をリスト1-3のように実装します。
リスト1-3 productadd.aspx.csファイルのLoadイベントの処理
リスト1-3では、非ポストバック状態にときには、入力フォームを含むInputPanelパネルを表示し、結果出力を含むSubmitPanelパネルを隠すという処理をしています。
InputPanel.Visible = true;
SubmitPanel.Visible = true;
|
コラム:Loadイベントで処理すべきかClickedイベントで処理すべきか
ユーザーが入力フォームでボタンを押したときの処理を実装するには、2通りの方法があります。ひとつは今回扱っているように、Loadイベントで処理する方法、もうひとつは、SUBMITボタン(<INPUT type="SUBMIT">)ではなくButtonコントロール(<asp:Button>)を用い、Clickedイベントで処理する方法です。
図1-13に示したように、LoadイベントとClickedイベントでは、発生順序が異なります。ClickedイベントはLoadイベントの後に発生し、また検証コントロールによって検証されたあとに実行されます。
そのためClicekdイベントの時点で検証を無効にするといった処理は実装できません。またButtonコントロールはサーバーコントロールであり、ASP.NETによってサーバー側のイベントが処理されるため、SUBMITボタンを使うよりも、(ほとんど気にならないほどの微々たるものですが)いくばくかのリソースを消費するという点も欠点として挙げられます。
実際にWebアプリケーションを構築する場合には、Loadイベントで処理しても、Clickedイベントで処理しても、どちらでもかまいませんが、本稿では、より柔軟性の高い、Loadイベントを使って処理で実装していくことにします。
とはいえ、つねにLoadイベントでの処理が適切であるとは限りません。たとえば、ひとつの入力フォームに複数のボタンがあり、どのボタンが押されたのかで処理を分けたい場合には、Buttonコントロールを用い、Clickedイベントで処理したほうが簡単でしょう。また、Windowsアプリケーションの構築経験があり、すべてをイベントで処理するのに慣れている人の場合には、Buttonコントロールのほうが扱いやすいかも知れません。
|
|
●検証コントロールを使ったエラーチェック
一方で、ポストバック状態のときには、ユーザーがコントロールに何か値を入力して、それを送信してきているわけですから、入力されたデータのエラーチェックが必要です。
ここまでの行程で、すでにWebフォームに検証コントロールを配置しているので、まずは、検証コントロールを使って、エラーがないかどうかをチェックします。
図1-13に示したように、検証コントロールによる検証はLoadイベントより後に実施されます。そのため、Loadイベントが発生した段階では、まだ検証コントロールによる検証が実行されていません。
そこで明示的にValidateメソッドを呼び出して、検証コントロールに検証させます。Validateメソッドは、個々の検証コントロールに対して呼び出すこともできますが、次のように自分自身のValidateメソッドを呼び出すと、Webフォームに配置したすべての検証コントロールにおいて、検証が実行されます。
this.Validate();
検証が成功したかどうかは、IsValidプロパティで調べられます。
if (this.IsValid)
{
// 検証成功。すなわちエラーなし
}
else
{
// 検証失敗。何らかのエラーがあった
}
リスト1-3では、検証に成功した場合に限り、次に説明する、データベースに値を書き込むInsertDBメソッドを呼び出し、データベースへの書き込み処理をしています。
検証に失敗した場合には、検証コントロールの部分にエラーメッセージが表示されます。図1-2に示したように、今回のWebフォームでは、検証コントロールのエラーを一カ所にまとめるValidateSummaryコントロールを設置しているため、検証エラーが生じたときの画面表示は、図1-15のようになります。
図1-15 検証エラーがあったときの表示
●検証コントロールの注意
検証コントロールは便利ですが、利用する場合には、注意点があります。それは、検証コントロールは、デフォルトで、「クライアント側」と「サーバー側」の両者で動作するという点です。
実際に検証コントロールを含むWebフォームをWebブラウザで開き、ソースを参照してみるとわかりますが、検証コントロールを含んでいる場合には、クライアント側にJavaScriptで書かれた入力検証のためのコードが送信されます。
|
[TIPS]
クライアント側で利用するスクリプト言語は、WebフォームのdefaultClientScriptプロパティでVBScriptに変更することもできます。 |
|
そしてユーザーがSUBMITボタンを押したときには、サーバーへの要求がされる前に、クライアント側のスクリプトによって検証チェックがなされます。検証エラーがあれば、クライアント側で実行されるスクリプトによって、エラーメッセージが動的に表示されます。
このとき、次の2点に注意してください。
(1)SUBMITボタンを押しても、常に、サーバー側のWebフォームが呼び出されるとは限らない
ユーザーがSUBMITボタンを押したときに、検証コントロールの条件に満たない値が入力されていた場合には、サーバー側のWebフォームは呼び出されないのでLoadイベントは発生しません。
それどころか、Webサーバーに接続されることもありません。これは、わざとエラーのある入力をしてSUBMITボタンを押しても、IISのログに、その接続が記録されないことからも確認できます。
(2)クライアントの検証コントロールはライブラリの形で提供される
クライアントの検証コントロールは、IISの/aspnet_client仮想ディレクトリ以下で公開されるスクリプトライブラリによって提供されます。
そのため、IISの設定で、/aspnet_client仮想ディレクトリを公開しないように設定したり、削除したりしてしまうと、クライアント側の検証コントロールは、正しく動作しません。
なお、当然ながら、すべてのWebブラウザが、JavaScriptなどのスクリプトをサポートするわけではありませんし、Webブラウザによって、スクリプトの文法が違うことも考えられます。
そこでASP.NETでは、接続してきたWebブラウザの種類を判別し、スクリプトをサポートしていると思われるWebブラウザに対してだけ、スクリプトを送信します。具体的には、Internet
Explorer 4.0以上のWebブラウザの場合にだけ、スクリプトを送信し、クライアント側での検証コントロールを有効化します。
そのため、Webブラウザがスクリプトをサポートしない場合には、クライアント側の検証コントロール機能は無効となります。
なお、スクリプトを含んでいるとデバッグが困難になるとか、汎用性が高められなくなるといった理由から、ASP.NETがスクリプトをクライアントに送信しないようにしたいことがあるかも知れません。
その場合には、EnableClientScriptプロパティをFalseに設定することで抑制できます。
|
[TIPS]
ASP.NETでは、Internet Explorer 4.0以上のWebブラウザを「上位レベルブラウザ」と呼んでいます。何が上位レベルブラウザとなるのかは、Webブラウザがサーバーに向けて送信してくるUser-Agentヘッダで調べられます。そのため、ユーザーがWebブラウザのオプション設定でスクリプトの利用を禁止している場合には、たとえ上位レベルブラウザでも、クライアント側の検証コントロールは、動作しません。上位レベルブラウザを判定する条件は、.NET
Frameworkのmachine.configファイルの<clientTarget>要素に記述されています。 |
|
|
[TIPS]
ASP.NETでは、Webブラウザが、「ie5(Internet Explorer 5.0以降)」「ie4(Internet
Explorer 4.0)」「uplevel(上位レベルブラウザ。JavaScript 1.2、HTML
4.0、Microsoft Document Object Model)、CSSをサポートする汎用Webブラウザ)」「downlevel(下位レベルブラウザ。HTML
3.2しかサポートしないWebブラウザ)」の4つに自動分類され、Webフォームに配置したコントロールが、適したHTML形式に変換され、クライアントに送信されます。自動分類を無効にしたい場合には、WebフォームのClientTargetプロパティで強制的に、「ie5」「ie4」「uplevel」「downlevel」のいずれかに変更することもできます。 |
|
■データベースへの書き込み処理
では、いよいよADO.NETを使ったデータベースへの書き込み処理を実装します。そのプログラムは、リスト1-4のようになります。
データベースに書き込むには、(1)SqlCommandオブジェクトを使う方法、(2)DataAdapterオブジェクトを使う方法、の2通りがあります。前者は、SQL文を直接実行することによってデータベースを更新する方法、後者は、すでにメモリ内に保存されているデータを一括してデータベースに書き戻す方法です。
今回のように、単一レコードを追加する場合には、(1)の方法のほうが簡単なので、リスト1-4では、(1)の方法を採用しています。
リスト1-4 データベースへの書き込み処理
●名前空間のインポート
SQL Serverを利用するには、System.Data.SqlClient名前空間で定義されている各クラスを使います。そこで、次のようにして、この名前空間をインポートしておく必要があります。
using System.Data.SqlClient;
●ユーザーがコントロールに入力した値の参照
ポストバック状態であるときには、ユーザーがWebフォーム上のコントロールに入力した値を、各コントロールのプロパティを参照することで取得できます。
リスト1-4では、次のようにして、入力されたそれぞれの値を取得しています。
// コントロールにユーザーが入力した値を取得
string productname = ProductNameText.Text;
string comment = CommentText.Text;
string detail = DetailText.Text;
// 以下は、Parseメソッドで例外が発生する可能性があるが
// 検証コントロールで書式チェックをしているので
// 例外が発生する可能性はなく、try〜catchでの例外チェックは省略
Decimal price = Decimal.Parse(PriceText.Text);
int stocknum = int.Parse(StockList.SelectedValue);
|
●データベースへのコネクションの準備
データベースに接続するためには、SqlConnectionオブジェクトが必要です。
次のようにしてSqlConnectionオブジェクトを用意します。
SqlConnection sqlconn =
new SqlConnection(データベース接続文字列)
データベース接続文字列は、“Server=localhost;database=ProductDB;Trusted_Connection=Yes”のような、接続先のサーバー名やデータベース名、ユーザー名、パスワードなどを指定する文字列です。
リスト1-4では、データベース接続文字列を定義するのに、アプリケーション構成ファイルを利用しています。
まずは、アプリケーション構成ファイルであるWeb.configファイルに、次のように<appSettings>要素を加え、ここにデータベース接続文字列を記述しておきます。
<configuration> <system.web> <!-- Visual Studio .NETで自動生成される設定項目が、 デフォルトでここに記述されています--> </system.web> <!-- 以下を追加します(値は実際のデータベース環境に合わせてください)--> <appSettings> <add key="DSNSTRING" value="Server=localhost;database=ProductDB;Trusted_Connection=Yes"/> </appSettings> </configuration>
ここでは、DSNSTRINGという名前の項目を追加し、そこにデータベース接続文字列を設定しています。
|
[TIPS]
“Trusted_Connection=Yes”は、WIndows認証を使うことを示します。すなわち、ASPNETユーザーでデータベースに接続することを意味します。SQL Server認証を使う場合には、代わりに“uid=ユーザー名;pwd=パスワード;”と指定します。 |
|
このときプログラムから、次のようにすれば、このWeb.configファイルで定義したデータベース接続文字列を参照できます。
SqlConnection sqlconn =
new SqlConnection(
ConfigurationSettings.AppSettings["DSNSTRING"]);
なお、Configurationクラスは、System.Configuration名前空間にあるので、次のようなインポートが必要です。
using System.Configuration;
アプリケーション構成ファイルを使わずとも、SqlConnectionクラスのコンストラクタ引数に直接データベース接続文字列を指定する方法をとっても、もちろんかまいません。
しかしアプリケーション構成ファイルを使っておくと、あとでデータベース接続文字列を変更したい場合にプログラムを再コンパイルすることなく、Web.configファイルを変更すればよいので、メンテナンス性に優れるというメリットがあります。
●SqlCommandオブジェクトの用意
次に、SQL Serverに送信したいSQL文を指定したSqlCommandオブジェクトを用意します。ここでは、Productsテーブルに新しいレコードを追加するため、次のようなINSERT文を指定したSqlCommandオブジェクトを用意することになります。
SqlCommand sqlcmd = new SqlCommand(
"INSERT INTO Products (productname, price, stock, comment, detail, imgdata, createdate, lastupdate)" +
" VALUES(@productname, @price, @stock, @comment, @detail, @imgdata, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)", sqlconn);
●パラメータの処理
いま用意したSqlCommandオブジェクトでは、パラメータクエリーを使っています。“@productname”や“@price”など、“@パラメータ名”となっている部分が、パラメータです。
このパラメータに対して、実際の値を埋め込みます。
パラメータに対する値の埋め込みは、いくつかの方法がありますが、次のような定型的な書式を覚えておくと簡単です。
SqlCommandオブジェクト.Parameters.Add(パラメータ名,
型, 長さ).Value = 埋め込みたい値
型は、SqlDbType列挙体で指定されている各種の型を指定します。長さは、そのパラメータの長さですが、SqlDbType.IntやSqlDbType.Moneyなど、型が決まれば長さも決まってくるような型の場合には省略可能です。
リスト1-4では、次のようにして埋め込んでいます。
sqlcmd.Parameters.Add("@productname", SqlDbType.NVarChar, 64).Value = productname;
sqlcmd.Parameters.Add("@price", SqlDbType.Money).Value = price;
sqlcmd.Parameters.Add("@stock", SqlDbType.Int).Value = stocknum;
sqlcmd.Parameters.Add("@comment", SqlDbType.NVarChar, 256).Value = comment;
sqlcmd.Parameters.Add("@detail", SqlDbType.NVarChar, 2048).Value = detail;
●アップロードされたファイルの処理
今回は、画像ファイルのアップロードをサポートしています。
アップロードされたファイルの情報は、次のようにして、HttpPostedFileオブジェクトとして取得できます。
HttpPostedFile postedfile = Request.Files["ImgFile"];
Request.Filesコレクションに指定している“ImgFile”は、Webフォーム上に用意したファイルフィールドの名前(name属性の値)です(図1-7を参照)。該当するフィールドが存在しない場合にはnullが、そうでない場合には有効なHttpPostedFileオブジェクトが得られます。
|
[TIPS]
図1-2のWebフォームには、ImgFileという名前のファイルフィールドを配置していますから、Request.Files["ImgFile"]がnullとなることは、通常は、あり得ません。しかし悪意のある者が、このImgFileファイルフィールドを削除してWebフォームを呼び出した場合には、Request.Files["ImgFile"]がNothingになることがあります。 |
|
送信されたファイルサイズは、HttpPostedFileオブジェクトのContentLengthプロパティで調べられます。何もファイルが送信されてきていない場合には、ContentLengthプロパティの値は0となります。
そこで次のようにして、ファイルがアップロードされてきたかどうかを調べることができます。
if ((postedfile == null) ||
(postedfile.ContentLength == 0))
{
// アップロードされていない
}
else
{
// アップロードされてきた
}
|
|
[TIPS]
アップロード可能なファイルサイズは、.NET Frameworkの設定やIISの設定に左右されます。.NET
Frameworkでは、machine.configファイルやWeb.configファイルの<httpRuntime>要素のmaxRequestLength属性で制限されます。デフォルトは、4MBです。変更したい場合には、Web.configファイルに<httpRuntime>要素を記述し、デフォルトの設定を上書きします。IIS
6.0の場合、MaxRequestEntityAllowedプロパティで、さらに制限されますが、デフォルトではMaxRequestEntityAllowedプロパティによる制限は課せられていません。
また、IISのセキュリティを高めるためにURLScanツール(IIS Lockdownツール)を用いている場合には、RequestLimitsセクションのMaxAllowedContentLength項目によっても制限されます。
|
|
ファイルがアップロードされていないときには、データベースの該当列にNull値を設定すればよく、次のようになります。DBNull.Valueは、Null値を示す定数です。
sqlcmd.Parameters.Add("@imgdata", SqlDbType.Image).Value = DBNull.Value;
一方ファイルがアップロードされてきた場合には、アップロードされたファイルのデータを読み取り、データベースの該当列に設定することになりますが、リスト1-4では、その前にJPEG形式ファイルかどうかを調べています。
送信されたファイルのタイプは、ContentTypeプロパティで取得できます。
string contenttype = postedfile.ContentType.ToLower();
if (contenttype != "image/jpeg" &&
contenttype != "image/pjpeg")
{
// JPEG形式ファイルではない
ErrorMessage.Text =
"アップロードされた画像は、JPEG形式ではありません";
return false;
}
上にあるように、ContentTypeプロパティの値が“image/jpeg”や“image/pjpeg”でない場合には、JPEG形式ファイルではないと判断できます。
もちろんこの処理は必須ではありません。多くのWebブラウザは、画像を「JPEG形式」「GIF形式」「PNG形式」しか表示できないので、今回のアプリケーションではJPEG形式のみを受け付けるようにしただけの話です。
|
[TIPS]
ContentTypeプロパティは、クライアントが送信してきたコンテンツタイプ(MIMEタイプ)を保持します。そのため、ContentTypeプロパティにどのような値が設定されているのかは、クライアントのWebブラウザの設定次第です。クライアントによっては、JPEGファイルを送信してくるときに、“application/octet-stream”のような別コンテンツタイプを送信してくるものもあるかも知れません。つまりContentTypeプロパティによる種別判定は、完璧なものではありません。より完璧にJPEGデータであるのかを調べたければ、Bitmapクラスを使って実際に送信されたデータを読み込んで画像として認識できるかを判断するといった方法をとることになるでしょう。 |
|
さてアップロードされたファイルのデータをパラメータとして設定する方法ですが、これは、次のようになります。
try
{
byte []img = new byte [postedfile.ContentLength];
postedfile.InputStream.Read(
img, 0, postedfile.ContentLength);
// パラメータとして設定
sqlcmd.Parameters.Add(
"@imgdata", SqlDbType.Image).Value = img;
}
catch (Exception ex)
{
// 何らかのエラーが発生した
// ログに書き込む
Response.AppendToLog(ex.Message);
ErrorMessage.Text = "画像のアップロード処理に失敗しました";
return false;
}
アップロードされたファイルは、IntputStreamプロパティを参照するとStreamオブジェクトが得られるので、Streamオブジェクトの各メソッドを使って、実際に読み取ることができます。上のプログラムでは、Readメソッドを使い、全データをバイト配列として読み込んでいます。
バイト配列として読み込んだならば、先に示したパラメータの埋め込み方法と同様に、Parametersコレクションにパラメータを追加していくだけです。
|
[TIPS]
アップロードされたファイルをデータベースに書き込むのではなく、ファイルとして保存するならば、SaveAsメソッドを利用します。SaveAsメソッドは、引数に保存したいファイル名を指定するだけでファイルとして保存できます。 |
|
ところで上のプログラムでは、例外が発生したときに、その内容をIISのログに書き込むという処理をしています。
Response.AppendToLog(ex.Message);
このようにResponseオブジェクトのAppendToLogメソッドを使うと、任意の文字列をIISのログに追記でき、問題が発生したときの原因をあとで掴みやすくなります。
|
[TIPS]
例外が発生したとき、例外のメッセージを、そのままクライアントに表示するのは好ましくありません。とくにデータベースに対する例外の場合、その例外に含まれるメッセージに、データベース接続文字列やデータベースの構造などを連想させるものが含まれている可能性も否定できず、セキュリティ的に、見せてはならないものを見せてしまうかも知れないからです。よってWebアプリケーションの例外処理では、クライアントに表示するのは「エラーが発生しました」などとし、詳細な例外は表示せず、ログに記録するという方式をとったほうがよいでしょう。 |
|
[TIPS]
Response.AppendToLogメソッドで出力したログが記録されるのは、IISの設定で、ログ形式が[Microsoft IIS ログファイル形式]になっているときだけです。[W3C拡張ログファイル形式]などの場合には、出力した内容はログに記録されません。 |
|
●データベースに対するSQL文の実行
以上で準備は整ったので、いよいよデータベースに接続して、SQL文を実行します。その処理は、次のようになります。
int recnum = -1;
try
{
// 接続を開く
sqlconn.Open();
// INSERT文を実行する
recnum = sqlcmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
// 何らかのエラーが発生した
// エラーをIISのログに記録しておくことにする
Response.AppendToLog(ex.Message);
}
finally
{
// 接続を閉じる
sqlconn.Close();
}
データベースに対するSQL文の実行は、次の流れとなります。
(1)SqlConnectionオブジェクトのOpenメソッドを呼び出す
データベースと接続される。
(2)SqlCommandオブジェクトのExecuteNonQueryメソッドを呼び出す
SqlCommandオブジェクトに指定してあるSQL文が実行される。
(3)SqlConnectionオブジェクトのCloseメソッドを呼び出す
データベースから切断する。 |
[TIPS]
SELECT文など、結果レコードを返すSQL文を実行する場合には、(2)の処理において、ExecuteReaderメソッドを使います。その詳細は、次回以降に説明します。 |
|
ExecuteNonQueryメソッドは、呼び出しに成功すると、データベース上で影響を受けたレコードの数を返します。ここでは、1つのレコードを追加していますから、成功すれば、1が得られます。
そこで次のようにして、本当にレコードが追加できたかどうかを確認できます。
if (recnum == 1)
{
// 更新に成功した
return true;
}
else
{
// 更新に失敗した
ErrorMessage.Text =
"データベースへの書き込みに失敗しました";
return false;
}
|
■まとめ
今回は、どちらかというと、ADO.NETの処理よりも、ASP.NETの基本的な仕組みの解説が多くなりましたが、実際にWebアプリケーションを構築する場合には、今回説明した、ASP.NETならではのコントロールの扱い方やエラーチェックの方法なども、意外と重要です。
次回は、今回書き込んだデータベースから商品情報を読み取り、それをクライアントに出力するプログラムを作っていきます。
今回のリストをすべてダウンロード(LZH形式)
|