読者です 読者をやめる 読者になる 読者になる

プログラミングと日々思ったことなど

ブログ名通りです。2017年3月から、仕事でプログラミングをはじめました。関東あたりに在住。

SqlServerでストアドプロシージャを作成する

ストアドプロシージャを作成したので、復習です。
このストアドプロシージャはインサート処理と、エラー表示を行います(多分・・・)。

USE [AA_DB]
GO

CREATE PROCEDURE [aa]
	(@log VARCHAR(5)				--パラメータを宣言
	,@name VARCHAR(30)		
	,@MsgReturn VARCHAR(2000) OUTPUT
	)

AS
	DECLARE @SQL NVARCHAR(4000)		--変数を宣言
	DECLARE @DefMsg VARCHAR(1000)
	DECLARE @ReturnVal int
	DECLARE @Err INTEGER

BEGIN
	SET @DefMsg='ストアドエラー'
	SET @ReturnVal = 2                     --失敗したら2がかえってくる

	/** 引数チェック **/
	IF(@log IS NULL OR @log = '' OR
		@name IS NULL OR @name = '' OR
		)
	BEGIN
		SET @MsgReturn = @DefMsg + '引数未設定'
		RETURN @ReturnVal
	END

	BEGIN TRY

	SET @SQL = N'INSERT INTO [AA] VALUES
				('''+ @log + ''''			--引数を文字列として認識するための、コーテーション
					+N',''' + @name +N''')'	--NはUnicodeとして処理させるためにつける
	EXECUTE @Err = sp_executeaql @SQL --@Errに、インサートが失敗したのか成功したのかわかる値が入っている
	IF @Err <> 0
	BEGIN
		SET @MsgReturn = @DefMsg + 'インサート失敗'
		RETURN @ReturnVal
	END

	SET @ReturnVal = 0

	END TRY
	BEGIN CAHTCH
		SET @MsgReturn = @DefMsg + '[ERROR_NUMBER]'+@SQL
		RETURN @ReturnVal
	END CAHTCH
END

GO

変数とパラメータが、少しだけ混乱する時があるので気をつけます。

キャストについて【C#】

昨日は、キャストが思いつかず悔しい思いをしました。
忘れないように、コードを残します。

問題のキャスト処理を書く前に、条件分岐の処理も復習します。

ublic void kakunin_Click(C1.WPF.DataGrid.DataGridEventArgs e)
{
	this.IsRegistEnable = true; //登録ボタンにバインドさせている。
	bool isError = false;
	string name = e.Column.Name;

	DataRowView dv = (DataRowView)e.Row.DataItem;
	dv.Row.ClearErrors();
	e.Row.Errors.Clear();

	DataGridRowError  dgreroor = new DataGridRowError();

	//必須入力チェック
	//sentakuメソッドで、DataGridの項目の選択か必須かを判断している。
	if(!this.sentaku(name))
	{
		//DtoCheckメソッドにて、エラーの出ているメソッドを判別。
		isError = this.DtoCheck(e.Column,dv.Row)
		if(isError)
		{
			dgreroor.ColumnNames.Add(name);
			dgreroor.Message="必須入力エラー";
			e.Row.Errors.Add(dgreroor);
			dv.Row.SetColumnError(name,"[項目:"+ name + "]を入力してください");
		}
	}

	//switchで、入力した値が項目に合う値なのか(数字なのか、半角英数なのかなど)判別。
	switch(name)
	{
		case"aa":
		case"bb":
		if(!InputAuxiliaryUtil.IsNumeric(dv[name].Tostring()))
		{
			dgreroor.ColumnNames.Add(name);
			dgreroor.Message = "入力エラー"
			e.Row.Errors.Add(dgreroor);
			dgreroor.Row.SetColumnError(name,"数字を入力してください");
		
			isError = true;
		}
		break;

		case"cc":
		if(....)
		{
			//略...
		}
		break;

	}

	//エラーがあった場合、登録ボタンを使えないようにする。
	if(isError)
	{
		e.Cansel = true;
		this.IsRegistEnable = false;

	}

}

SetColumnErrorというのが、DataGridの問題セルに出現するエラーです。(見た目はフキダシみたいなものです)
InputAuxiliaryUtil.IsNumericというメソッドで、判定させます。

#region 文字チェック

public static bool IsNumeric(string value)
{
	if(null == value)
	{
		return true;
	}
	if(string.IsNullOrEmpty(value.Trim()))
	{
		return true;
	}
	if(!Regex.IsMach(value.Trim(),REGEX_Numeric))
	{
		return false;
	}

	return true;
}

#endregion


また、この文字チェック内で使われているREGEX_Numericは、辿っていくと正規表現にたどり着きます。

//こんなの
//これは例なので、正規表現は半角英数字の判別設定です。

private static string REGEX_Numeric = "^[-_/0-9a-zA-Z]+$";

DataGridは表示されるだけなので、入力文字についてはこのように自分で書いていかなければならないのです。

次はキャストです。

else if(e.Column.Name == "ff")
{
	dv[name] = dv[name].Tostring().PadLeft(2,'0')
}

これです。
間違って書いてコードは以下になります。

//このコードは間違っています。
else if(e.Column.Name == "ff")
{
	var i = (DataRowView)e.Row.DataItem;
	i.PadLeft(7,'0')

	string col = e.Column.Tostring();
	col.PadLeft(7,'0')
}

iやcolには値が入っていますが、オブジェクトを知りません。
ただ値を入れているという処理をしているだけなので、DataGridには何も変化がない、です。

値が入っているプロパティを見つけ、キャストして使用できるようにするまでが難しいな・・・。

オブジェクト指向プログラミングについて

オブジェクト指向プログラミング(Object Oriented Programming = OOP)とは
プログラムを書く人自身が、プログラムを把握しきれなくなることを防ぐためのプログラム設計方法です。

このプログラム設計方法の特徴は、3つあります。
カプセル化
 フィールドへの読み書きやメソッドの呼び出しを制限します。
 制限することによって、重要(プログラム中で、書き換えてはいけないもの)な情報(フィールド)や操作(メソッド)を保護することができます。
 これは誤りの起きにくいプログラム、つまりはクラスを設計する時に役立ちます。

②継承
 新しいクラスを作るとき、そのクラスがすでに作られているクラスと類似している部分がある場合があります。

 例)
 車の設計が書かれたクラスがもともとあって、新しくスポーツカーの設計を書く必要がある。

 そのときに、すでに作られたクラスのメンバ変数・メソッドを受け継ぐ仕組みです。
 もともとあったクラスは、基本クラスや親クラスと言います。
 新しく作るクラスを、派生クラスや子クラスと言います。

③多様性(ポリモーフィズム
 メソッドに多様な振る舞いをさせることです。
 多様な振る舞いをさせるメソッドは、抽象的になります。
 抽象的になるというのは、簡単に言えば(おそらく)「5m動く」を「動く」にする、ということです。
 「動く」にすると、各クラス内で何メートル動くのかを決めることができます。
 そうすることによって、効率的にプログラム開発を行えます。


今はこれが限界です。
1年後、詳しく書けるように頑張ろう。

DataGridの必須入力チェック【WPF】

今週も頑張りました。
オブジェクト指向の前に、昨日の復習を記事にします(難しかった・・・)

昨日会社で教えてもらったのは、DataGridの必須入力チェックです。
DataGridに表示されているデータを編集したとき、必須項目が見入力だった際にその項目の枠が赤くなり、かつ、「必須エラーです」とメッセージが表示される処理です。

この必須入力チェックは、登録ボタン(コマンド)を押すと処理されます。
まずコマンド内でどの項目がエラーなのか判別処理・表示文章設定します。
その後ViewのTargetUpdated内で、実際にエラーメッセージが表示されるように処理していきました。


DataGridViewModel.cs
//このbtnRegist_Clickは、登録ボタンのコマンドに仕込んであるもの。
public void btnRegist_Click()
{
	bool isError = this.DtoCheck();

	//チェック処理
	if(isError)
	{
		this.RaisePropertyChanged("Views");
		return;
	}

	if(MessageBoxResult.Yes == System.Windows.MessageBox.Show("変更項目を登録しますか?","登録変更確認",MessageBoxButton.YesNo))
	{
		testDataGridModel model =
			new testDataGridModel(this.Dto);
		model.InsertData(updateflag);

		//登録変更が成功したか

		isSuccess = model.IsSuccess;
		if(isSuccess)
		{
			//略...登録更新の成功・失敗メッセージ処理...
		}
	}
}

private bool DtoCheck()
{
	bool isError = false;
	//エラーフィールドのクリア
	//ここで中身をクリアにしておかないと、連続処理したときに前の処理が残ってしまう。
	foreach(DataRow dr in this.Dto.testData.Rows)
	{
		dr["ERROR_FIELD"] = string.Empty;
		dr["ERROR_MSG"] = string.Empty;
		dr.ClearErrors();
	}

        //foreachの中にforeachでRowとColumnを1つずつ取り出している(結果1つのCellずつ処理ができる)
	foreach(DataRow dr in this.dto.testData.Rows)
	{
		//testDataの列を一列ずつ読み込む
		foreach(DtaColumn dc in this.dto.testData.Columns)
		{
			//必須か選択項目か確認 
			if(sentakuFields(dc.ColumnName))
			{
				continue;
			}

			if((dr[dc.ColumnName] == null) || (dr[dc.ColumnName].Tostring() == string.Empty))
			{
				//ここで空のERROR_FIELD行の中身を作っている
				if(dr["ERROR_FIELD"].Tostring() == string.Empty)
				{
					dr["ERROR_FIELD"] = dc.ColumnName;
				}
				else
				{
					dr["ERROR_FIELD"] += " " + dc.ColumnName; //区切り指定
				}

				dr["ERROR_MSG"] = dc.ColumnName + "で必須エラーです。¥r¥n";
				dr.RowError = dc.ColumnName + "で必須エラーです。¥r¥n";
				dr.SetColumnError(dc,dr["ERROR_MSG"].Tostring());

				isError = true;
			}
		}
	}
	return isError;

}

//選択項目を、ここで抜き出し
private bool sentakuFields(string strFieldName)
{
	bool isSentaku = false;

	switch(strFieldName)
	{
		case "aa":
		case "bb":

		isSentaku = true;
		break;
	}

	return isSentaku;
}
	
DataGrid.cs(View)
public test()

{
	InitializeComponent();

	//画面が表示されたときに、色々なデータを取得する処理
	ThisForm = ExPageParamsUtil.GetPageParams("TForms") as ExMenuWindow;
	//...略

	this.DataContext =
		new testViewModel(CommonTbls,exWin,cmbAuth);
	this.testDataGrid.TargetUpdated += 
	new EventHandler<DataTransferEventArgs>(testDataGrid_TargetUpdated);

}

private void testDataGrid_TargetUpdated(object sender,DataTransferEventArgs e)
{
	//エラー内容のクリア
	for(int idx = 0;idx < this.testDataGrid.Rows.Count;idx++)
	{
		this.testDataGrid.Rows[idx].Errors.Clear();
	}

	BindingListCollectionView chkView =
		this.testDataGrid.ItemSorce as BindingListCollectionView;

	//フィルタかける前のすべてのデータを取得する。
	DataTable dt = ((DataView)chkView.SorceCollection).ToTable().Copy();

	//フィルタ圧縮かける <>''←これは値が入っているかどうかを判断することができる。
        //RowFiletrで圧縮をかけると、番号がおかしくなるので注意。
	DataRow[] drs = dt.Select("ERROR_FIELD<>''");

	foreach(DataRow dr in drs)
	{
		//エラー項目及びエラー内容の取得
		DataGridRowError dgErr = new DataGridRowError();
		string[] errField = dr["ERROR_FIELD"].Tostring().Split('');

		foreach(string fld in errField)
		{
			dgErr.ColumnsNames.Add(fld);
			dgErr.Message = dr.GetColumnError(fld);

			//エラーオブジェクトを対象データグリッド行にセットする

			//エラー行番号の取得
			int intRowIdx = dt.Rows.IndexOf(dr);
			//データグリッドエラー対象行にエラーセット
			this.testDataGrid.Rows[intRowIdx].Errors.Add(dgErr);
		}
	}
}

以上なのですが、このコードだとセレクターにメッセージが表示されません。
どうすれば良いのか、調べてみようと思います。


-----ここから雑記

教えてもらっていたのに、復習してみるとわかっていないところが出てきました。
書いてみるとわかっていないところがわかるので、これからもコードを書いていこうと思います。

あと、色々コードを教えてもらっていて思っていたことを、昨日やっと聞くことができました。
foreachの中に、foreachやifが入っているのが変な感じがしていたのですが、それは処理が多くなりそうであれば、別のメソッドへ飛ばしても良いそうです。

些細なことも答えてくれる先輩方が、会社にたくさんいるのがとてもありがたいです。

そういやボーナスは出るのかなー。
出たらマイコンキットか、Windowsを購入したいのです・・・うーん難しいかな・・・。

Listでコンボボックスの項目を追加する(グリッドコントロール)

朝早く起きてしまったので、昨日の復習を書きます。

昨日はC1.WPF.DataGridのテキストボックス列(デフォルト)からコンボボックス列へ変更する処理をしました。
以下はそのコードです。

 private void c1DataGrid_AutoGeneratingColumns(object sender, C1.WPF.DataGrid.DataGridAutoGeneratingColumnsEventArgs e)
    {
        if(e.Property.Name == "aaaa")
        {
        //コンボボックス設定
        var cmbClmn = new C1.WPF.DataGrid.DataGridComboBoxColumn(e.Property);
        cmbClmn.DisplayMemberPath = "a.NAME";
        cmbClmn.SelectedValuePath = "a.CODE";

        DataView dv = new DataView(this.aTbls.Tables["a.CODE"]);
        dv.RowFilter = "a.CODE.ccc = '00'";

        DataTable dtCode = dv.ToTable();

        List<Model> models = new List<Model>();
        foreach(DataRow dr in dtCode.Rows)
        {
            Model data = new Model();
            data.a.NAME = dr["a.CODE"].ToString() + " " + dr["a.NEME"].ToString();
            data.a.CODE = dr["a.CODE"].ToString();
            models.Add(data);
        }

        Model data1 = new Model();
        data1.a.NAME = "00 その他";
        data1.a.CODE = "00";
        models.Add(data1);

            cmbClmn.ItemSource = models;
        }

        if(e.Property.Name == "bbbb")
        {
            var cmbClmn = new C1.WPF.DataGrid.DataGridComboBoxColumn(e.Property);
            cmbClmn.DisplayMemberPath = "a.NAME";
            cmbClmn.SelectedValuePath = "a.CODE";

            List<Model> models = new List<Model>();

            Model data1 = new Model();
            data1.a.NAME = "00 選択";
            data1.a.CODE = "00";
            models.Add(data1);

            cmbClmn.ItemSource = models;

        }
  

Listは別クラスにてModelの箱(a.NAMEと、a.CODEのプロパティと、WPFなのでプロパティチェンジを記述)を作成しています。

if文が2つあるのですが、Gridのヘッダー名で違った項目を表示することができるようにするためです。
1つ目のif文の中には、ループ文があります。
これは、もともとデータベースにあった項目を使用しているためです。
直接データベースからは持ってこれないので、
DataView dv = new DataView(this.aTbls.Tables["a.CODE"]); ←ここ
にて、DataViewの中身を指定したのちにフィルターをかけて使用する項目を指定しています。

2つ目はもともとある項目を使用していないので、新しい項目だけを追加しています。


また、デフォルトのテキストボックスだった時、各テキストボックスのデータは数字で表示されるようになっていました。
それはDtoから取得してきたデータが、数字であるためです。
それをこの処理をすることによって、数字の意味に適応した文字に変更しています。
変更したのは、数字だと何を意味しているのかわかりづらいからです。

以上です。

昨日はまだよくわかっていないListと、DataViewが作業で出てきました。
これで少し整理できてよかったです。

SQLクエリ実行処理など

今週も色々苦戦していました。
中でもSQL関係が気になったので、その復習です。

DaoBase(親クラス...多分)にコーディングされている実行処理の中で、SQL実行処理クエリがありました。

DaoBase.cs

//機能名称 SQLクエリ実行処理
//パラメータで受け取ったSQL文を実行し、結果セット

//SQL条件パラメータ
protected DataTable ExecuteQuery(string sSql, IDbDataParameter[] collection, string tblName)
{
       try {
       	  DataSet ds = new DataSet();

       	  connection = DbFactory.GetCommand(sSql,connection)
       	  comm.Parameters.Clear();
       	  if(conParams.Driver == "ORACLE")
       	  {
       	  	foreach(OracleParameter p in collection)
       	  	{
       	  	  comm.Parameters.Add(p);
       	  	}
       	  }else
       	  {     //ココの処理がよくわからない明日聞く
      // 5.16 ifの条件、DriverでORACLEかSqlserverか切り替えていた。
     //が、何らかの理由でその必要がなくなったため、この部分が残った(今ココは通りません)
       	  	foreach(OracleParameter p in collection)
       	  	{
       	  		comm.Parameters.Add(p);
       	  	}
       	  }
       	  log.Debug(comm.CommandText);
       	  //アダプタ作成
       	  adapter = DbFactory.CreateAdapter(comm);
       	  adapter.Fill(ds);
       	  ds.Tables[0].TableName = tblName;
       	  return ds.Tables[0];
       }
     catch(Exception ex)
     {
    	log.Info("SQLクエリ実行時エラー",ex);
    	return new DataTable();
     }
     finally
     {
    	DbFactory.ReleaseConnection(connection);
     }
   	}
}

これによって、子クラスのSQLクエリも実行されるようになります。

またconnectionは、web.config内でパラメータが定義されています。

<add key="DRIVER" value="ORACLE" />
<add key="ConnectionCnt" value="2" />

valueが2なので、connectionが2より多く繋がれると、エラーが発生します。

そうして、DaoBaseにてクエリ実行処理がコーディングされていることによって、実行できるのが以下の情報取得処理です。

namespace Logic
{
	public class DaoImpl:DaoBase,IDaoaa
	{
		readonly log4net.Ilog log =
					log4net.LogManager.GetLogger
					 (System.Reflaction.MethodBase.GetCurrentMethod().DeclaringType);
		 public DaoImpl()
		  :base()
		  {

		  }
		  //情報取得処理
		 public DtoaaList GetDtoList(DtoaaList dto)
		 {
		 	string sSql = string.Empty;
		 	sSql = @"
		 		SELECT
		 			aa
		 			,bb
		 			,cc
		 			,...略
		 		FROM aa_COLUMNS
		 		ORDER BY aa,bb
		 	";
		  try
		  {
		  	DataTable dt = ExecuteQuery(sSql,"aa_COLUMNS");
		  	dto.Merge(dt,true,MissingSchemeAction.Ignore);
		  }
		  catch(Exception ex)
		  {
		  	log.Error("テーブル情報取得エラー",ex);
		  }
		  return dto;
		 }
	}
}

この処理で、データをDtoから取ってきています。
なので、同じDaoの中でセレクト文を作成するときにはconnectionは使用しなくて良いのです。

ちなみにこの中でLIKE文を使用するときには、||で%を囲わなければ文字だと判別してくれないようです。


-----ここから雑記
このconnectionの話を以前にもしていただいたのに、忘れてしまっていて再度教えてもらいました。
ぬおー悔しい。

そして現在、その悔しさをバネにしつつこの参考書を使って自習してます。

www.amazon.co.jp


この参考書は、綺麗なコードになるまでを詳しく書いてくれているのでわかりやすいです。
わかりやすいといっても、実際にコーディングしながら進めているので数ページ読むのに随分時間がかかってしまいますが…。



今回は以上です。
来週はオブジェクト指向について、今ある知識で頑張って書いていきます。

SQLについて色々【メモ】

Execute Query

DataContextクラスのメソッド。
SQLクエリを直接記述して、実行することができます。

クエリを直接記述したとき

(テーブル項目)
   ↓
(値:@をつける)
   ↓
(パラメータ:@をつける)

@がキーワードとなって、値を取得します。

*クエリを書くときは、カンマを前に書いた方が良い。


サブスクエリ 副問い合わせ

SQLではSELECT文による問い合わせを入れ子にすることができます。
入れ子にした問い合わせを副問い合わせと言います。

インジェクション攻撃

ソフトウェアへの攻撃手法の1つです。
文字列入力を受け付けるプログラムに対して、セキュリティを無効化するような不正な文字列を入れてシステムを乗っ取ったり、データを詐取したりします。

不正な文字列と勘違いされないように、MySqlなどではクエリに:=や:をつけます。