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

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

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

try...catch の文字列の設定方法について

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

■96698 / inTopicNo.1)  try...catch の文字列の設定方法について
  
□投稿者/ 天海 (6回)-(2021/01/11(Mon) 11:30:47)

分類:[C#] 

BEGIN TRY
BEGIN TRANSACTION

INSERT INTO DB (ID)
VALUES (@ID, @ID)

COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
THROW 50000, 'ERROR', 1
END CATCH


C# の try catch で
try {
SQL呼び出し
}
catch(Exception ex)
{
  ex.Message を "ERROR" にしたいのですが
  "VALUES 句で指定された値よりも INSERT ステートメントの列数が少なすぎます...."
になってしまいます。
}

できないのでしょうか?
引用返信 編集キー/
■96699 / inTopicNo.2)  Re[1]: try...catch の文字列の設定方法について
□投稿者/ WebSurfer (2177回)-(2021/01/11(Mon) 13:33:00)
No96698 (天海 さん) に返信

> C# の try catch で
> try {
> SQL呼び出し
> }
> catch(Exception ex)
> {
>   ex.Message を "ERROR" にしたいのですが
>   "VALUES 句で指定された値よりも INSERT ステートメントの列数が少なすぎます...."
> になってしまいます。
> }
>
> できないのでしょうか?

その「SQL呼び出し」の具体的なコードはどうなっているのですか? 

BEGIN TRY ... END CATCH ではないですよね。それではコンパイルも通らないはずですし。
引用返信 編集キー/
■96700 / inTopicNo.3)  Re[1]: try...catch の文字列の設定方法について
□投稿者/ Azulean (1176回)-(2021/01/11(Mon) 13:34:16)
No96698 (天海 さん) に返信
> catch(Exception ex)
> {
>   ex.Message を "ERROR" にしたいのですが
>   "VALUES 句で指定された値よりも INSERT ステートメントの列数が少なすぎます...."
> になってしまいます。
> }
>
> できないのでしょうか?

メッセージを置き換えたいなら、自分で Exception を作り直せば良いのでは?
(それによって失うもの(解析性)もあるので過度な利用はやめた方が良いと思いますが)


catch (Exception ex) { throw new Exception("あああ"); }
引用返信 編集キー/
■96701 / inTopicNo.4)  Re[2]: try...catch の文字列の設定方法について
□投稿者/ とっちゃん (711回)-(2021/01/11(Mon) 14:11:53)
No96700 (Azulean さん) に返信
> メッセージを置き換えたいなら、自分で Exception を作り直せば良いのでは?
> (それによって失うもの(解析性)もあるので過度な利用はやめた方が良いと思いますが)
>
>
> catch (Exception ex) { throw new Exception("あああ"); }

補足。もし、例外を作り直して投げる場合は

throw new Exception( "新しいメッセージ", ex );

と、以前の例外を追加しておくことをお勧めします。

例外が途切れてしまうことに変わりはありませんが、分断されるとはいえ
そこに至る例外の経路は残せるので、ほぼほぼ原因までたどり着けます。


引用返信 編集キー/
■96702 / inTopicNo.5)  Re[1]: try...catch の文字列の設定方法について
□投稿者/ WebSurfer (2178回)-(2021/01/11(Mon) 14:50:35)
No96698 (天海 さん) に返信

catch(Exception ex) が問題ないと思っているかもしれないので一言。

それだと再 throw しない限りすべての例外を握りつぶしてなかったことにしてしまいますが、それは
いろいろまずいので止めるべきです。

.NET アプリの例外処置について、自分的に一般的と思うことを書きます。

(1) 予測可能で正しい業務フローに戻すことができる「業務エラー」(例:ユーザーの入力間違い)と、
  予測できないもしくは予測はできても何の対応もできない「例外」(例:DB サーバーダウン)を
  区別して対処。

(2) 「例外」はランタイムに拾わせてアプリケーションを停止させる。無かったことにして、ユーザが
  作業を続けられるようにすると、大事なデータを壊したり、強制的に停止させるより好ましからざ
  る状況に陥るかも。

(3) よほどのことがない限り try-catch は書かない。

(4) キャッチせざるを得ない場合でも Execption はキャッチしない。キャッチせざるを得ないとしても
  範囲を絞る。例えば DB 関係の例外が予測される SqlException に限定して catch するとか。

(5) 間違って補足してしまった例外は throw する。(注:catch ブロックでキャッチした例外を throw 
  するとスタックトレースが途切れるので単に throw と書く)

(6) ユーザーへの通知が必要なら、集約的例外処置を利用する。

それから、.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも 
Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

破損状態例外を処理する
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/clr-inside-out-handling-corrupted-state-exceptions

自分が持っている Microsoft の本に、上に書いた原則に則ったサンプルが載っていたので紹介しておきます。

データベースに INSERT する際に PK 制約違反で発生する例外のみ catch して業務エラー(2 重登録)
としてユーザーに再入力を促し、その他は再 throw してランタイムに拾わせてアプリケーションを停止
させるというものです。


public bool InsertAuthors()
{
    var connection = new SqlConnection("接続文字列");
    var command = new SqlCommand("INSERT INTO authors VALUES ('172-32-1176', 'White', ...)", connection);
    try
    {
        connection.Open();
        try
        {
            command.ExecuteNonQuery();
        }
        catch(SqlException sqle)
        {
            if (sqle.Number == 2627)
            {
                return false;
            }
            else
            {
                throw;
            }
        }
    }
    finally
    {
        connection.Close();
    }

    return true;
}

以下の記事も参考になると思います。

例外の推奨事項
https://docs.microsoft.com/ja-jp/dotnet/standard/exceptions/best-practices-for-exceptions

引用返信 編集キー/
■96703 / inTopicNo.6)  Re[2]: try...catch の文字列の設定方法について
□投稿者/ 天海 (7回)-(2021/01/11(Mon) 14:52:34)


SQLの呼び出しが以下のようになっています。

private static DataTable GetDatatable(string connectionstring, string CommandText)
{
 var table = new DataTable();
 dtError = null;

 connectionstring += "; connect timeout=60";
 using (var connection = new SqlConnection(connectionstring))
 {
  using(var command = connection.CreateCommand())
  {
   try
   {
    command.CommandText = CommandText;
    var adapter = new SqlDataAdapter(command);
    adapter.Fill(table);
   }
   catch(Exception e)
   {
    GetErrorDatatable(connectionstring, CommandText);
    throw new Exception(e.Message);
   }
  }
 }
 return table;
}

CommandText に以下のSQLを入れて呼び出したときに

DECLARE @ID INT = 1;

BEGIN TRY
 BEGIN TRANSACTION

 INSERT INTO DB (ID)
 VALUES (@ID, @ID)     ←本来は VALUES (@ID) なのですがあえてエラーになるようにします。

 COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
THROW 50000, 'ERROR', 1
END CATCH


呼び出し側 C# プログラム

try {
 GetDatatable(connectionstring, CommandText);
}
catch(Exception ex)
{
 MessageBox.Show(ex.Message);
}

この時に表示されるメッセージを "ERROR" にしたいのです。

しかし、
本来のやりたいことは
ネットワークの切断等による接続エラーによるものか
処理のエラーなのかSQLの記述によるエラーなのかを判断したいのです。

もっといい方法があれば教えてください。
GetDatatable() は変更できないので
SQL の記述の方法で何とかなるのかと思って質問させもらいました。



引用返信 編集キー/
■96704 / inTopicNo.7)  Re[3]: try...catch の文字列の設定方法について
□投稿者/ WebSurfer (2179回)-(2021/01/11(Mon) 18:12:02)
No96703 (天海 さん) に返信

> もっといい方法があれば教えてください。
> GetDatatable() は変更できないので
> SQL の記述の方法で何とかなるのかと思って質問させもらいました。

以下を試してみてください。

using System.IO.Compression;
using System.Xml.Linq;
using System.Data.SqlClient;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            string connString = @"Data Source=lpc:(local)\sqlexpress;Initial Catalog=NORTHWIND;Integrated Security=True";
            string query = "THROW 51000, 'The record does not exist.', 1;";

            using (var connection = new SqlConnection(connString))
            {
                using (var command = new SqlCommand(query, connection))
                {
                    connection.Open();
                    try
                    {
                        command.ExecuteNonQuery();
                    }
                    catch (SqlException ex)
                    {
                        if(ex.Number == 51000)
                        {
                            Console.WriteLine("51000");
                        }
                    }
                }
            }
        }
    }
}

引用返信 編集キー/
■96705 / inTopicNo.8)  Re[4]: try...catch の文字列の設定方法について
□投稿者/ 天海 (8回)-(2021/01/12(Tue) 08:29:19)
No96704 (WebSurfer さん) に返信

51000が表示されました。
引用返信 編集キー/
■96706 / inTopicNo.9)  Re[5]: try...catch の文字列の設定方法について
□投稿者/ WebSurfer (2180回)-(2021/01/12(Tue) 09:25:33)
No96705 (天海 さん) に返信
> ■No96704 (WebSurfer さん) に返信
>
> 51000が表示されました。

その延長であなたのやりたいこと、

> この時に表示されるメッセージを "ERROR" にしたいのです。

はできますが、分かりませんでしたか? SqlException のプロパティにどういうもの
があるか調べましたか?

さらに、

> 本来のやりたいことは
> ネットワークの切断等による接続エラーによるものか
> 処理のエラーなのかSQLの記述によるエラーなのかを判断したいのです。

も SqlExceprion のプロパティから得られる情報である程度分かるはずです。

レスを読んで理解してもらってますか? どうもちゃんと理解してないのではという
気がします。
引用返信 編集キー/
■96707 / inTopicNo.10)  Re[3]: try...catch の文字列の設定方法について
□投稿者/ 魔界の仮面弁士 (2952回)-(2021/01/12(Tue) 10:47:08)
No96703 (天海 さん) に返信
> もっといい方法があれば教えてください。
> GetDatatable() は変更できないので
> SQL の記述の方法で何とかなるのかと思って質問させもらいました。

今回の質問は、GetDatatable が例外を握りつぶす実装になっているという問題点
(スタックトレースを破壊しているし、元の例外クラスの情報も削ぎ落してしまっている)は
理解したうえで、その GetDatatable の実装を変えることなく、
Transact-SQL 側の例外処理だけで、SQL 側からの例外メッセージを
C# 側に伝えたいという質問だと理解しました。


で、構文エラーに関しては BEGIN CATCH ブロックでは拾えないため、
sp_executesql で動的問い合わせに加工する必要があるでしょう。
https://www.dbsheetclient.jp/blog/?p=658


ただ、効率度外視、スペルミス等に実行時まで気づけない、そして
インジェクションの危険性が高まるなどといったデメリットの方が多いので、
GetDatatable そのものに手を入れるのが健全だと思いますよ…。
引用返信 編集キー/
■96708 / inTopicNo.11)  Re[4]: try...catch の文字列の設定方法について
□投稿者/ 天海 (9回)-(2021/01/12(Tue) 11:20:20)
No96707 (魔界の仮面弁士 さん) に返信

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

おっしゃる通りです。
SQL構文の中のtry...catch...
で構文のエラーが引っ掛からないのを知らずに
エラーを出すために構文のエラーを使ってしまったので
迷ってしまいました。



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

このトピックをツリーで一括表示


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

このトピックに書きこむ