C# SQLパラメーター整理ツール

  • 117
  • 0
  • SQL
  • 2018-09-02

開発時、パラメーター設定を楽にしたい。。。

簡単な検証したが、

実用性・複雑なものに頼られるかどうか要検証

SqlClause

    /* 開発時、パラメーター格納処理
       *
       * ※ 検索値の型が自動判定され、CommandのDbTypeに設定する。(日付型使用は要注意)
       * ※ GetSql取得する前、AddReplaceConditionに設定されたものが先に実行される
       *
       *
       * ADD(name,val)メソッド:SQLに設定されるパラメーター名・検索値
       *     name:パラメーター名。
       *         使用説明、先頭に”:”が必要です(:name)
       *         同じ名で追加された場合、取得時に追加されたパラメーター名+C連番でパラメーター名を置き換える
       *         (:name→:nameC001、:nameC002...)、使用タイミングはIN句
       *         
       *      val:検索値。
       *         IN句、追加された値かずで上記で作成したパラメーター名に応じて設定する
       *         LIKE句、あいまい検索する場合、”%”を検索値に追加すること
       *         
       *
       * AddReplaceCondition(oldStr, newStr)メソッド:SQLに設定された文字を置き換える
       *
       * GetSql(sql)メソッド:ベースSQLを実行時SQLに変更し、取得する
       *
       * GetParameters()メソッド:整理したパラメーター名・検索値リスト取得
       */

    /// <summary>
    /// SQLパラメーター格納(storage)
    /// </summary>
    public class SqlClause
    {
        /// <summary>
        /// 
        /// </summary>
        private StringBuilder BaseSql { get; set; } = new StringBuilder();

        /// <summary>
        /// パラメーターリスト(ユーザー用)
        /// </summary>
        private IDictionary<string, object> storagelist = new Dictionary<string, object>();

        /// <summary>
        /// LINK JOIN文字
        /// </summary>
        private string joinmark = @",";

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SqlParameterClause(string sql)
        {
            BaseSql.Append(sql);
        }


        /// <summary>
        /// リストタイプ判定
        /// </summary>
        private bool IsList(object obj)
        {
            if (obj == null)
                return false;
            if (obj.GetType().Name.IndexOf("List`") < 0) // if(Value.GetType() == typeof(List<>)) がうまく判定できず
                return false;
            return true;
        }

        /// <summary>
        /// パラメーター追加
        /// </summary>
        /// <param name="name">パラメーター名</param>
        /// <param name="val">値</param>
        public void Add(string name, object val)
        {
            if (storagelist.ContainsKey(name))
            {
                var objitem = storagelist[name];
                if (IsList(objitem))
                    ((IList<object>)objitem).Add(val);
                else
                {
                    var temp = new List<object>
                    {
                        objitem,
                        val
                    };
                    storagelist[name] = temp;
                }
            }
            else
                storagelist.Add(name, val);
        }

        /// <summary>
        /// パラメーター除去
        /// </summary>
        /// <param name="name">パラメーター名</param>
        public void Remove(string name)
        {
            if (storagelist.ContainsKey(name))
                storagelist.Remove(name);
        }

        /// <summary>
        /// Has
        /// </summary>
        public bool Has()
        {
            if (storagelist.Count() == 0)
                return false;
            return true;
        }

        /// <summary>
        /// パラメーター名置換リスト(自動作成用)
        /// </summary>
        private readonly IDictionary<string, string> replacenames = new Dictionary<string, string>();

        /// <summary>
        /// パラメーターリスト(自動作成用)
        /// </summary>
        private readonly IDictionary<string, object> parameters = new Dictionary<string, object>();

        /// <summary>
        /// カスタム置換設定
        /// </summary>
        public void AddReplaceCondition(string oldStr, string newStr)
        {
            replacenames.Add(oldStr, newStr);
        }

        /// <summary>
        /// ユーザー追加したパラメーターを処理する
        /// </summary>
        private void Arrange()
        {
            if (parameters.Count() > 0)
                return;
            // 同じキーで複数な値がある場合、キーの連番で作成
            foreach (var item in storagelist)
            {
                if (IsList(item.Value))
                {
                    var i = 0;
                    var pn = new StringBuilder();
                    foreach (var e in (IList<object>)item.Value)
                    {
                        var key = string.Format(item.Key + "C{0:000}", i);
                        if (i > 0)
                            pn.Append(joinmark);
                        pn.Append(key);
                        parameters.Add(key, e);
                        i++;
                    }
                    replacenames.Add(item.Key, pn.ToString());
                }
                else
                    parameters.Add(item.Key, item.Value);
            }

        }

        /// <summary>
        /// SQL追加
        /// </summary>
        public void BaseSqlAppendLine(string sql)
        {
            BaseSql.AppendLine(sql);
        }


        /// <summary>
        /// 実行用SQL
        /// </summary>
        public string GetSql()
        {
            Arrange();
            var result = BaseSql.ToString();
            foreach (var e in replacenames)
            {
                result = result.Replace(e.Key, e.Value);
            }
            return result;
        }

        /// <summary>
        /// 実行用パラメーター
        /// </summary>
        public IDictionary<string, object> GetParameters()
        {
            Arrange();
            return parameters;
        }

        /// <summary>
        /// クリア
        /// </summary>
        public void Clear()
        {
            storagelist.Clear();
            replacenames.Clear();
            parameters.Clear();
            BaseSql.Clear();

        }

    }

簡単なテスト例

設定:

public void Execute()
{
    var sql = @"
SELECT COUNT(*) 
FROM TESTTABLE
WHERE 
    ITEM IN (:ITEM)
/*CODE*/
AND (NAME IS NULL
OR NAME IN (:NAME))
";

    var pars = new SqlClause(sql);
    pars.Add(":ITEM", "001");
    pars.Add(":ITEM", "002");
    pars.Add(":ITEM", "003");
    pars.Add(":ITEM", "004");
    pars.Add(":ITEM", "005");
    pars.Add(":NAME", "N0003");
    pars.Add(":NAME", "N0005");
    pars.Add(":CODE", "C0003");
    pars.Add(":CODE", "C0001");
    pars.AddReplaceCondition("/*CODE*/", "AND CODE IN (:CODE)");

    var result = GetResult(pars);
    Console.WriteLine(result.ToString());

}

パラメーター設定

private object GetResult(SqlClause @params)
{
    OracleConnection con = new OracleConnection("connectionString");
    con.Open();
    object result = null;
    OracleCommand cmd = new OracleCommand("dummy-sql",con)
    {
        CommandText = @params.GetSql(),
        BindByName = true 
    };

    foreach (var e in @params.GetParameters())
    {
        var prm = cmd.CreateParameter();
        prm.ParameterName = e.Key;
        prm.DbType = GetDbType(e.Value);
        prm.Value = e.Value ?? DBNull.Value;
        cmd.Parameters.Add(prm);
    }
    result =  cmd.ExecuteScalar();
    return result;
}

GetDbType

public static DbType GetDbType(object value)
{
    DbType result;

    if ((value == null))
        result = DbType.String;
    else if ((value.GetType() == typeof(string)))
        result = DbType.String;
    else if ((value.GetType() == typeof(Int32)))
        result = DbType.Int32;
    else if ((value.GetType() == typeof(Int32?)))
        result = DbType.Int32;
    else if ((value.GetType() == typeof(Int64)))
        result = DbType.Int64;
    else if ((value.GetType() == typeof(Int64?)))
        result = DbType.Int64;
    else if ((value.GetType() == typeof(DateTime)))
        result = DbType.DateTime;
    else if ((value.GetType() == typeof(DateTime?)))
        result = DbType.DateTime;
    else if ((value.GetType() == typeof(decimal)))
        result = DbType.Decimal;
    else if ((value.GetType() == typeof(decimal?)))
        result = DbType.Decimal;
    else
    {
        var type = value.GetType();
        throw new Exception(string.Format("対応されていない型 {0} があります。", type));
    }

    return result;
}