選択した条件を満たすデータを取得するためのベストプラクティス
質問
call_time、location、emergency_typeの列を持つcallというデータベーステーブルがあり、救急隊、警察、消防士の3つのタイプの緊急事態があります。 WindowsフォームでCheckBoxes 'paramedics'、 'police'、 'firefighters'を作成し、ユーザーの選択に一致するすべてのテーブル列を取得します。
関数を作成しました:
public static DataTable GetHistory(DateTime from, DateTime to, bool paramedics, bool police, bool firefighters)
{
string select =
"SELECT call_time, location, emergency_type where call_time between @from AND @to AND";
if(paramedics)
{
select += " emergency_type = 'paramedics' ";
}
if(paramedics && police)
{
select +=" emergency_type = 'paramedics' OR emergency_type = 'police';
}
...
}
ただし、このコードは非常に汚れているようです。なぜなら、30種類の緊急事態が発生した場合、30の緊急事態があるからです!すべてのifステートメントを書く前に、私は年を取ります。
選択できるオプションが多数ある場合、選択した検索条件を満たすデータを取得するためのプラクティスを共有していただければ幸いです。
ありがとう!
解決
emergency_typeを文字列として使用する必要がある場合、boolを渡す代わりに、緊急タイプのテキスト表現を含むListを送信できます。たとえば、上記のコードを調整するには、メソッドシグネチャを次のように変更します
public static DataTable GetHistory(DateTime from, DateTime to, List<string> types)
{
..
}
次に、これらのようなリストを渡します(たとえば)
List<string> types =
new List<string> { "paramedics" };
or
List<string> types =
new List<string> { "paramedics", "police" };
次に、where句でSQL INステートメントを使用するようにクエリを調整できます。次に、文字列のリストをコンマ区切りの文字列に変換します
string values = "'paramedics', 'police'"
values変数を作成する簡単な方法は、使用することです
string values = string.Empty;
types.ForEach(s =>
{
if (!string.IsNullOrEmpty(values))
values += ",";
values += string.Format("'{0}'", s);
});
ところで、パラメーター化されたコマンドを使用して、SQLインジェクションを回避できます。文字列を取得したら、簡単に実行できます
string select =
"SELECT call_time, location, emergency_type where call_time between @from AND @to AND emergency_type IN " + values
他のヒント
これはこれを行うための汚い方法です。
string select = "SELECT call_time, location, emergency_type where call_time between @from AND @to AND (1=0";
if(paramedics) { select += " OR emergency_type = 'paramedics' "; }
if(police) { select += " OR emergency_type = 'police'"; }
if(xyz) { select += " OR emergency_type = 'xyz'"; }
select += ")";
文字列の連結は、いくつかの厄介な脆弱性の一因となる可能性があるため、避ける必要があります。 プログラムによるアクセスの観点からベストプラクティスを探している場合、ここでのベストプラクティスはパラメーター化されたクエリを使用することです。
安価にしたい場合は、in句にパラメータを指定し、チェックボックスのリストからその文字列を連結して、in句のパラメータの値として渡します。次のようになります。
where ... and emergency_type in (?)
もう1つの方法は、チェックされているチェックボックスの数をカウントし、in句のパラメーターのリストを作成して、次のように表示することです。
where ... and emergency_type in(?,?...) -- as many params as there are checked checkboxes.
これらのどちらでも問題ありません。 これらの種類のクエリを使用して、独自のSQLコンストラクターメソッドを構築するまで行って、パラメーターとそのデータ型の内部カウントを保持し、SQLを動的に構築し、適切なパラメーターの既知のリストを使用して準備を行います。
Linqの学習をご覧ください。
ユーザーの比較値のリスト(@EmergencyList)を作成し、Contains演算子を使用してパラメーター化されたクエリでSQLを使用します。
SELECT call_time,
location,
emergency_type
where call_time between @from AND @to
AND CONTAINS( Emegency_Type, @EmergencyList )