C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: OracleDataAdapter.Updateについて


(過去ログ 113 を表示中)

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■66880 / inTopicNo.1)  OracleDataAdapter.Updateについて
  
□投稿者/ びぎなー (3回)-(2013/06/01(Sat) 16:23:20)

分類:[C#] 

最近ここでお世話になっております。
ご教授頂いた方、ありがとうございました。

C#を最近する事になって、色々と分からない事があるので
どうかお力をお貸し下さい。

Oracleから取得したDataTableをフォームとバインドしています。
単純に、そのDataTableをOracle.DataAccess.ClientのOracleDataAdapter.Update(Datatable)
で更新したいのですが、OracleDataAdpterが自動生成してくれるUpdate文では更新が
うまくいかず、自前でUpdate文、Delete文を作ってOracleDataAdapter.UpdateCommandに代入しました。

ここで分からないのが、自前でUpdateCommandとDeleteCommandを設定した場合、OracleDataAdapter.Update(Datatable)
で更新するとして、それぞれのクエリのバインド変数とDataTableをリンクできるOracleParameterの記述がさっぱり
思い浮かびません。
どのようにしたらいいのでしょうか?







引用返信 編集キー/
■66882 / inTopicNo.2)  Re[1]: OracleDataAdapter.Updateについて
□投稿者/ WebSurfer (25回)-(2013/06/01(Sat) 17:02:39)
No66880 (びぎなー さん) に返信
> 最近ここでお世話になっております。
> ご教授頂いた方、ありがとうございました。
>
> C#を最近する事になって、色々と分からない事があるので
> どうかお力をお貸し下さい。
>
> Oracleから取得したDataTableをフォームとバインドしています。
> 単純に、そのDataTableをOracle.DataAccess.ClientのOracleDataAdapter.Update(Datatable)
> で更新したいのですが、OracleDataAdpterが自動生成してくれるUpdate文では更新が
> うまくいかず、自前でUpdate文、Delete文を作ってOracleDataAdapter.UpdateCommandに代入しました。

Oracle は分かりませんが ODP.NET が SQL Server 用と全く同じに作ってあれ
ば、CommandBuilder オブジェクト(Oracle の場合 OracleCommandBuilder?)
を生成してやれば、InsertCommand、UpdateCommand、DeleteCommand の各プロ
パティが null 参照である場合、自動的に UPDATE, DELETE, INSERT のための
コードを生成するはずです。

具体的には以下のページの「リスト 6 非接続データアクセスでの更新サンプル」
を見てください。

DB 設計者のための明解 ADO.NET 第 1 回
http://msdn.microsoft.com/ja-jp/events/dd231281.aspx


もちろん、CommandBuilder オブジェクトを生成しないで、自力で UPDATE,
DELETE, INSERT のためのコードを書くことも、ODP.NET が SQL Server 用
と全く同じに作ってあれば可能です。

引用返信 編集キー/
■66884 / inTopicNo.3)  Re[2]: OracleDataAdapter.Updateについて
□投稿者/ びぎなー (4回)-(2013/06/01(Sat) 21:35:10)
2013/06/01(Sat) 21:36:08 編集(投稿者)
WebSurferさん、返信有難うございました。

>>具体的には以下のページの「リスト 6 非接続データアクセスでの更新サンプル」
>>を見てください。
>>DB 設計者のための明解 ADO.NET 第 1 回
>>http://msdn.microsoft.com/ja-jp/events/dd231281.aspx

参照のWEBサイト有難うございます。まだプログラム自体が初心者域なので
分からない事がおおくて・・・ゆっくりみたいと思います。

実は、DataAdapterで自動で生成されるUpdateCommandやDeleteCommandの楽観的排他制御というのが
都合がわるくて自前でクエリを設定しています。

ADO.NETならOracleDataAdapter.ConflictOption = ConflictOption.OverwriteChangesが効いてくれて
問題なかったのですが、ODP.NETはまだ実装されてないらしく、今回このような事をしようかと思って
おります。
プログラムは一部抜粋して以下の様に行っています。


        int i;

        OracleDataAdapter adaper = this.createDataAdapter(tableName,table);

        i = adaper.Update(table);
        
        private OracleDataAdapter createDataAdapter(string tableName,DataTable dt)
        {
            OracleCommand selectCommand =
                new OracleCommand(string.Format("SELECT * FROM {0}", tableName),
                    this._Connection);
            selectCommand.Transaction = this._Transaction;           

            OracleDataAdapter adaper = new OracleDataAdapter(selectCommand);
            OracleCommandBuilder builder = new OracleCommandBuilder(adaper);

            adaper.InsertCommand = builder.GetInsertCommand();

            adaper.UpdateCommand = getUpdateCommand(tableName);  //スキーマーからUpdateCommandを生成しています。
            adaper.UpdateCommand.BindByName = true;
            adaper.UpdateCommand.Parameters.AddRange(getUpdateParamater(tableName,dt).ToArray());  //※1

            adaper.DeleteCommand = GetDeleteCommmand(tableName);   //スキーマーからDeleteCommandを生成しています。
            adaper.DeleteCommand.BindByName = true;
            adaper.DeleteCommand.Parameters.AddRange(getDeleteParamater(tableName,dt).ToArray());  //※2

            adaper.SelectCommand.Connection = this._Connection;
            adaper.InsertCommand.Connection = this._Connection;
            adaper.UpdateCommand.Connection = this._Connection;
            adaper.DeleteCommand.Connection = this._Connection;
    
            return adaper;
        }

    //UpdateParamaterの取得
        private  List<OracleParameter> getUpdateParamater(string tableName,DataTable dt)
        {
            List<OracleParameter> l = new List<OracleParameter>();

            OracleCommand oracmd =
                          new OracleCommand(string.Format("SELECT * FROM {0}", tableName),
                          this._Connection);

            OracleDataReader r = oracmd.ExecuteReader(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly);
            DataTable tbl = r.GetSchemaTable();

            foreach(DataRow dr in tbl.Rows)
            {
                OracleParameter prm = new OracleParameter(dr["ColumnName"].ToString(), //※3ここがわかりません。。
                l.Add(prm);
            }

            return l;
        }


ここで、UpdateCommandとDeleteCommandを自前で作っているのでUpdateとDeleteはParameterを設定してあげないと
いけないと思っており※1と※2のメソッドは作ったのですが、※3の所に何のObjectを入れていいかさっぱりです。。
adapter.Update(Datarow)ならば、単純にValueを入れればいいのでしょうが・・。

知識不足で申し訳ないです。お力お貸しください。




引用返信 編集キー/
■66885 / inTopicNo.4)  Re[3]: OracleDataAdapter.Updateについて
□投稿者/ 魔界の仮面弁士 (237回)-(2013/06/01(Sat) 22:38:36)
No66884 (びぎなー さん) に返信
> ※3の所に何のObjectを入れていいかさっぱりです。。

TABLE1 の列が、KEY1, KEY2, COL3, COL4 という構成で、
その中の KEY1/KEY2 が主キーだとして、

cmd.CommandText = "UPDATE TABLE1 SET COL3 = :COL3, COL4 = :COL4 WHERE KEY1 = :KEY1 AND KEY2 = :KEY2"
cmd.BindByName = True
Parameters.Add( "KEY1", OracleDbType.Char, 3, ParameterDirection.Input)
Parameters.Add( "KEY2", OracleDbType.Char, 3, ParameterDirection.Input)
Parameters.Add( "COL3", OracleDbType.Decimal, 12, ParameterDirection.Input)
Parameters.Add( "COL4", OracleDbType.Decimal, 12, ParameterDirection.Input)
Return cmd

という感じでどうでしょうか。(手元に ODP が無いので未検証)

キー情報、列名、型などはスキーマから拾ってみてください。
引用返信 編集キー/
■66887 / inTopicNo.5)  Re[4]: OracleDataAdapter.Updateについて
□投稿者/ ?т???[ (1回)-(2013/06/02(Sun) 00:10:37)
魔界の仮面弁士さん。またまた有難うございます!

ちょっとやってみます!
やっぱ明示的にそのようにする必要があるんですね。。。

なんとなくイメージが沸いてきました!!
引用返信 編集キー/
■66890 / inTopicNo.6)  Re[5]: OracleDataAdapter.Updateについて
□投稿者/ WebSurfer (26回)-(2013/06/02(Sun) 16:14:00)
以前検証のため作った SqlDataAdapter の場合の例ですが、ODP.NET でも多分
似たような形になると思いますので、参考に張っておきます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace SqlDataAdapterTest
{
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{

}

private DataSet dataset;
private SqlDataAdapter adapter;
private string connString;
private SqlConnection connection;
private BindingSource bindingSource;

public Form1()
{
InitializeComponent();

connString = Properties.Settings.Default.MyDB;
connection = new SqlConnection(connString);
adapter = CreateAdapter(connection);
dataset = CreateDataSet(adapter);
bindingSource = new BindingSource();
bindingSource.DataMember = "Cards";
bindingSource.DataSource = dataset;
bindingNavigator1.BindingSource = bindingSource;

// AutoGenerateColumns が True の場合は、データソースの各列に対して DataPropertyName が
// その列名になっている列が既に存在すればその列にバインドし、存在しなければ新しく列が作
// 成されます。AutoGenerateColumns が False の場合も、データソースの各列に対して
// DataPropertyName がその列名になっている列が既に存在すればその列にバインドしますが、
// 存在しなかった場合には新しい列を作成しません。
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = bindingSource;
}

public SqlDataAdapter CreateAdapter(SqlConnection connection)
{
// SqlDataAdapter の作成。
adapter = new SqlDataAdapter();

string query = "SELECT ID, CardYear, KindOfCard, SendMonth, SendDay, RecieveMonth, ReceiveDay, CardMemo FROM Cards";
SqlCommand command = new SqlCommand(query, connection);
command.CommandType = CommandType.Text;
adapter.SelectCommand = command;

query = "INSERT INTO Cards (ID, CardYear, KindOfCard, SendMonth, SendDay, RecieveMonth, ReceiveDay, CardMemo) " +
"VALUES (@ID, @CardYear, @KindOfCard, @SendMonth, @SendDay, @RecieveMonth, @ReceiveDay, @CardMemo)";
command = new SqlCommand(query, connection);
command.Parameters.Add("@ID", SqlDbType.Int, 0, "ID");
command.Parameters.Add("@CardYear", SqlDbType.Int, 0, "CardYear");
command.Parameters.Add("@KindOfCard", SqlDbType.Int, 0, "KindOfCard");
command.Parameters.Add("@SendMonth", SqlDbType.Int, 0, "SendMonth");
command.Parameters.Add("@SendDay", SqlDbType.Int, 0, "SendDay");
command.Parameters.Add("@RecieveMonth", SqlDbType.Int, 0, "RecieveMonth");
command.Parameters.Add("@ReceiveDay", SqlDbType.Int, 0, "ReceiveDay");
// MSDN ライブラリには「可変長データ型では、Size は、サーバーに送
// 信するデータの最大量を示します。」とあるが、0 の場合は制限しな
// い。0 以外の数字を入れると制限する。例えば以下のように 5 にする
// と 5 文字に制限される。5 文字を超えて入力してもエラーは出ない。
// DataSet には入力しただけ文字列が入るので、表示と DB の内容が
// 異なってしまう。やはり制限するならもっと前の段階の TextBox への
// 入力時にすべき。
command.Parameters.Add("@CardMemo", SqlDbType.NVarChar, 5, "CardMemo");
adapter.InsertCommand = command;

query = "UPDATE Cards " +
"SET ID = @ID, CardYear = @CardYear, KindOfCard = @KindOfCard, SendMonth = @SendMonth, SendDay = @SendDay, " +
"RecieveMonth = @RecieveMonth, ReceiveDay = @ReceiveDay, CardMemo = @CardMemo " +
"WHERE (([ID] = @Original_ID) AND ([CardYear] = @Original_CardYear) AND ([KindOfCard] = @Original_KindOfCard))";
command = new SqlCommand(query, connection);
command.Parameters.Add("@ID", SqlDbType.Int, 0, "ID");
command.Parameters.Add("@CardYear", SqlDbType.Int, 0, "CardYear");
command.Parameters.Add("@KindOfCard", SqlDbType.Int, 0, "KindOfCard");
command.Parameters.Add("@SendMonth", SqlDbType.Int, 0, "SendMonth");
command.Parameters.Add("@SendDay", SqlDbType.Int, 0, "SendDay");
command.Parameters.Add("@RecieveMonth", SqlDbType.Int, 0, "RecieveMonth");
command.Parameters.Add("@ReceiveDay", SqlDbType.Int, 0, "ReceiveDay");
command.Parameters.Add("@CardMemo", SqlDbType.NVarChar, 5, "CardMemo");
SqlParameter parameter = command.Parameters.Add("@Original_ID", SqlDbType.Int, 0, "ID");
parameter.SourceVersion = DataRowVersion.Original;
parameter = command.Parameters.Add("@Original_CardYear", SqlDbType.Int, 0, "CardYear");
parameter.SourceVersion = DataRowVersion.Original;
parameter = command.Parameters.Add("@Original_KindOfCard", SqlDbType.Int, 0, "KindOfCard");
parameter.SourceVersion = DataRowVersion.Original;
adapter.UpdateCommand = command;

query = "DELETE FROM Cards WHERE " +
"((ID = @Original_ID) AND (CardYear = @Original_CardYear) AND (KindOfCard = @Original_KindOfCard))";
command = new SqlCommand(query, connection);
parameter = command.Parameters.Add("@Original_ID", SqlDbType.Int, 0, "ID");
parameter.SourceVersion = DataRowVersion.Original;
parameter = command.Parameters.Add("@Original_CardYear", SqlDbType.Int, 0, "CardYear");
parameter.SourceVersion = DataRowVersion.Original;
parameter = command.Parameters.Add("@Original_KindOfCard", SqlDbType.Int, 0, "KindOfCard");
parameter.SourceVersion = DataRowVersion.Original;
adapter.DeleteCommand = command;

return adapter;
}

public DataSet CreateDataSet(SqlDataAdapter adapter)
{
// DataSet の作成。
DataSet ds = new DataSet();
adapter.Fill(ds, "Cards");
DataTable table = ds.Tables["Cards"];

// 主キーは以下のようにしないと設定されない(自動的には設定されない)。
// 制約違反例外をスローするのは DataSet/DataTable でなく、DataGridView になる。その例外を処置するには
// DataGridView.DataError イベントを利用する。
// DataGridView に制約情報を渡しているのは BindingSource なのか?・・・と思ったがそうではない。
table.PrimaryKey = new DataColumn[] { table.Columns["ID"], table.Columns["CardYear"], table.Columns["KindOfCard"] };
//DataColumn[] columns = table.PrimaryKey;
//int num = columns.Length;

// 制約を DataTable に追加するには DbDataAdapter.FillSchema メソッドというのもある。

return ds;
}

private void toolStripButtonSaveItem_Click(object sender, EventArgs e)
{
try
{
this.Validate();
this.bindingSource.EndEdit();
this.adapter.Update(dataset, "Cards");
MessageBox.Show("Update successful");
}
catch (System.Exception ex)
{
MessageBox.Show("Update failed", ex.Message);
}
}

}
}

引用返信 編集キー/
■66892 / inTopicNo.7)  Re[6]: OracleDataAdapter.Updateについて
□投稿者/ びぎなー (5回)-(2013/06/02(Sun) 20:13:47)
2013/06/02(Sun) 20:17:17 編集(投稿者)
2013/06/02(Sun) 20:16:28 編集(投稿者)
2013/06/02(Sun) 20:15:07 編集(投稿者)

<pre><pre>魔界の仮面弁士さん、WebSurferさん、有難うございました。
希望通りにデータ更新できる様になりました。

WebSurferさんのサンプルデータもまだしたことない事が
書いてあるので参考にさせて頂きます。
実際に書かれたサンプルデータが一番勉強になりますね!

パラメーターの設定は以下の様にしました。
まだまだ修正は必要ですが・・・


        private  List<OracleParameter> GetParamater(string tableName,DataTable dt)
        {
            List<OracleParameter> l = new List<OracleParameter>();

            OracleCommand oracmd =
                          new OracleCommand(string.Format("SELECT * FROM {0}", tableName),
                          this._Connection);

            OracleDataReader r = oracmd.ExecuteReader(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly);
            DataTable tbl = r.GetSchemaTable();

            foreach(DataRow dr in tbl.Rows)
            {
                OracleParameter prm;
                switch(dr["DataType"].ToString())
                {
                    case "System.String":
                        {
                            prm = new OracleParameter(dr["ColumnName"].ToString(), OracleDbType.Char, (int)dr["ColumnSize"],                           dr["ColumnName"].ToString());
                            break;
                        }
                    case "System.Decimal":
                        {
                            prm = new OracleParameter(dr["ColumnName"].ToString(), OracleDbType.Decimal, (int)dr["ColumnSize"],                          dr["ColumnName"].ToString());
                            break;
                        }
                    default:
                        {
                            //TODO 他の型はあとで考える
                            prm = new OracleParameter(dr["ColumnName"].ToString(), OracleDbType.Char, (int)dr["ColumnSize"],                       dr["ColumnName"].ToString());
                            break;
                        }
                }
                
                l.Add(prm);
            }

            return l;
        }</pre></pre>

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -