Пример #1
0
        /// <summary>
        /// 指定された型情報から対象となるテーブルのレコードを取得するクエリを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルの型</typeparam>
        /// <param name="propertyNames">抽出する列にマッピングされるプロパティのコレクション。指定がない場合はすべての列を抽出対象とします。</param>
        /// <returns>生成されたSQL</returns>
        public static string CreateSelect(Type type, IEnumerable <string> propertyNames = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (propertyNames == null)
            {
                propertyNames = Enumerable.Empty <string>();
            }

            var table   = TableMappingInfo.Create(type);
            var columns = propertyNames.IsEmpty()
                        ? table.Columns
                        : table.Columns.Join
                          (
                propertyNames,
                x => x.PropertyName,
                y => y,
                (x, y) => x
                          );
            var columnNames = columns.Select(x => $"    {x.ColumnName} as {x.PropertyName}");
            var builder     = new StringBuilder();

            builder.AppendLine("select");
            builder.AppendLine(string.Join($",{Environment.NewLine}", columnNames));
            builder.Append($"from {table.FullName}");
            return(builder.ToString());
        }
Пример #2
0
        /// <summary>
        /// バルク方式で指定のデータを挿入するためのコマンドを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>コマンド</returns>
        private DbCommand CreateBulkInsertCommand <T>(IEnumerable <T> data)
        {
            //--- 実体化
            data = data.Materialize();

            //--- build DbCommand
            var     factory = DbProvider.GetFactory(this.DbKind);
            dynamic command = factory.CreateCommand();

            command.Connection     = (dynamic)this.Connection;
            command.CommandText    = PrimitiveSql.CreateInsert <T>(this.DbKind, false, true);
            command.BindByName     = true;
            command.ArrayBindCount = data.Count();
            if (this.Timeout.HasValue)
            {
                command.CommandTimeout = this.Timeout.Value;
            }

            //--- bind params
            foreach (var x in TableMappingInfo.Create <T>().Columns)
            {
                var getter = AccessorCache <T> .LookupGet(x.PropertyName);

                dynamic parameter = factory.CreateParameter();
                parameter.ParameterName = x.PropertyName;
                parameter.DbType        = x.ColumnType;
                parameter.Value         = data.Select(y => getter(y)).ToArray();
                command.Parameters.Add(parameter);
            }
            return(command);
        }
Пример #3
0
        /// <summary>
        /// 指定された型情報から、対象となるテーブルのレコードを指定されたプロパティにマッピングされている列に絞って更新するクエリを生成します。
        /// </summary>
        /// <param name="targetDatabase">対象データベース</param>
        /// <param name="type">テーブルの型</param>
        /// <param name="propertyNames">プロパティ名のコレクション。指定がない場合はすべての列を抽出対象とします。</param>
        /// <param name="setIdentity">自動採番のID列に値を設定するかどうか</param>
        /// <returns>生成されたSQL</returns>
        public static string CreateUpdate(DbKind targetDatabase, Type type, IEnumerable <string> propertyNames = null, bool setIdentity = false)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (propertyNames == null)
            {
                propertyNames = Enumerable.Empty <string>();
            }

            var prefix  = targetDatabase.GetBindParameterPrefix();
            var table   = TableMappingInfo.Create(type);
            var columns = table.Columns.Where(x => setIdentity ? true : !x.IsAutoIncrement);

            if (propertyNames.Any())
            {
                columns = columns.Join(propertyNames, x => x.PropertyName, y => y, (x, y) => x);
            }
            var setters = columns.Select(x => $"    {x.ColumnName} = {prefix}{x.PropertyName}");
            var builder = new StringBuilder();

            builder.AppendLine($"update {table.FullName}");
            builder.AppendLine("set");
            builder.Append(string.Join($",{Environment.NewLine}", setters));
            return(builder.ToString());
        }
Пример #4
0
        /// <summary>
        /// InsertAndGet メソッドを実行可能かどうかを診断します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <returns>実行可能かどうか</returns>
        protected static void AssertInsertAndGet <T>()
        {
            if (typeof(T).IsCollection())
            {
                throw new InvalidOperationException("Can insert single entity only.");
            }

            var table   = TableMappingInfo.Create <T>();
            var primary = table.Columns.Where(x => x.IsPrimaryKey).ToArray();

            if (primary.Length != 1)
            {
                throw new InvalidOperationException("Primary key column should be only one.");
            }

            var autoIncrement = table.Columns.Where(x => x.IsAutoIncrement).ToArray();
            var sequence      = table.Columns.Where(x => x.Sequence != null).ToArray();
            var idColumnCount = autoIncrement.Length + sequence.Length;

            if (idColumnCount != 1)
            {
                throw new InvalidOperationException("Id column (auto increment or sequence) should be only one.");
            }

            if (!primary[0].IsAutoIncrement && primary[0].Sequence == null)
            {
                throw new InvalidOperationException("Id column should be primary key.");
            }
        }
Пример #5
0
        /// <summary>
        /// 指定したデータからバルクインサート用のSQL文を生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>SQL文</returns>
        private string CreateBulkInsertSql <T>(IEnumerable <T> data)
        {
            var prefix      = this.DbKind.GetBindParameterPrefix();
            var table       = TableMappingInfo.Create <T>();
            var columnNames = table.Columns.Select(x => "    " + x.ColumnName);
            var builder     = new StringBuilder();

            builder.AppendLine($"insert into {table.FullName}");
            builder.AppendLine("(");
            builder.AppendLine(string.Join($",{Environment.NewLine}", columnNames));
            builder.AppendLine(")");
            builder.Append("values");

            var getters = table.Columns.Select(c => AccessorCache <T> .LookupGet(c.PropertyName)).ToArray();

            foreach (var x in data)
            {
                builder.AppendLine();
                builder.Append("(");
                var values = getters.Select(f => ToSqlLiteral(f(x)));
                builder.Append(string.Join(", ", values));
                builder.Append("),");
            }
            builder.Length--;  //--- 最後の「,」を削除

            return(builder.ToString());
        }
        /// <summary>
        /// レコードを挿入し、そのレコードに自動採番されたIDを取得するSQLを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <returns>SQL文</returns>
        protected override string CreateInsertAndGetSql <T>()
        {
            var sequence = TableMappingInfo.Create <T>().Columns.First(x => x.IsPrimaryKey).Sequence;

            return
                ($@"{PrimitiveSql.CreateInsert<T>(this.DbKind)};
select currval({sequence.FullName}) as Id;");
        }
Пример #7
0
        /// <summary>
        /// 指定された型情報から対象となるテーブルのレコード数をカウントするクエリを生成します。
        /// </summary>
        /// <param name="type">テーブルの型</param>
        /// <returns>生成されたSQL</returns>
        public static string CreateCount(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var table = TableMappingInfo.Create(type);

            return($"select count(*) as Count from {table.FullName}");
        }
Пример #8
0
        /// <summary>
        /// 指定された型情報から対象となるテーブルのすべてのレコードを切り捨てるクエリを生成します。
        /// </summary>
        /// <param name="type">テーブルの型</param>
        /// <returns>生成されたSQL</returns>
        public static string CreateTruncate(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var table = TableMappingInfo.Create(type);

            return($"truncate table {table.FullName}");
        }
Пример #9
0
        /// <summary>
        /// 指定された型情報から対象となるテーブルにレコードを挿入するクエリを生成します。
        /// </summary>
        /// <param name="targetDatabase">対象データベース</param>
        /// <param name="type">テーブルの型</param>
        /// <param name="useSequence">シーケンスを利用するかどうか</param>
        /// <param name="setIdentity">自動採番のID列に値を設定するかどうか</param>
        /// <returns>生成されたSQL</returns>
        public static string CreateInsert(DbKind targetDatabase, Type type, bool useSequence = true, bool setIdentity = false)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var prefix  = targetDatabase.GetBindParameterPrefix();
            var table   = TableMappingInfo.Create(type);
            var columns = table.Columns.Where(x => setIdentity ? true : !x.IsAutoIncrement);
            var values  = columns.Select(x =>
            {
                if (useSequence)
                {
                    if (x.Sequence != null)
                    {
                        switch (targetDatabase)
                        {
                        case DbKind.SqlServer:  return($"next value for {x.Sequence.FullName}");

                        case DbKind.Oracle:     return($"{x.Sequence.FullName}.nextval");

                        case DbKind.PostgreSql: return($"nextval('{x.Sequence.FullName}')");
                        }
                    }
                }
                return($"{prefix}{x.PropertyName}");
            })
                          .Select(x => "    " + x);
            var columnNames = columns.Select(x => "    " + x.ColumnName);
            var builder     = new StringBuilder();

            builder.AppendLine($"insert into {table.FullName}");
            builder.AppendLine("(");
            builder.AppendLine(string.Join($",{Environment.NewLine}", columnNames));
            builder.AppendLine(")");
            builder.AppendLine("values");
            builder.AppendLine("(");
            builder.AppendLine(string.Join($",{Environment.NewLine}", values));
            builder.Append(")");
            return(builder.ToString());
        }
Пример #10
0
        /// <summary>
        /// バルク方式での挿入処理の準備を行います。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="executor">バルク処理実行機能</param>
        /// <param name="data">挿入する生データ</param>
        /// <returns>挿入するデータ</returns>
        private DataTable SetupBulkInsert <T>(SqlBulkCopy executor, IEnumerable <T> data)
        {
            //--- タイムアウト
            if (this.Timeout.HasValue)
            {
                executor.BulkCopyTimeout = this.Timeout.Value;
            }

            //--- 対象テーブル名
            var info = TableMappingInfo.Create <T>();

            executor.DestinationTableName = info.FullName;

            //--- 列のマップ
            var table   = new DataTable();
            var getters = new List <Func <T, object> >();

            foreach (var x in info.Columns)
            {
                executor.ColumnMappings.Add(x.PropertyName, x.ColumnName);
                table.Columns.Add(new DataColumn {
                    ColumnName  = x.PropertyName,
                    DataType    = x.IsNullable ? Nullable.GetUnderlyingType(x.PropertyType) : x.PropertyType,
                    AllowDBNull = x.IsNullable
                });
                getters.Add(AccessorCache <T> .LookupGet(x.PropertyName));
            }

            //--- データ生成
            foreach (var x in data)
            {
                var row = table.NewRow();
                for (int i = 0; i < getters.Count; i++)
                {
                    row[i] = getters[i](x);
                }
                table.Rows.Add(row);
            }
            return(table);
        }
Пример #11
0
        /// <summary>
        /// InsertAndGetするためのパラメーターを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>パラメーター</returns>
        private Tuple <DbCommand, DbParameter> CreateInsertAndGetParameter <T>(T data)
        {
            //--- command
            var     factory = DbProvider.GetFactory(this.DbKind);
            dynamic command = factory.CreateCommand();

            command.BindByName = true;
            command.Connection = (dynamic)this.Connection;
            if (this.Timeout.HasValue)
            {
                command.CommandTimeout = this.Timeout.Value;
            }

            //--- parameters
            DbParameter output = null;

            foreach (var x in TableMappingInfo.Create <T>().Columns)
            {
                dynamic parameter = factory.CreateParameter();
                parameter.ParameterName = x.PropertyName;
                parameter.DbType        = x.ColumnType;
                if (x.IsPrimaryKey)
                {
                    parameter.Direction = ParameterDirection.Output;
                    output = parameter;
                    command.CommandText =
                        $@"{PrimitiveSql.CreateInsert<T>(this.DbKind)}
returning {x.ColumnName} into :{x.PropertyName}";
                }
                else
                {
                    parameter.Direction = ParameterDirection.Input;
                    parameter.Value     = AccessorCache <T> .LookupGet(x.PropertyName)(data);
                }
                command.Parameters.Add(parameter);
            }

            //--- ok
            return(Tuple.Create((DbCommand)command, output));
        }
Пример #12
0
        /// <summary>
        /// 条件を表す式からSQLを生成します
        /// </summary>
        /// <typeparam name="T">テーブルの型</typeparam>
        /// <param name="targetDatabase">対象となるデータベース</param>
        /// <param name="predicate">条件式</param>
        /// <returns>条件SQL</returns>
        public static This From <T>(DbKind targetDatabase, Expression <Func <T, bool> > predicate)
        {
            //--- 解析実行
            var root = PredicateParser.Parse(predicate);

            //--- SQL文 / パラメーター生成の定義
            uint index          = 0;
            var  columnMap      = TableMappingInfo.Create <T>().Columns.ToDictionary(x => x.PropertyName);
            var  prefix         = targetDatabase.GetBindParameterPrefix();
            var  parameterCount = root.DescendantsAndSelf().Count(x =>
            {
                return(x.Operator != PredicateOperator.AndAlso &&
                       x.Operator != PredicateOperator.OrElse &&
                       x.Value != null);
            });
            var digit       = (parameterCount - 1).ToString().Length;
            var digitFormat = $"D{digit}";
            IDictionary <string, object>    parameter  = new ExpandoObject();
            Func <PredicateElement, string> sqlBuilder = null;

            sqlBuilder = element =>
            {
                if (element.HasChildren)
                {
                    var left  = sqlBuilder(element.Left);
                    var right = sqlBuilder(element.Right);
                    if (element.Operator != element.Left.Operator && element.Left.HasChildren)
                    {
                        left = $"({left})";
                    }
                    if (element.Operator != element.Right.Operator && element.Right.HasChildren)
                    {
                        right = $"({right})";
                    }
                    if (element.Operator == PredicateOperator.AndAlso)
                    {
                        return($"{left} and {right}");
                    }
                    if (element.Operator == PredicateOperator.OrElse)
                    {
                        return($"{left} or {right}");
                    }
                    throw new InvalidOperationException();
                }
                else
                {
                    var builder = new StringBuilder();
                    builder.Append(columnMap[element.PropertyName].ColumnName);
                    switch (element.Operator)
                    {
                    case PredicateOperator.Equal:
                        if (element.Value == null)
                        {
                            builder.Append(" is null");
                            return(builder.ToString());
                        }
                        builder.Append(" = ");
                        break;

                    case PredicateOperator.NotEqual:
                        if (element.Value == null)
                        {
                            builder.Append(" is not null");
                            return(builder.ToString());
                        }
                        builder.Append(" <> ");
                        break;

                    case PredicateOperator.LessThan:            builder.Append(" < ");  break;

                    case PredicateOperator.LessThanOrEqual:     builder.Append(" <= "); break;

                    case PredicateOperator.GreaterThan:         builder.Append(" > ");  break;

                    case PredicateOperator.GreaterThanOrEqual:  builder.Append(" >= "); break;

                    case PredicateOperator.Contains:            builder.Append(" in "); break;

                    default:                                    throw new InvalidOperationException();
                    }

                    var parameterName = $"p{index.ToString(digitFormat)}";
                    ++index;
                    parameter.Add(parameterName, element.Value);  //--- cache parameter
                    builder.Append($"{prefix}{parameterName}");
                    return(builder.ToString());
                }
            };

            //--- 組み立て
            return(new This(sqlBuilder(root), parameter as ExpandoObject));
        }