解决 SqlServer 批量添加参数最多 2100 个参数

This commit is contained in:
28810 2019-03-12 20:01:20 +08:00
parent f653f03073
commit aa2040a629
7 changed files with 273 additions and 43 deletions

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.3.13.1</Version>
<Version>0.3.14</Version>
<Authors>YeXiangQin</Authors>
<Description>FreeSql 通用仓库层实现,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite读写分离、分表分库。</Description>
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>

View File

@ -68,6 +68,12 @@ namespace FreeSql.Tests.SqlServer {
var item = g.sqlserver.Insert<Topic>(new Topic { Title = "xxxx", CreateTime = DateTime.Now }).ExecuteInserted();
Assert.Equal(item[0].Id, delete.Where(a => a.Id == item[0].Id).ExecuteDeleted()[0].Id);
var items = Enumerable.Range(0, 3001).Select(a => new Topic { Title = "xxxx" + a, CreateTime = DateTime.Now });
var itemsInserted = g.sqlserver.Insert<Topic>(items).ExecuteInserted();
Assert.Equal(items.First().Title, itemsInserted[0].Title);
Assert.Equal(itemsInserted[0].Id, delete.Where(a => a.Id == itemsInserted[0].Id).ExecuteDeleted()[0].Id);
}
[Fact]

View File

@ -66,6 +66,9 @@ namespace FreeSql.Tests.SqlServer {
Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows());
Assert.Equal(10, insert.AppendData(items).ExecuteAffrows());
items = Enumerable.Range(0, 9989).Select(a => new Topic { Title = "newtitle" + a, CreateTime = DateTime.Now }).ToList();
Assert.Equal(9989, g.sqlserver.Insert<Topic>(items).ExecuteAffrows());
}
[Fact]
public void ExecuteIdentity() {
@ -73,6 +76,11 @@ namespace FreeSql.Tests.SqlServer {
for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now });
Assert.NotEqual(0, insert.AppendData(items.First()).ExecuteIdentity());
items = Enumerable.Range(0, 9999).Select(a => new Topic { Title = "newtitle" + a, CreateTime = DateTime.Now }).ToList();
var lastId = g.sqlite.Select<Topic>().Max(a => a.Id);
Assert.NotEqual(lastId, g.sqlserver.Insert<Topic>(items).ExecuteIdentity());
}
[Fact]
public void ExecuteInserted() {
@ -80,6 +88,11 @@ namespace FreeSql.Tests.SqlServer {
for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now });
var items2 = insert.AppendData(items).ExecuteInserted();
items = Enumerable.Range(0, 9990).Select(a => new Topic { Title = "newtitle" + a, CreateTime = DateTime.Now }).ToList();
var itemsInserted = g.sqlserver.Insert<Topic>(items).ExecuteInserted();
Assert.Equal(items.First().Title, itemsInserted.First().Title);
Assert.Equal(items.Last().Title, itemsInserted.Last().Title);
}
[Fact]

View File

@ -66,7 +66,7 @@ namespace FreeSql.Tests.Sqlite {
[Fact]
public void ExecuteDeleted() {
//var item = g.Sqlite.Insert<Topic>(new Topic { Title = "xxxx", CreateTime = DateTime.Now }).ExecuteInserted();
//var item = g.Sqlite.Delete<Topic>(new Topic { Title = "xxxx", CreateTime = DateTime.Now }).ExecuteInserted();
//Assert.Equal(item[0].Id, delete.Where(a => a.Id == item[0].Id).ExecuteDeleted()[0].Id);
}

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.3.13.1</Version>
<Version>0.3.14</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>YeXiangQin</Authors>
<Description>打造 .NETCore 最方便的 ORMDbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite读写分离、分表分库。</Description>

View File

@ -43,8 +43,8 @@ namespace FreeSql.Internal.CommonProvider {
return this;
}
public int ExecuteAffrows() => _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, this.ToSql(), _params);
public Task<int> ExecuteAffrowsAsync() => _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, this.ToSql(), _params);
public virtual int ExecuteAffrows() => _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, this.ToSql(), _params);
public virtual Task<int> ExecuteAffrowsAsync() => _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, this.ToSql(), _params);
public abstract long ExecuteIdentity();
public abstract Task<long> ExecuteIdentityAsync();
public abstract List<T1> ExecuteInserted();

View File

@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -12,58 +14,267 @@ namespace FreeSql.SqlServer.Curd {
: base(orm, commonUtils, commonExpression) {
}
public override long ExecuteIdentity() {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
public override int ExecuteAffrows() {
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return 0;
case 1: return _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, sql[0].Item1, sql[0].Item2);
default:
var affrows = 0;
if (_transaction == null) {
using (var conn = _orm.Ado.MasterPool.Get()) {
var tran = conn.Value.BeginTransaction();
try {
foreach (var s in sql)
affrows += _orm.Ado.ExecuteNonQuery(tran, CommandType.Text, s.Item1, s.Item2);
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
foreach (var s in sql)
affrows += _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, s.Item1, s.Item2);
}
return affrows;
}
}
async public override Task<int> ExecuteAffrowsAsync() {
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return 0;
case 1: return await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, sql[0].Item1, sql[0].Item2);
default:
var affrows = 0;
if (_transaction == null) {
using (var conn = await _orm.Ado.MasterPool.GetAsync()) {
var tran = conn.Value.BeginTransaction();
try {
foreach (var s in sql)
affrows += await _orm.Ado.ExecuteNonQueryAsync(tran, CommandType.Text, s.Item1, s.Item2);
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
foreach (var s in sql)
affrows += await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, s.Item1, s.Item2);
}
return affrows;
}
}
return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0;
public override long ExecuteIdentity() {
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return 0;
case 1:
if (string.IsNullOrEmpty(sql[0].Item1)) return 0;
return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql[0].Item1, "; SELECT SCOPE_IDENTITY();"), sql[0].Item2)), out var trylng) ? trylng : 0;
default:
long ret = 0;
if (_transaction == null) {
using (var conn = _orm.Ado.MasterPool.Get()) {
var tran = conn.Value.BeginTransaction();
try {
for (var a = 0; a < sql.Count; a++) {
var s = sql[a];
if (a < sql.Count - 1) _orm.Ado.ExecuteNonQuery(tran, CommandType.Text, s.Item1, s.Item2);
else ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(tran, CommandType.Text, string.Concat(s.Item1, "; SELECT SCOPE_IDENTITY();"), s.Item2)), out trylng) ? trylng : 0;
}
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
for (var a = 0; a < sql.Count; a++) {
var s = sql[a];
if (a < sql.Count - 1) _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, s.Item1, s.Item2);
else ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(s.Item1, "; SELECT SCOPE_IDENTITY();"), s.Item2)), out trylng) ? trylng : 0;
}
}
return ret;
}
}
async public override Task<long> ExecuteIdentityAsync() {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0;
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return 0;
case 1:
if (string.IsNullOrEmpty(sql[0].Item1)) return 0;
return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql[0].Item1, "; SELECT SCOPE_IDENTITY();"), sql[0].Item2)), out var trylng) ? trylng : 0;
default:
long ret = 0;
if (_transaction == null) {
using (var conn = await _orm.Ado.MasterPool.GetAsync()) {
var tran = conn.Value.BeginTransaction();
try {
for (var a = 0; a < sql.Count; a++) {
var s = sql[a];
if (a < sql.Count - 1) await _orm.Ado.ExecuteNonQueryAsync(tran, CommandType.Text, s.Item1, s.Item2);
else ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(tran, CommandType.Text, string.Concat(s.Item1, "; SELECT SCOPE_IDENTITY();"), s.Item2)), out trylng) ? trylng : 0;
}
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
for (var a = 0; a < sql.Count; a++) {
var s = sql[a];
if (a < sql.Count - 1) await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, s.Item1, s.Item2);
else ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(s.Item1, "; SELECT SCOPE_IDENTITY();"), s.Item2)), out trylng) ? trylng : 0;
}
}
return ret;
}
}
public override List<T1> ExecuteInserted() {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return new List<T1>();
string output = null;
Func<string, string> getOutputSql = oldsql => {
if (string.IsNullOrEmpty(output)) {
var sb = new StringBuilder();
sb.Append(" OUTPUT ");
var colidx = 0;
foreach (var col in _table.Columns.Values) {
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.QuoteReadColumn(col.CsType, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
output = sb.ToString();
}
var validx = oldsql.IndexOf(") VALUES");
if (validx == -1) throw new ArgumentException("找不到 VALUES");
var newsql = new StringBuilder().Append(output);
newsql.Insert(0, oldsql.Substring(0, validx + 1));
newsql.Append(oldsql.Substring(validx + 1));
return newsql.ToString();
};
var sb = new StringBuilder();
sb.Append(" OUTPUT ");
var colidx = 0;
foreach (var col in _table.Columns.Values) {
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.QuoteReadColumn(col.CsType, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return new List<T1>();
case 1:
if (string.IsNullOrEmpty(sql[0].Item1)) return new List<T1>();
return _orm.Ado.Query<T1>(_transaction, CommandType.Text, getOutputSql(sql[0].Item1), sql[0].Item2);
default:
var ret = new List<T1>();
if (_transaction == null) {
using (var conn = _orm.Ado.MasterPool.Get()) {
var tran = conn.Value.BeginTransaction();
try {
foreach (var s in sql)
ret.AddRange(_orm.Ado.Query<T1>(tran, CommandType.Text, getOutputSql(s.Item1), s.Item2));
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
foreach (var s in sql)
ret.AddRange(_orm.Ado.Query<T1>(_transaction, CommandType.Text, getOutputSql(s.Item1), s.Item2));
}
return ret;
}
var validx = sql.IndexOf(") VALUES");
if (validx == -1) throw new ArgumentException("找不到 VALUES");
sb.Insert(0, sql.Substring(0, validx + 1));
sb.Append(sql.Substring(validx + 1));
return _orm.Ado.Query<T1>(_transaction, CommandType.Text, sb.ToString(), _params);
}
async public override Task<List<T1>> ExecuteInsertedAsync() {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return new List<T1>();
string output = null;
Func<string, string> getOutputSql = oldsql => {
if (string.IsNullOrEmpty(output)) {
var sb = new StringBuilder();
sb.Append(" OUTPUT ");
var colidx = 0;
foreach (var col in _table.Columns.Values) {
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.QuoteReadColumn(col.CsType, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
output = sb.ToString();
}
var validx = oldsql.IndexOf(") VALUES");
if (validx == -1) throw new ArgumentException("找不到 VALUES");
var newsql = new StringBuilder().Append(output);
newsql.Insert(0, oldsql.Substring(0, validx + 1));
newsql.Append(oldsql.Substring(validx + 1));
return oldsql;
};
var sb = new StringBuilder();
sb.Append(" OUTPUT ");
var colidx = 0;
foreach (var col in _table.Columns.Values) {
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.QuoteReadColumn(col.CsType, $"INSERTED.{_commonUtils.QuoteSqlName(col.Attribute.Name)}")).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
var sql = this.ToSql2100();
switch (sql.Count) {
case 0: return new List<T1>();
case 1:
if (string.IsNullOrEmpty(sql[0].Item1)) return new List<T1>();
return await _orm.Ado.QueryAsync<T1>(_transaction, CommandType.Text, getOutputSql(sql[0].Item1), sql[0].Item2);
default:
var ret = new List<T1>();
if (_transaction == null) {
using (var conn = await _orm.Ado.MasterPool.GetAsync()) {
var tran = conn.Value.BeginTransaction();
try {
foreach (var s in sql)
ret.AddRange(await _orm.Ado.QueryAsync<T1>(tran, CommandType.Text, getOutputSql(s.Item1), s.Item2));
tran.Commit();
} catch {
tran.Rollback();
}
}
} else {
foreach (var s in sql)
ret.AddRange(await _orm.Ado.QueryAsync<T1>(_transaction, CommandType.Text, getOutputSql(s.Item1), s.Item2));
}
return ret;
}
}
var validx = sql.IndexOf(") VALUES");
if (validx == -1) throw new ArgumentException("找不到 VALUES");
sb.Insert(0, sql.Substring(0, validx + 1));
sb.Append(sql.Substring(validx + 1));
public List<(string, DbParameter[])> ToSql2100() { //传入的请求具有过多的参数。该服务器支持最多 2100 个参数。请减少参数的数目,然后重新发送该请求。
if (_source == null || _source.Any() == false) return new List<(string, DbParameter[])>();
var ret = new List<(string, DbParameter[])>();
var sbhead = new StringBuilder();
sbhead.Append("INSERT INTO ").Append(_commonUtils.QuoteSqlName(_tableRule?.Invoke(_table.DbName) ?? _table.DbName)).Append("(");
var colidx = 0;
foreach (var col in _table.Columns.Values)
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
if (colidx > 0) sbhead.Append(", ");
sbhead.Append(_commonUtils.QuoteSqlName(col.Attribute.Name));
++colidx;
}
sbhead.Append(") VALUES");
var sbh = sbhead.ToString();
return await _orm.Ado.QueryAsync<T1>(_transaction, CommandType.Text, sb.ToString(), _params);
var sbsql = new StringBuilder().Append(sbh);
var sbsqlParams = new List<DbParameter>();
var didx = 0;
foreach (var d in _source) {
if ((didx + 1) * colidx >= 2100) {
ret.Add((sbsql.ToString(), sbsqlParams.ToArray()));
sbsql.Clear().Append(sbh);
sbsqlParams.Clear();
}
if (sbsqlParams.Count > 0) sbsql.Append(", ");
sbsql.Append("(");
var colidx2 = 0;
foreach (var col in _table.Columns.Values)
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
if (colidx2 > 0) sbsql.Append(", ");
sbsql.Append(_commonUtils.QuoteWriteParamter(col.CsType, $"{_commonUtils.QuoteParamterName(col.CsName)}{didx}"));
object val = null;
if (_table.Properties.TryGetValue(col.CsName, out var tryp)) {
val = tryp.GetValue(d);
if (col.Attribute.IsPrimary && (col.CsType == typeof(Guid) || col.CsType == typeof(Guid?))
&& (val == null || (Guid)val == Guid.Empty)) tryp.SetValue(d, val = FreeUtil.NewMongodbId());
}
sbsqlParams.Add(_commonUtils.AppendParamter(null, $"{col.CsName}{didx}", col.CsType, val));
++colidx2;
}
sbsql.Append(")");
++didx;
}
ret.Add((sbsql.ToString(), sbsqlParams.ToArray()));
return ret;
}
}
}