none
TextBoxの指定値を受け、DB上の合致データをDropDownListに即時反映したい。(但し当該画面初期表示時はDropDownList=全件) RRS feed

  • 質問

  • 非常に低レベルな話ですが、頭を悩ましております。LoadやTextChangeの概念が理解できていないのかも知れません。お手透きの方ございましたらご教示をお願い致します。

    弊社取扱商品のコード検索画面を作りました。(VS2012/VB.NET)

    TextBox一つとDropDownList一つ、行を選択した後に押下するコマンドボタン一つのWebフォームです。当該画面のLoad時に、OracleDBからの上記DropDownListのデータソースを生成しています。そのデータソースは、Webフォーム上のテキストボックスの内容も絡めた(接続した)SELECT文のデータセットです。

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)  

    strSQL = "SELECT 品名, 品名コード FROM MST_ITEM WHERE 品名 LIKE '%" & TextBox1.Text & "%' ORDER BY 品名コード"

    DropDownList1.DataSource = CreateDataSetという関数(strSQL)
    DropDownList1.DataTextField = "品名"
    DropDownList1.DataValueField = "品名コード"
    DropDownList1.DataBind()

    End Sub

    当該画面が「他画面より呼ばれた初回表示時」DBの全件がDropDownListに現れます(期待どおり)。 次にユーザがTextBoxへ検索したいキーワードを入力することで、DropDownListに格納される件数が制限され、【即】そのDropDownListがリフレッシュされる動作を期待しています。

    下記は当初より変更を加えていないTextChangeイベントであり、実は 現在一応に上記の希望動作が達成できています。上下の組み合わせで。

    Protected Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
            DropDownList1.Items.Clear()
            DropDownList1.Items.Add(New ListItem("指定して下さい", String.Empty))
            DropDownList1.DataBind()
    End Sub

    ただ、デバッグモードでSelect発行がやたらいききしていることに気がつき、放っておけない気持ちになりました。DropDownListから行を選択した際に押下するコマンドボタン押下後にまで、DropDownlListのデータセットを再生成しようとしている始末です...。

    当初上記Loadイベントには Not IsPostBackの際のみデータセットが生成されるな記述をしていました。テキストチェンジイベントの際だけ、DropDownListへ再度データがBindされれば良いと信じていたためです。しかしながら、この組み合わせ(Loadイベント内の実動作はNot PostBackの際のみ&上記TextChangeイベント記述)では、初回表示は良いものの、テキストボックスに指定値を入れた後は、"!指定してください"の1行になってしまう状況でした。

    また、やたらとLoadイベントに入ってくることがデバッグモードで確認できたので(全てのコントロールがAutoPostBack=Trueのためと思われる)、テキストチェンジイベントもいらないのでは?(画面のテキストボックスを採用したSelect文にしているし)と思い、こちらをコメントアウトしてみれば....、テキストボックスの内容を無視した初回と代わらないDropDownListの内容となりました。

    AutoPostBackをTrueにしたコントロールをフォームに配置しているので、やたらとLoadイベントが走るのはやむを得ないと思いますが、一般的にどういったやり方で、必要最低限にデータソースを最新化する動作を達成できるのでしょうか?初歩的なことと思いますが、何卒よろしくお願い申し上げます。

    長々とすみません。


    • 編集済み saya24 2013年2月27日 14:53
    2013年2月27日 14:50

回答

  • ユーザー入力でクエリを組み立てるのは SQL インジェクションの問題があります。問題とは関係ないですが、何よりまずそこを直す(パラメータ化クエリを使うなど)ことをお勧めします。


    > 当初上記Loadイベントには Not IsPostBackの際のみデータセットが生成
    > されるな記述をしていました。テキストチェンジイベントの際だけ、
    > DropDownListへ再度データがBindされれば良いと信じていたためです。
    > しかしながら、この組み合わせ(Loadイベント内の実動作はNot PostBack
    > の際のみ&上記TextChangeイベント記述)では、初回表示は良いものの、
    > テキストボックスに指定値を入れた後は、"!指定してください"の1行にな
    > ってしまう状況でした。

    上記の問題の原因ですが、Load イベントのハンドラで「Not IsPostBackの際のみデータセットが生成」とすると、ポストバックの際はデータセットは生成されないからです。データが存在しないから、「"指定してください"の1行になってしまう」という結果になるのでしょう。

    解決方法は、まず Load イベントのハンドラでは、最初のプランどおり Not IsPostBack が True の時のみデータセットの生成、DropDownList へのバインドが行われるようにします。

    次に、TextChanged イベントのハンドラでは「CreateDataSetという関数(strSQL)」でデータセットを生成し、その中身が空なら "指定して下さい" を表示、データがあればデータセットを DropDownList.DataSource に設定してから DataBind すれば、望みの結果になると思います。


    以下はオマケの情報です。

    <その1>
    TextBox と DropDownList を組み合わせて使っているのは苦肉の策だと思いますが、TextBox に入力して行った時にリストが現れて、それに候補が表示されればいいのではないですか?

    ASP.NET Ajax Control Toolkit の Autocomplete を使ってはいかがですか? DropDownList 風にしたいのであれば ComboBox があります(ただしComboBox は日本語に対応してませんが)。

    jQuery UI が使えるなら、その Autocomplete のデモページの Examples の中の Combobox が DropDownList 風の表示になり、日本語にも対応しています。

    jQuery UI - Autocomplete
    http://jqueryui.com/autocomplete/

    <その2>
    DropDownList のようなデータバインドコントロールのデータソースには SqlDataSource や ObjectDataSource を使うことを検討してください。DataBind が自動的に行われる(逆に、必要のないときは行わない)ようになるなどメリットが多いです。

    ただし、ODP.NET で SqlDataSource を使った場合、Visual Studio のデザイン画面でウィザードを利用してのクエリの組み立てがうまくいかず、コード画面で自力でコードを書かないとダメかもしれません。

    ORACLE参照によるSQL記載方法
    http://social.msdn.microsoft.com/Forums/ja-JP/aspnetja/thread/1454d071-1a16-4e69-a651-1c2cb30b8550

     

    • 回答としてマーク saya24 2013年3月1日 0:29
    2013年2月28日 12:13
  • > データセットの中身が代わっていない状況可で、再度DropDownList
    > にでデータをバインドしたところで何も変わらない、そういうこと
    > だったみたいです。

    そうではありません。

    ポストバックした時には、初期画面をレンダリングするときに生成したデータセットは存在しません。(サーバーのメモリから消去されてます。Web アプリはそういう仕組みです)

    なので、ポストバックした時 DropDownList にリストを表示するには、ViewState からデータを得るか(これは ViewState を無効にしてなければ、ASP.NET が自動的にやってくれます)、再度データセットを生成してデータバインドすることになります。

    今回の場合は、TextBox の内容で DropDownList のリストに表示するデータが変わるはずですから、TextChanged イベントで再度データセットを生成してデータバインドする必要があります。

    Button をクリックするなどして(TextBox の内容を変えないで)ポストバックした場合は、ViewState からデータを取得して前回と同じリストの内容を表示するはずです(ViewState を無効にしてなければ)。

    Web アプリは、Windows アプリと違って、ステートレスということを強く意識して開発しましょう。でないとまた今回のようにハマります。


    SqlDataSource が使えないという件については、状況が見えないので、すみませんがお役に立てるコメントはできないです

    • 回答としてマーク saya24 2013年3月2日 4:16
    2013年3月1日 14:25

すべての返信

  • > 全てのコントロールがAutoPostBack=Trueのためと

    なぜこのようにしているのですか?
    AutoPostBackは実際にはどのような動作をしているか理解されてます?
    とりあえずAutoPostBackはTextBoxにだけ設定すればいいよーな。。。


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    2013年2月28日 2:34
  • ユーザー入力でクエリを組み立てるのは SQL インジェクションの問題があります。問題とは関係ないですが、何よりまずそこを直す(パラメータ化クエリを使うなど)ことをお勧めします。


    > 当初上記Loadイベントには Not IsPostBackの際のみデータセットが生成
    > されるな記述をしていました。テキストチェンジイベントの際だけ、
    > DropDownListへ再度データがBindされれば良いと信じていたためです。
    > しかしながら、この組み合わせ(Loadイベント内の実動作はNot PostBack
    > の際のみ&上記TextChangeイベント記述)では、初回表示は良いものの、
    > テキストボックスに指定値を入れた後は、"!指定してください"の1行にな
    > ってしまう状況でした。

    上記の問題の原因ですが、Load イベントのハンドラで「Not IsPostBackの際のみデータセットが生成」とすると、ポストバックの際はデータセットは生成されないからです。データが存在しないから、「"指定してください"の1行になってしまう」という結果になるのでしょう。

    解決方法は、まず Load イベントのハンドラでは、最初のプランどおり Not IsPostBack が True の時のみデータセットの生成、DropDownList へのバインドが行われるようにします。

    次に、TextChanged イベントのハンドラでは「CreateDataSetという関数(strSQL)」でデータセットを生成し、その中身が空なら "指定して下さい" を表示、データがあればデータセットを DropDownList.DataSource に設定してから DataBind すれば、望みの結果になると思います。


    以下はオマケの情報です。

    <その1>
    TextBox と DropDownList を組み合わせて使っているのは苦肉の策だと思いますが、TextBox に入力して行った時にリストが現れて、それに候補が表示されればいいのではないですか?

    ASP.NET Ajax Control Toolkit の Autocomplete を使ってはいかがですか? DropDownList 風にしたいのであれば ComboBox があります(ただしComboBox は日本語に対応してませんが)。

    jQuery UI が使えるなら、その Autocomplete のデモページの Examples の中の Combobox が DropDownList 風の表示になり、日本語にも対応しています。

    jQuery UI - Autocomplete
    http://jqueryui.com/autocomplete/

    <その2>
    DropDownList のようなデータバインドコントロールのデータソースには SqlDataSource や ObjectDataSource を使うことを検討してください。DataBind が自動的に行われる(逆に、必要のないときは行わない)ようになるなどメリットが多いです。

    ただし、ODP.NET で SqlDataSource を使った場合、Visual Studio のデザイン画面でウィザードを利用してのクエリの組み立てがうまくいかず、コード画面で自力でコードを書かないとダメかもしれません。

    ORACLE参照によるSQL記載方法
    http://social.msdn.microsoft.com/Forums/ja-JP/aspnetja/thread/1454d071-1a16-4e69-a651-1c2cb30b8550

     

    • 回答としてマーク saya24 2013年3月1日 0:29
    2013年2月28日 12:13
  • 小野@どっとねっとふぁん様

    ご見解を頂きまして真にありがとうございます。当方うそを申していました。

    DropDownListはAutoPostBackはFalseのままでした。

    SuferOnWww様のご見解で気がつきました。データセットの中身が代わっていない状況可で、再度DropDownListにでデータをバインドしたところで何も変わらない、そういうことだったみたいです。確かにTextBoxのTextChangeイベントで データセットをリニュアルすることをしていませんでしたから。

    また何かの機会でご支援頂けましたら幸いでございます、ありがとうございました。

    2013年3月1日 0:35
  • SuferOnWww様

    いつもありがとうございます。データセット&データソースの中身が代わっていない状況可で、再度DropDownListにデータをバインドしたところで何も変わらない、そういうことだったみたいですね。ご見解を頂きまして気が付くことができました。重ねて御礼を申し上げます。

    【冒頭とその2に記載頂きました件に関し】

    従来は確かにSQLDataSourceをフォーム上に配置しておりました。現在も今も参照先となっているDBはOracleですが、以前はそのSQLDataSourceの定義上にSystem.Data.OracleProviderを使用していました。(VS2005で構築した時代) この背景には当時VSのツールボックスへODP.NETのアイコン4種が現れなかったことに起因したと記憶しています。

    今回自分の端末がVS2012になり、ツールボックスへ再度ODP.NETのアイコンを表示させようとトライしました!しかし失敗に終わりました。(これは関係ない話しかな)

    しかし、ツールボックスへアイコンが表示されずともSQLDataSouraceの接続文字列にODP.NETから確立された接続文字列を選択すればデータを取得できることを知りました。それ以降は いけいけどんどん でこの方策で開発を進めていきました。自分の開発端末で!!

    いざ、本番運用先サーバーへ開発したWebフォームを配置してみると....

    ODP.NETのバージョンが自身の開発端末と違うとのエラーで、SQLDataSourceを画面に貼り付けたWebフォーム達全てが動作しない事象を招いてしまいました。

    たしか、本番運用サーバーの当該サイトのBinフォルダに開発端末からOracleDataAccess.dllを配置した対応もとった記憶です。それでも上記Webフォームたちは動作しませんでした。そもそもインストールされているOracleクライアントが本番運用サーバーと自身の開発端末で異なることに起因しているななぁと勝手に解釈し......

    それ以降私の中に、OracleをDBとするデータソースを生成する際、Webフォーム上に予めSQLDataSourceを張ることはご法度!と定義づけられたのです。すなわちフォームのLoad時にデータセットをこしらえて これをデータソースとする方針で開発を進めています。(つまり SQLDataSourceをあきらめた)

    開発マシンに、本番運用サーバーと同じOracleCleintのVerをインストールすれば ひょっとしたら解決の兆しが見えるかもしれません(下げる)、しかし、あんまりこのOracleClientの再インストールに自信をもてず...実行に踏み切れていない、というのが実状です。

    私共のOracleDBが11gへあがる予定も今秋に予定されているので、この際本番運用サーバーと自分の開発端末のOracleClientを最新化&同一にしたいと考えています。

    AjaxのAutoCompleteの件、ModalPopUpExtenderに引き続いて非常に興味をそそられました。今回は採用を見送りますが、近日中に別フォームでトライしてみます。有用な情報をありがとうございました。

    長々とすみません

    2013年3月1日 3:53
  • > データセットの中身が代わっていない状況可で、再度DropDownList
    > にでデータをバインドしたところで何も変わらない、そういうこと
    > だったみたいです。

    そうではありません。

    ポストバックした時には、初期画面をレンダリングするときに生成したデータセットは存在しません。(サーバーのメモリから消去されてます。Web アプリはそういう仕組みです)

    なので、ポストバックした時 DropDownList にリストを表示するには、ViewState からデータを得るか(これは ViewState を無効にしてなければ、ASP.NET が自動的にやってくれます)、再度データセットを生成してデータバインドすることになります。

    今回の場合は、TextBox の内容で DropDownList のリストに表示するデータが変わるはずですから、TextChanged イベントで再度データセットを生成してデータバインドする必要があります。

    Button をクリックするなどして(TextBox の内容を変えないで)ポストバックした場合は、ViewState からデータを取得して前回と同じリストの内容を表示するはずです(ViewState を無効にしてなければ)。

    Web アプリは、Windows アプリと違って、ステートレスということを強く意識して開発しましょう。でないとまた今回のようにハマります。


    SqlDataSource が使えないという件については、状況が見えないので、すみませんがお役に立てるコメントはできないです

    • 回答としてマーク saya24 2013年3月2日 4:16
    2013年3月1日 14:25
  • SuferOnWww様

    データセットがポストバック後は空になっている、為と理解を改めました。ありがとうございます。

    画面上に表示されるコントロールはステートレスであることを意識してきましたが、まさかデータセットまでとは。

    これは以降大変有用な情報だと思います。

    SQLDataSourceを画面に貼りつけた方針であれば、ポストバック後はBindだけでいいのでしょうね。

    今回もありがとうございました。

    2013年3月2日 4:26