Exemple #1
0
        public async static Task <int> UpdateFromQueryAsync <T>(this IQueryable <T> querable, Expression <Func <T, T> > updateExpression, int?commandTimeout = null,
                                                                CancellationToken cancellationToken = default) where T : class
        {
            int rowAffected = 0;
            var dbContext   = querable.GetDbContext();

            using (var dbTransactionContext = new DbTransactionContext(dbContext))
            {
                var dbConnection  = dbTransactionContext.Connection;
                var dbTransaction = dbTransactionContext.CurrentTransaction;
                try
                {
                    var    sqlQuery         = SqlBuilder.Parse(querable.ToQueryString());
                    string setSqlExpression = updateExpression.ToSqlUpdateSetExpression(sqlQuery.GetTableAlias());
                    sqlQuery.ChangeToUpdate(sqlQuery.GetTableAlias(), setSqlExpression);
                    rowAffected = await dbContext.Database.ExecuteSqlRawAsync(sqlQuery.Sql, sqlQuery.Parameters.ToArray(), cancellationToken);

                    dbTransactionContext.Commit();
                }
                catch (Exception ex)
                {
                    dbTransactionContext.Rollback();
                    throw ex;
                }
            }
            return(rowAffected);
        }
        public static int InsertFromQuery <T>(this IQueryable <T> querable, string tableName, Expression <Func <T, object> > insertObjectExpression, int?commandTimeout = null) where T : class
        {
            int rowAffected = 0;

            using (var dbTransactionContext = new DbTransactionContext(querable.GetDbContext()))
            {
                var dbContext = dbTransactionContext.DbContext;
                try
                {
                    var sqlQuery = SqlBuilder.Parse(querable.ToQueryString());
                    if (dbContext.Database.TableExists(tableName))
                    {
                        sqlQuery.ChangeToInsert(tableName, insertObjectExpression);
                        dbContext.Database.ToggleIdentityInsert(true, tableName);
                        rowAffected = dbContext.Database.ExecuteSql(sqlQuery.Sql, sqlQuery.Parameters.ToArray(), commandTimeout);
                        dbContext.Database.ToggleIdentityInsert(false, tableName);
                    }
                    else
                    {
                        sqlQuery.Clauses.First().InputText += string.Format(" INTO {0}", tableName);
                        rowAffected = dbContext.Database.ExecuteSql(sqlQuery.Sql, sqlQuery.Parameters.ToArray(), commandTimeout);
                    }

                    dbTransactionContext.Commit();
                }
                catch (Exception ex)
                {
                    dbTransactionContext.Rollback();
                    throw ex;
                }
            }
            return(rowAffected);
        }
Exemple #3
0
        public async static Task <int> BulkUpdateAsync <T>(this DbContext context, IEnumerable <T> entities, BulkUpdateOptions <T> options, CancellationToken cancellationToken = default)
        {
            int rowsUpdated  = 0;
            var outputRows   = new List <BulkMergeOutputRow <T> >();
            var tableMapping = context.GetTableMapping(typeof(T));

            using (var dbTransactionContext = new DbTransactionContext(context))
            {
                var dbContext    = dbTransactionContext.DbContext;
                var dbConnection = dbTransactionContext.Connection;
                var transaction  = dbTransactionContext.CurrentTransaction;
                try
                {
                    string   stagingTableName          = CommonUtil.GetStagingTableName(tableMapping, options.UsePermanentTable, dbConnection);
                    string   destinationTableName      = string.Format("[{0}].[{1}]", tableMapping.Schema, tableMapping.TableName);
                    string[] columnNames               = tableMapping.GetNonValueGeneratedColumns().ToArray();
                    string[] storeGeneratedColumnNames = tableMapping.GetPrimaryKeyColumns().ToArray();

                    if (storeGeneratedColumnNames.Length == 0 && options.UpdateOnCondition == null)
                    {
                        throw new InvalidDataException("BulkUpdate requires that the entity have a primary key or the Options.UpdateOnCondition must be set.");
                    }

                    await context.Database.CloneTableAsync(destinationTableName, stagingTableName, null, null, cancellationToken);
                    await BulkInsertAsync(entities, options, tableMapping, dbConnection, transaction, stagingTableName, null, SqlBulkCopyOptions.KeepIdentity, false, cancellationToken);

                    IEnumerable <string> columnstoUpdate = CommonUtil.FormatColumns(columnNames.Where(o => !options.IgnoreColumns.GetObjectProperties().Contains(o)));

                    string updateSetExpression = string.Join(",", columnstoUpdate.Select(o => string.Format("t.{0}=s.{0}", o)));
                    string updateSql           = string.Format("UPDATE t SET {0} FROM {1} AS s JOIN {2} AS t ON {3}; SELECT @@RowCount;",
                                                               updateSetExpression, stagingTableName, destinationTableName, CommonUtil <T> .GetJoinConditionSql(options.UpdateOnCondition, storeGeneratedColumnNames, "s", "t"));

                    rowsUpdated = await context.Database.ExecuteSqlRawAsync(updateSql, cancellationToken);

                    dbContext.Database.DropTable(stagingTableName);

                    //ClearEntityStateToUnchanged(context, entities);
                    dbTransactionContext.Commit();
                }
                catch (Exception)
                {
                    dbTransactionContext.Rollback();
                    throw;
                }

                return(rowsUpdated);
            }
        }
Exemple #4
0
        public async static Task <int> BulkDeleteAsync <T>(this DbContext context, IEnumerable <T> entities, BulkDeleteOptions <T> options, CancellationToken cancellationToken = default)
        {
            int rowsAffected = 0;
            var tableMapping = context.GetTableMapping(typeof(T));

            using (var dbTransactionContext = new DbTransactionContext(context))
            {
                var dbConnection = dbTransactionContext.Connection;
                var transaction  = dbTransactionContext.CurrentTransaction;
                try
                {
                    string   stagingTableName     = CommonUtil.GetStagingTableName(tableMapping, options.UsePermanentTable, dbConnection);
                    string   destinationTableName = string.Format("[{0}].[{1}]", tableMapping.Schema, tableMapping.TableName);
                    string[] keyColumnNames       = options.DeleteOnCondition != null ? CommonUtil <T> .GetColumns(options.DeleteOnCondition, new[] { "s" })
                        : tableMapping.GetPrimaryKeyColumns().ToArray();

                    if (keyColumnNames.Length == 0 && options.DeleteOnCondition == null)
                    {
                        throw new InvalidDataException("BulkDelete requires that the entity have a primary key or the Options.DeleteOnCondition must be set.");
                    }

                    await context.Database.CloneTableAsync(destinationTableName, stagingTableName, keyColumnNames, null, cancellationToken);
                    await BulkInsertAsync(entities, options, tableMapping, dbConnection, transaction, stagingTableName, keyColumnNames, SqlBulkCopyOptions.KeepIdentity,
                                          false, cancellationToken);

                    string deleteSql = string.Format("DELETE t FROM {0} s JOIN {1} t ON {2}", stagingTableName, destinationTableName,
                                                     CommonUtil <T> .GetJoinConditionSql(options.DeleteOnCondition, keyColumnNames));
                    rowsAffected = await context.Database.ExecuteSqlRawAsync(deleteSql, cancellationToken);

                    context.Database.DropTable(stagingTableName);
                    dbTransactionContext.Commit();
                }
                catch (Exception)
                {
                    dbTransactionContext.Rollback();
                    throw;
                }
                return(rowsAffected);
            }
        }
        public static int UpdateFromQuery <T>(this IQueryable <T> querable, Expression <Func <T, T> > updateExpression, int?commandTimeout = null) where T : class
        {
            int rowAffected = 0;

            using (var dbTransactionContext = new DbTransactionContext(querable.GetDbContext()))
            {
                var dbContext = dbTransactionContext.DbContext;
                try
                {
                    var    sqlQuery         = SqlBuilder.Parse(querable.ToQueryString());
                    string setSqlExpression = updateExpression.ToSqlUpdateSetExpression(sqlQuery.GetTableAlias());
                    sqlQuery.ChangeToUpdate(sqlQuery.GetTableAlias(), setSqlExpression);
                    rowAffected = dbContext.Database.ExecuteSql(sqlQuery.Sql, sqlQuery.Parameters.ToArray(), commandTimeout);
                    dbTransactionContext.Commit();
                }
                catch (Exception ex)
                {
                    dbTransactionContext.Rollback();
                    throw ex;
                }
            }
            return(rowAffected);
        }
        public static int DeleteFromQuery <T>(this IQueryable <T> querable, int?commandTimeout = null) where T : class
        {
            int rowAffected = 0;

            using (var dbTransactionContext = new DbTransactionContext(querable.GetDbContext()))
            {
                var dbContext = dbTransactionContext.DbContext;
                try
                {
                    var sqlQuery = SqlBuilder.Parse(querable.ToQueryString());
                    sqlQuery.ChangeToDelete();
                    rowAffected = dbContext.Database.ExecuteSql(sqlQuery.Sql, sqlQuery.Parameters.ToArray(), commandTimeout);

                    dbTransactionContext.Commit();
                }
                catch (Exception ex)
                {
                    dbTransactionContext.Rollback();
                    throw ex;
                }
            }
            return(rowAffected);
        }
        private static BulkMergeResult <T> InternalBulkMerge <T>(this DbContext context, IEnumerable <T> entities, BulkMergeOptions <T> options)
        {
            int rowsAffected = 0;
            var outputRows   = new List <BulkMergeOutputRow <T> >();
            var tableMapping = context.GetTableMapping(typeof(T));
            int rowsInserted = 0;
            int rowsUpdated  = 0;
            int rowsDeleted  = 0;

            using (var dbTransactionContext = new DbTransactionContext(context))
            {
                var dbConnection = dbTransactionContext.Connection;
                var transaction  = dbTransactionContext.CurrentTransaction;
                try
                {
                    string   stagingTableName          = CommonUtil.GetStagingTableName(tableMapping, options.UsePermanentTable, dbConnection);
                    string   destinationTableName      = string.Format("[{0}].[{1}]", tableMapping.Schema, tableMapping.TableName);
                    string[] columnNames               = tableMapping.GetNonValueGeneratedColumns().ToArray();
                    string[] storeGeneratedColumnNames = tableMapping.GetPrimaryKeyColumns().ToArray();

                    if (storeGeneratedColumnNames.Length == 0 && options.MergeOnCondition == null)
                    {
                        throw new InvalidDataException("BulkMerge requires that the entity have a primary key or the Options.MergeOnCondition must be set.");
                    }

                    context.Database.CloneTable(destinationTableName, stagingTableName, null, Common.Constants.InternalId_ColumnName);
                    var bulkInsertResult = BulkInsert(entities, options, tableMapping, dbConnection, transaction, stagingTableName, null, SqlBulkCopyOptions.KeepIdentity, true);

                    IEnumerable <string> columnsToInsert = CommonUtil.FormatColumns(columnNames.Where(o => !options.GetIgnoreColumnsOnInsert().Contains(o)));
                    IEnumerable <string> columnstoUpdate = CommonUtil.FormatColumns(columnNames.Where(o => !options.GetIgnoreColumnsOnUpdate().Contains(o))).Select(o => string.Format("t.{0}=s.{0}", o));
                    List <string>        columnsToOutput = new List <string> {
                        "$Action", string.Format("{0}.{1}", "s", Constants.InternalId_ColumnName)
                    };
                    List <PropertyInfo> propertySetters = new List <PropertyInfo>();
                    Type entityType = typeof(T);

                    foreach (var storeGeneratedColumnName in storeGeneratedColumnNames)
                    {
                        columnsToOutput.Add(string.Format("inserted.[{0}]", storeGeneratedColumnName));
                        columnsToOutput.Add(string.Format("deleted.[{0}]", storeGeneratedColumnName));
                        propertySetters.Add(entityType.GetProperty(storeGeneratedColumnName));
                    }

                    string mergeSqlText = string.Format("MERGE {0} t USING {1} s ON ({2}) WHEN NOT MATCHED BY TARGET THEN INSERT ({3}) VALUES ({3}) WHEN MATCHED THEN UPDATE SET {4}{5}OUTPUT {6};",
                                                        destinationTableName, stagingTableName, CommonUtil <T> .GetJoinConditionSql(options.MergeOnCondition, storeGeneratedColumnNames, "s", "t"),
                                                        SqlUtil.ConvertToColumnString(columnsToInsert),
                                                        SqlUtil.ConvertToColumnString(columnstoUpdate),
                                                        options.DeleteIfNotMatched ? " WHEN NOT MATCHED BY SOURCE THEN DELETE " : " ",
                                                        SqlUtil.ConvertToColumnString(columnsToOutput)
                                                        );

                    var bulkQueryResult = context.BulkQuery(mergeSqlText, dbConnection, transaction, options);
                    rowsAffected = bulkQueryResult.RowsAffected;

                    foreach (var result in bulkQueryResult.Results)
                    {
                        string id     = string.Empty;
                        object entity = null;
                        string action = (string)result[0];
                        if (action != SqlMergeAction.Delete)
                        {
                            int entityId = (int)result[1];
                            id     = (storeGeneratedColumnNames.Length > 0 ? Convert.ToString(result[2]) : "PrimaryKeyMissing");
                            entity = bulkInsertResult.EntityMap[entityId];
                            if (options.AutoMapOutputIdentity && entity != null)
                            {
                                for (int i = 2; i < 2 + storeGeneratedColumnNames.Length; i++)
                                {
                                    propertySetters[0].SetValue(entity, result[i]);
                                }
                            }
                        }
                        else
                        {
                            id = Convert.ToString(result[2 + storeGeneratedColumnNames.Length]);
                        }
                        outputRows.Add(new BulkMergeOutputRow <T>(action, id));

                        if (action == SqlMergeAction.Insert)
                        {
                            rowsInserted++;
                        }
                        else if (action == SqlMergeAction.Update)
                        {
                            rowsUpdated++;
                        }
                        else if (action == SqlMergeAction.Delete)
                        {
                            rowsDeleted++;
                        }
                    }
                    context.Database.DropTable(stagingTableName);

                    //ClearEntityStateToUnchanged(context, entities);
                    dbTransactionContext.Commit();
                }
                catch (Exception)
                {
                    dbTransactionContext.Rollback();
                    throw;
                }

                return(new BulkMergeResult <T>
                {
                    Output = outputRows,
                    RowsAffected = rowsAffected,
                    RowsDeleted = rowsDeleted,
                    RowsInserted = rowsInserted,
                    RowsUpdated = rowsUpdated,
                });
            }
        }
        public static int BulkInsert <T>(this DbContext context, IEnumerable <T> entities, BulkInsertOptions <T> options)
        {
            int rowsAffected = 0;
            var tableMapping = context.GetTableMapping(typeof(T));

            using (var dbTransactionContext = new DbTransactionContext(context))
            {
                try
                {
                    var    dbConnection         = dbTransactionContext.Connection;
                    var    transaction          = dbTransactionContext.CurrentTransaction;
                    string stagingTableName     = CommonUtil.GetStagingTableName(tableMapping, options.UsePermanentTable, dbConnection);
                    string destinationTableName = string.Format("[{0}].[{1}]", tableMapping.Schema, tableMapping.TableName);

                    IEnumerable <string> columnNames = options.InputColumns != null?options.InputColumns.GetObjectProperties() : tableMapping.GetColumns(options.KeepIdentity);

                    columnNames = columnNames.Where(o => !options.IgnoreColumns.GetObjectProperties().Contains(o));
                    string[]             storeGeneratedColumnNames = tableMapping.GetPrimaryKeyColumns().ToArray();
                    IEnumerable <string> columnsToInsert           = CommonUtil.FormatColumns(columnNames);
                    columnNames = columnNames.Union(storeGeneratedColumnNames);

                    context.Database.CloneTable(destinationTableName, stagingTableName, columnNames, Common.Constants.InternalId_ColumnName);
                    var bulkInsertResult = BulkInsert(entities, options, tableMapping, dbConnection, transaction, stagingTableName, columnNames, SqlBulkCopyOptions.KeepIdentity, true);

                    List <string> columnsToOutput = new List <string> {
                        "$Action", string.Format("[{0}].[{1}]", "s", Constants.InternalId_ColumnName)
                    };
                    List <PropertyInfo> propertySetters = new List <PropertyInfo>();
                    Type entityType = typeof(T);

                    foreach (var storeGeneratedColumnName in storeGeneratedColumnNames)
                    {
                        columnsToOutput.Add(string.Format("[inserted].[{0}]", storeGeneratedColumnName));
                        propertySetters.Add(entityType.GetProperty(storeGeneratedColumnName));
                    }

                    string insertSqlText = string.Format("MERGE {0} t USING {1} s ON {2} WHEN NOT MATCHED THEN INSERT ({3}) VALUES ({3}){4};",
                                                         destinationTableName,
                                                         stagingTableName,
                                                         options.InsertIfNotExists ? CommonUtil <T> .GetJoinConditionSql(options.InsertOnCondition, storeGeneratedColumnNames, "t", "s") : "1=2",
                                                         SqlUtil.ConvertToColumnString(columnsToInsert),
                                                         columnsToOutput.Count > 0 ? " OUTPUT " + SqlUtil.ConvertToColumnString(columnsToOutput) : "");

                    if (options.KeepIdentity && storeGeneratedColumnNames.Length > 0)
                    {
                        context.Database.ToggleIdentityInsert(true, destinationTableName);
                    }
                    var bulkQueryResult = context.BulkQuery(insertSqlText, dbConnection, transaction, options);
                    if (options.KeepIdentity && storeGeneratedColumnNames.Length > 0)
                    {
                        context.Database.ToggleIdentityInsert(false, destinationTableName);
                    }
                    rowsAffected = bulkQueryResult.RowsAffected;

                    if (options.AutoMapOutputIdentity)
                    {
                        if (rowsAffected == entities.Count())
                        {
                            foreach (var result in bulkQueryResult.Results)
                            {
                                int entityId = (int)result[1];
                                var entity   = bulkInsertResult.EntityMap[entityId];
                                for (int i = 2; i < columnsToOutput.Count; i++)
                                {
                                    propertySetters[2 - i].SetValue(entity, result[i]);
                                }
                            }
                        }
                    }

                    context.Database.DropTable(stagingTableName);

                    //ClearEntityStateToUnchanged(context, entities);
                    dbTransactionContext.Commit();
                    return(rowsAffected);
                }
                catch (Exception ex)
                {
                    dbTransactionContext.Rollback();
                    throw;
                }
            }
        }