WebSurfer's Home

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

固定長パラメータの LIKE 比較

by WebSurfer 2010年9月3日 22:35

Transact-SQL の LIKE 句を使用して文字列の比較を行うときは、パターン文字列と比較対象文字列の中の空白の取り扱いに注意が必要です。

詳しくは MSDN ライブラリの LIKE (Transact-SQL) を見てもらうとして、ここでは ADO.NET や Visual Studio のウィザードで作る TableAdapter を利用する場合の注意事項を、例をあげて書いておきます。

例として、SQL Server DB に TestTable という名前のテーブルがあり、以下のフィールドを持っているとします。

FieldA  nchar(5)
FieldB  nchar(20)

TeatTable は以下の内容になっています。(下の画像では TestTable2 になっていますが、気にしないでください。(笑))

TeatTable の内容

この Table を、以下の SELECT クエリを用いて、FieldA の 前 2 文字を指定して検索を行うとします。

SELECT FieldA, FieldB
FROM   TestTable
WHERE  (FieldA LIKE @FieldA + N'%')

まず、Visual Studio のウィザードを利用して、上記の SELECT クエリをベースに型付 DataSet + TableAdapter を作ります。デザイン画面で見ると、以下のようになります。

型付 DataSet + TableAdapter

TableAdapter 作成された GetData メソッドを使って、その引数を "01" としてデータを抽出してみます。

結果は "01" しか抽出されません。FieldA = "01" で、"01", "01111", "012", "01234", "01457" がマッチするように思えますが、"01" しか抽出されないのは何故でしょう?

理由は以下のとおりです。

自動生成される TableAdapter のコードは、nchar(5) の場合、パラメータの追加部分は以下のようになります。

this._commandCollection[0].Parameters.Add(
  new global::System.Data.SqlClient.SqlParameter(
    "@FieldA", global::System.Data.SqlDbType.NChar, 5, 
    global::System.Data.ParameterDirection.Input, 0, 0, 
    "FieldA", global::System.Data.DataRowVersion.Current, 
    false, null, "", "", ""
  )
);

この ... SqlDbType.NChar, 5, ... というところが問題のようです。SqlParameter.Size に 5 が設定されるので、FieldA = "01" の場合、クエリは LIKE '01   %'(01 と % の間に 3 文字空白)となって、"01"(実際は 01 の後に空白 3 文字あり) 以外はマッチしないということのようです。

MSDN ライブラリの SqlParameter.Size プロパティ の解説には "固定長データ型では、Size の値は無視されます。" と書いてあったんですが・・・

解決策は以下のとおりです。

  1. FieldA の型を変更できるのであれば、nchar(5) を nvarchar(5) に変更して、型付 DataSet + TableAdapter を作り直す。
  2. FieldA の型を変更できなければ、ウィザードベースで GetData メソッドを作るのは諦めて、自力でコードを書いて TableAdapter を拡張する。

上記 2 ですが、パラメータの追加の部分を以下のようにすると期待通りの結果になります。自力で書くのは面倒だからといって、自動生成されたコードを書き直すと、思わぬところで不具合を生じる可能性がありますので止めた方がいいです。

command1.Parameters.Add(
  new SqlParameter("@FieldA", SqlDbType.NChar));
command1.Parameters["@FieldA"].Value = "01";

または、

command1.Parameters.AddWithValue("@FieldA", "01");

なお、先の記事 SqlParameter の Size 指定 で、長さ(SqlParameter.Size)の指定は 0 が良いと書きましたが、それは今回のケースでも当てはまっていました。以下のようにしても OK です。

command1.Parameters.Add(
  new SqlParameter("@FieldA", SqlDbType.NChar, 0, "FieldA"));
command1.Parameters["@FieldA"].Value = "01";

------------ 2010/9/4 追記 ------------

パターン文字列の生成に使う型(パラメータ設定のデータ型)と比較対象文字列の型(テーブル定義のデータ型)を合わせる必要はないということに気がつきました。

パラメータ設定で SqlDbType.NChar を SqlDbType.NVarChar に変更しても OK です。すなわち以下のようにすれば、nvarchar(5) で文字列を組み立てるので、パターン文字列は '01%' となるはずです。

command1.Parameters.Add(
  new SqlParameter("@FieldA", SqlDbType.NVarChar, 5, "FieldA"));
command1.Parameters["@FieldA"].Value = "01";

要するに、パラメータ設定で、SqlDbType.NChar とすると MSDN ライブラリの LIKE (Transact-SQL) の解説のセクションにある 2 つの SQL の前者、SqlDbType.NVarChar とすると後者のようになるということのようです。

Tags: , ,

ADO.NET

ダウンロードの際の拡張子 と MIME Type の指定

by WebSurfer 2010年9月2日 22:42

MSDN フォーラムで、IE で xlsm をダウンロードすると zip 形式になってしまい、直接 Excel で開けないという話があったので、ちょっと調べてみました。

結局、単にそれはサーバー側の Web アプリの問題だったのですが、調査の結果、自分が知らなかったことがいろいろあったので、忘れないように書いておきます。

ブラウザは、ヘッダーの Content-Disposition: attachment; filename= で指定する拡張子と、Content-Type: で指定する MIME Type のどちらでファイルの種類を判断するでしょうか?

検証した結果は以下のとおりでした。簡単に言うと、IE8 は拡張子で判断、Opera は MIME Type で判断、Forefox は拡張子と MINE Type の指定が違う場合結果は未定義のようです。

ブラウザ Ext: xlsm
MT: xlsm
Ext: xlsm
MT: zip
Ext: zip
MT: zip
Ext: zip
MT: xlsm
IE8 Excel Excel Zip Zip
Firefox 3.6.8 Excel Zip Zip Zip
Opera 10.61 Excel Zip Zip Excel

上の表で、Ext は Content-Disposition に設定した拡張子、MT は Content-Type に設定した MIME Type です。

検証に使ったファイルは、有効な xlsm 形式のファイルで、拡張子は正しく xlsm としており、検証の間、中身も名前も一切変えていません。

クライアントの OS は Vista SP2 です。拡張子とアプリケーションの関連付けは、xlms は Excel、zip は WinZip としています。Web サーバーは Windows Server 2008 の IIS7, ASP.NET 3.5 SP1 です。

予想外の動きをしたのは Opera で、拡張子と MIME Type が異なる場合、拡張子を MIME Type に合わせて書き換えてしまいます。下の画像は、拡張子を xlsm、MIME Type を zip とした場合の例です。元のファイル名 001.xlsm が 001.zip に書き換えられています。

Opera のファイルダウンロード時のダイアログ

直リンクの場合(a 要素の href 属性にサーバーに置いた xlsm ファイルを指定した場合)、ヘッダーは以下のようになります。ブラウザは正しく判断出来るようです。

Content-Type: application/vnd.ms-excel.sheet.macroEnabled.12
Last-Modified: Thu, 02 Sep 2010 13:07:58 GMT
Accept-Ranges: bytes
ETag: "3e8647dd9f4acb1:0"
X-Powered-By: ASP.NET
Date: Thu, 02 Sep 2010 13:12:12 GMT
Content-Length: 12477

ちなみに、HTTP ハンドラーを使った場合のヘッダーは以下のようになります。

Cache-Control: private
Content-Type: application/vnd.ms-excel.sheet.macroEnabled.12
Content-Disposition: attachment; filename=001.xlsm
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Thu, 02 Sep 2010 13:21:00 GMT
Content-Length: 12477

検証に使った HTTP ハンドラのコードを参考までにアップしておきます。

<%@ WebHandler Language="C#" Class="_045_Handler" %>

using System;
using System.Web;
using System.IO;

public class _045_Handler : IHttpHandler 
{
  public void ProcessRequest(HttpContext context)
  {
    string path = 
      context.Server.MapPath("~/Test/data/001.xlsm");

    if (!File.Exists(path))
    {
      return;
    }
        
    string type = context.Request.QueryString["type"];

    switch (type)
    {
      case "xlsm_xlsm":
        SetHeader(context, "001.xlsm", 
          "application/vnd.ms-excel.sheet.macroEnabled.12");
        break;
      case "xlsm_zip":
        SetHeader(context, "001.xlsm", 
          "application/x-zip-compressed");
        break;
      case "zip_zip":
        SetHeader(context, "001.zip", 
          "application/x-zip-compressed");
        break;
      case "zip_xlsm":
        SetHeader(context, "001.zip", 
          "application/vnd.ms-excel.sheet.macroEnabled.12");
        break;
      default:
        SetHeader(context, "001.xlsm", 
          "application/vnd.ms-excel.sheet.macroEnabled.12");
        break;                
    }
    context.Response.TransmitFile(path);
    context.Response.End();
  }
    
  public bool IsReusable
  {
    get {
      return false;
    }
  }

  protected void SetHeader(HttpContext context, string name, string type)
  {
    context.Response.AppendHeader("Content-Disposition",
      "attachment; filename=" + name);
    context.Response.ContentType = type;
  }
}

他に、今回知った新(?)事実は、Excel 2007 からファイル形式が変わったということです。何をいまさらと言われるかも知れませんが。(汗)

Excel 2007 からはファイルはバイナリではなくなり、xml 形式のテキストを zip で圧縮したものになっているそうです。確かに WinZip で xlsm ファイルを開くことができ、中身は xml 形式のテキストファイルでした。

また、xlsm は「マクロ有効ブック」というそうで、セキュリティ対策として、拡張子からマクロが含まれていることが分かるような名前の規則を作ったそうです。

------------ 2011/5/21 追記 ------------

IE8 は拡張子で判断と書きましたが、やはり IE はそのような仕様になっているようです。以下のページを参考にしてください。

ファイルのダウンロードダイアログで表示されるファイル名の命名規則

即ち、上のページの「詳細」の 1 番目に書いてあるとおり「Content-Disposition: ヘッダが存在する場合は、filename パラメータで設定されたファイル名が利用されます。」ということです。

サポートページには書いてないですが、Content-Disposition: ヘッダでファイル名が指定してあれば、Content-Type: の指定は無視されます。

2 番目の「HTTP レスポンス内に存在する、送られてきた Content-Type が、レジストリの HKEY_CLASS_ROOT\MIME\Database\Content Type 以下に存在するかどうか・・・」というのは、Content-Disposition: ヘッダの filename パラメータにファイル名の指定がない場合に限るようです。

Tags: ,

Upload Download

FileUpload が無反応

by WebSurfer 2010年9月1日 14:03

FileUpload コントロールのテストをしている時、テキストボックスに文字を入力してボタンをクリックしても、入力内容によっては何の反応もないことがありました。

Fileupload

ボタンは単純に input type="submit" なので、クリックすれば、必ず form が submit されるはずなのですが・・・

何故でしょう?

不思議に思っていたんですが、MSDN フォーラムを見ていたら分かりました。XP SP2 以降に取られたセキュリティ対策だそうです。

詳しくは、マイクロソフトサポートオンラインの 文書番号:890981 に書いてありますが、ローカルや共有フォルダのファイルの完全修飾パスでないと、ファイルの送信ができないよう仕様が変更されたそうです。

ただし、あまり厳密なものではなく、それらしい形式なら submit はかかります。例えば、c は無反応ですが、c: なら OK といった具合です。

そんな程度でセキュリティ対策に効果があるのか、よく分かりません。(汗)

ちなみに、Firefox 3.6.8 の場合は、テキストボックスにフォーカスを当てると「ファイルの選択」ダイアログが開きます。要するに、テキストボックスに直接入力できないようになっています。セキュリティ対策としては、こちらの方が効果がありそうな気がします。

Tags:

Upload Download

About this blog

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

Calendar

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

View posts in large calendar