WriteToServerAsync 在升级到 .NET 6 时未按预期工作。

huangapple go评论59阅读模式
英文:

WriteToServerAsync Not working as expected when Upgrade to .net 6

问题

以下是您要翻译的代码部分:

我的当前项目是在 .net 5 中,我已经升级到 .net 6 版本。
之前的代码运行正常,但升级后,这段代码似乎不起作用。

public async Task AddBulkAsync(List<T> entityList)
{
    try
    {
        if (entityList.Count > 0)
        {
            using (var sqlBulkCopy = GetSqlBulkCopy(this._dbContext, this._dbContext.Database.CurrentTransaction))
            {
                sqlBulkCopy.BatchSize = 10000;
                sqlBulkCopy.BulkCopyTimeout = 1800;
                var dataTable = GetDataTable(entityList, sqlBulkCopy);
                await sqlBulkCopy.WriteToServerAsync(dataTable);
            }
        }
    }
    catch (Exception)
    {
        throw;
    }
}

internal DataTable GetDataTable<T>(IList<T> entities, SqlBulkCopy sqlBulkCopy)
{
    // ...(代码太长,无法一次性翻译完毕)...
}

private SqlBulkCopy GetSqlBulkCopy(DbContext dbContext, IDbContextTransaction transaction)
{
    // ...(代码太长,无法一次性翻译完毕)...
}

问题部分:

这似乎不按预期工作
await sqlBulkCopy.WriteToServerAsync(dataTable);
我尝试使用非异步方法实现
sqlBulkCopy.WriteToServer(dataTable);
但仍然发生相同的问题
它返回一个错误

"一个或多个错误发生。(用户 'Devadmin' 的登录失败。)"

我尝试了 Task.Run 方法,它不会返回任何错误,但数据不会插入表中。

Task.Run(async () => await sqlBulkCopy.WriteToServerAsync(dataTable));
请有人帮我解决这个问题。
英文:

My current project was in .net 5 which I have upgraded to .net 6 version.
Earlier the code was working fine but after upgrade this code seems to be not working.

        public async Task AddBulkAsync(List&lt;T&gt; entityList)
        {
            try
            {
                if (entityList.Count &gt; 0)
                {
                    using (var sqlBulkCopy = GetSqlBulkCopy(this._dbContext, this._dbContext.Database.CurrentTransaction))
                    {
                        sqlBulkCopy.BatchSize = 10000;
                        sqlBulkCopy.BulkCopyTimeout = 1800;
                        var dataTable = GetDataTable(entityList, sqlBulkCopy);
                        await sqlBulkCopy.WriteToServerAsync(dataTable);
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        internal DataTable GetDataTable&lt;T&gt;(IList&lt;T&gt; entities, SqlBulkCopy sqlBulkCopy)
        {
            var dataTable = new DataTable();
            var columnsDict = new Dictionary&lt;string, object&gt;();
            var ownedEntitiesMappedProperties = new HashSet&lt;string&gt;();

            var type = entities[0].GetType();
            var entityType = this._dbContext.Model.FindEntityType(type);
            //var entityPropertiesDict = entityType.GetProperties().Where(a =&gt; tableInfo.PropertyColumnNamesDict.ContainsKey(a.Name)).ToDictionary(a =&gt; a.Name, a =&gt; a);
            var entityPropertiesDict = entityType.GetProperties().ToDictionary(a =&gt; a.Name, a =&gt; a);
            var entityNavigationOwnedDict = entityType.GetNavigations().Where(a =&gt; a.GetTargetType().IsOwned()).ToDictionary(a =&gt; a.Name, a =&gt; a);
            var properties = type.GetProperties();
            // var discriminatorColumn = tableInfo.ShadowProperties.Count == 0 ? null : tableInfo.ShadowProperties.ElementAt(0);

            foreach (var property in properties)
            {
                if (entityPropertiesDict.ContainsKey(property.Name))
                {
                    var propertyEntityType = entityPropertiesDict[property.Name];
                    string columnName = propertyEntityType.GetColumnName();

                    // var isConvertible = tableInfo.ConvertibleProperties.ContainsKey(columnName);
                    var propertyType = property.PropertyType;

                    var underlyingType = Nullable.GetUnderlyingType(propertyType);
                    if (underlyingType != null)
                    {
                        propertyType = underlyingType;
                    }

                    dataTable.Columns.Add(columnName, propertyType);
                    columnsDict.Add(property.Name, null);
                }
            }


            foreach (var entity in entities)
            {
                foreach (var property in properties)
                {
                    if (entityPropertiesDict.ContainsKey(property.Name))
                    {
                        var propertyValue = property.GetValue(entity, null);
                        if (property.PropertyType == typeof(Guid) &amp;&amp; (Guid)propertyValue == default(Guid))
                        {
                            propertyValue = Guid.NewGuid();
                        }

                        columnsDict[property.Name] = propertyValue;
                    }
                }
                var record = columnsDict.Values.ToArray();
                dataTable.Rows.Add(record);
            }

            foreach (DataColumn item in dataTable.Columns)  //Add mapping
            {
                sqlBulkCopy.ColumnMappings.Add(item.ColumnName, item.ColumnName);
            }
            string schema = entityType.GetSchema() != null ? entityType.GetSchema() : &quot;dbo&quot;;
            dataTable.TableName = schema + &quot;.&quot; + entityType.GetTableName();
            sqlBulkCopy.DestinationTableName = dataTable.TableName;
            return dataTable;
        }

        private SqlBulkCopy GetSqlBulkCopy(DbContext dbContext, IDbContextTransaction transaction)
        {
            var sqlConnection = dbContext.Database.GetDbConnection().ConnectionString;
            if (transaction == null)
            {
                //return new SqlBulkCopy(sqlConnection, sqlBulkCopyOptions, null);
                return new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.CheckConstraints);
            }
            else
            {
                var sqlTransaction = (SqlTransaction)transaction.GetDbTransaction();
                //return new SqlBulkCopy(sqlConnection, sqlBulkCopyOptions, sqlTransaction);
                return new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.CheckConstraints);
            }
        }

This seems not working as expected
await sqlBulkCopy.WriteToServerAsync(dataTable);
I have tried to implement with non-async method
sqlBulkCopy.WriteToServer(dataTable);
But still, the same issue is occurring
It returns an error

"One or more errors occurred. (Login failed for user 'Devadmin'.)"
I have tried Task.Run method It will not return any error but will data will not be inserted into the table.

Task.Run(async () =&gt; await sqlBulkCopy.WriteToServerAsync(dataTable));

Please some one help me on this issue.

答案1

得分: 0

The problem is that the DbConnection has already been opened. So when you try to retrieve the connection string, the password has been wiped.

You could add persist security info=true to the string, but I would advise against it.

Instead, rewrite your GetSqlBulkCopy function, which is fundamentally broken in the way it handles transactions, so that it now reuses the connection object it receives, instead of just the connection string.

private SqlBulkCopy GetSqlBulkCopy(SqlConnection connection, DbContext dbContext)
{
    var sqlTransaction = _dbContext.Database.CurrentTransaction.GetDbTransaction() as SqlTransaction;
    return new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.CheckConstraints, sqltransaction);
}

And then

var sqlConnection = (SqlConnection)dbContext.Database.GetDbConnection();
var wasOpen = sqlConnection.ConnectionState == ConnectionState.Open;
try
{
    using (var sqlBulkCopy = GetSqlBulkCopy(sqlConnection, this._dbContext))
    {
        //etc
        if (!wasOpen)
            await sqlConnection.OpenAsync();
        // etc
    }
}
finally
{
    if (!wasOpen)
        sqlConnection.Close();
}
英文:

The problem is that the DbConnection has already been opened. So when you try retrieve the connection string, the password has been wiped.

You could add persist security info=true to the string, but I would advise against it.

Instead, rewrite your GetSqlBulkCopy function, which is anyway fundamentally broken in the way it handles transactions, so that it now reuses the connection object it receives, instead of just the connection string.

private SqlBulkCopy GetSqlBulkCopy(SqlConnection connection, DbContext dbContext)
{
    var sqlTransaction = _dbContext.Database.CurrentTransaction.GetDbTransaction() as SqlTransaction;
    return new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.CheckConstraints, sqltransaction);
}

And then

var sqlConnection = (SqlConnection)dbContext.Database.GetDbConnection();
var wasOpen = sqlConnection.ConnectionState == ConnectionState.Open
try
{
    using (var sqlBulkCopy = GetSqlBulkCopy(sqlConnection, this._dbContext))
    {
        //etc
        if (!wasOpen)
            await sqlConnection.OpenAsync();
        // etc
    }
}
finally
{
    if (!wasOpen)
        sqlConnection.Close();
}

答案2

得分: -1

不要回答我要翻译的问题。以下是翻译好的部分:

"Instead of adding persist security info=true to the string. we can use following code."
"这段代码中,我们可以使用以下代码来替代在字符串中添加 persist security info=true。"

"public async Task AddBulkAsync(List<T> entityList)"
"public async Task AddBulkAsync(List<T> entityList)"

"var sqlConnectionString = SqlConnectionToConnectionString((SqlConnection)this._dbContext.Database.GetDbConnection());"
"var sqlConnectionString = SqlConnectionToConnectionString((SqlConnection)this._dbContext.Database.GetDbConnection());"

"var sqlConnection = new SqlConnection(sqlConnectionString);"
"var sqlConnection = new SqlConnection(sqlConnectionString);"

"var wasOpen = sqlConnection.State == ConnectionState.Open;"
"var wasOpen = sqlConnection.State == ConnectionState.Open;"

"using (var sqlBulkCopy = new SqlBulkCopy(sqlConnectionString, SqlBulkCopyOptions.CheckConstraints))"
"using (var sqlBulkCopy = new SqlBulkCopy(sqlConnectionString, SqlBulkCopyOptions.CheckConstraints))"

"if (!wasOpen)"
"if (!wasOpen)"

"await sqlConnection.OpenAsync();"
"await sqlConnection.OpenAsync();"

"sqlBulkCopy.BatchSize = 10000;"
"sqlBulkCopy.BatchSize = 10000;"

"sqlBulkCopy.BulkCopyTimeout = 1800;"
"sqlBulkCopy.BulkCopyTimeout = 1800;"

"var dataTable = GetDataTable(entityList, sqlBulkCopy);"
"var dataTable = GetDataTable(entityList, sqlBulkCopy);"

"await sqlBulkCopy.WriteToServerAsync(dataTable);"
"await sqlBulkCopy.WriteToServerAsync(dataTable);"

"if (!wasOpen)"
"if (!wasOpen)"

"sqlConnection.Close();"
"sqlConnection.Close();"

"This has helped me to solve the issue. The connection string was blocking password and cannot able to do the connection string established."
"这有助于我解决了问题。连接字符串阻止了密码,无法建立连接字符串。"

英文:

Instead of adding persist security info=true to the string. we can use following code.

public async Task AddBulkAsync(List&lt;T&gt; entityList)
    {
                var sqlConnectionString = SqlConnectionToConnectionString((SqlConnection)this._dbContext.Database.GetDbConnection());
                var sqlConnection = new SqlConnection(sqlConnectionString);
                var wasOpen = sqlConnection.State == ConnectionState.Open;
                try
                {
                    using (var sqlBulkCopy = new SqlBulkCopy(sqlConnectionString, SqlBulkCopyOptions.CheckConstraints))
                    {
                        if (!wasOpen)
                            await sqlConnection.OpenAsync();
                        
                        sqlBulkCopy.BatchSize = 10000;
                        sqlBulkCopy.BulkCopyTimeout = 1800;
                        var dataTable = GetDataTable(entityList, sqlBulkCopy);
                        await sqlBulkCopy.WriteToServerAsync(dataTable);
                    }
                }
                finally
                {
                    if (!wasOpen)
                        sqlConnection.Close();
                }
}
    private static string SqlConnectionToConnectionString(SqlConnection conn)
    {
        System.Reflection.PropertyInfo property = conn.GetType().GetProperty(&quot;ConnectionOptions&quot;, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        object optionsObject = property.GetValue(conn, null);
        System.Reflection.MethodInfo method = optionsObject.GetType().GetMethod(&quot;UsersConnectionString&quot;);
        string connStr = method.Invoke(optionsObject, new object[] { false }) as string; // argument is &quot;hidePassword&quot; so we set it to false
        return connStr;
    }

This has helped me to solve the issue. The connection string was blocking password and cannot able to do the connection string established.

huangapple
  • 本文由 发表于 2023年6月29日 20:21:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76581013.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定