From aa2040a6293ceb876316542440840d49113e18cd Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Tue, 12 Mar 2019 20:01:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20SqlServer=20=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=B7=BB=E5=8A=A0=E5=8F=82=E6=95=B0=E6=9C=80=E5=A4=9A?= =?UTF-8?q?=202100=20=E4=B8=AA=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Repository/FreeSql.Repository.csproj | 2 +- .../SqlServer/Curd/SqlServerDeleteTest.cs | 6 + .../SqlServer/Curd/SqlServerInsertTest.cs | 13 + FreeSql.Tests/Sqlite/Curd/SqliteDeleteTest.cs | 2 +- FreeSql/FreeSql.csproj | 2 +- .../Internal/CommonProvider/InsertProvider.cs | 4 +- FreeSql/SqlServer/Curd/SqlServerInsert.cs | 287 +++++++++++++++--- 7 files changed, 273 insertions(+), 43 deletions(-) diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 6fef0a1a..dd70adab 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.13.1 + 0.3.14 YeXiangQin FreeSql 通用仓库层实现,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite,读写分离、分表分库。 https://github.com/2881099/FreeSql diff --git a/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs b/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs index 6ca9d8b2..c48cd061 100644 --- a/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs +++ b/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs @@ -68,6 +68,12 @@ namespace FreeSql.Tests.SqlServer { var item = g.sqlserver.Insert(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(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] diff --git a/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs b/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs index 625c38e7..6ac71c5b 100644 --- a/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs +++ b/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs @@ -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(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().Max(a => a.Id); + Assert.NotEqual(lastId, g.sqlserver.Insert(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(items).ExecuteInserted(); + Assert.Equal(items.First().Title, itemsInserted.First().Title); + Assert.Equal(items.Last().Title, itemsInserted.Last().Title); } [Fact] diff --git a/FreeSql.Tests/Sqlite/Curd/SqliteDeleteTest.cs b/FreeSql.Tests/Sqlite/Curd/SqliteDeleteTest.cs index 4a612fdc..56507ce9 100644 --- a/FreeSql.Tests/Sqlite/Curd/SqliteDeleteTest.cs +++ b/FreeSql.Tests/Sqlite/Curd/SqliteDeleteTest.cs @@ -66,7 +66,7 @@ namespace FreeSql.Tests.Sqlite { [Fact] public void ExecuteDeleted() { - //var item = g.Sqlite.Insert(new Topic { Title = "xxxx", CreateTime = DateTime.Now }).ExecuteInserted(); + //var item = g.Sqlite.Delete(new Topic { Title = "xxxx", CreateTime = DateTime.Now }).ExecuteInserted(); //Assert.Equal(item[0].Id, delete.Where(a => a.Id == item[0].Id).ExecuteDeleted()[0].Id); } diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 7c138df2..d93d6104 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.13.1 + 0.3.14 true YeXiangQin 打造 .NETCore 最方便的 ORM,DbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite,读写分离、分表分库。 diff --git a/FreeSql/Internal/CommonProvider/InsertProvider.cs b/FreeSql/Internal/CommonProvider/InsertProvider.cs index dfe29bec..aa0b035a 100644 --- a/FreeSql/Internal/CommonProvider/InsertProvider.cs +++ b/FreeSql/Internal/CommonProvider/InsertProvider.cs @@ -43,8 +43,8 @@ namespace FreeSql.Internal.CommonProvider { return this; } - public int ExecuteAffrows() => _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, this.ToSql(), _params); - public Task 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 ExecuteAffrowsAsync() => _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, this.ToSql(), _params); public abstract long ExecuteIdentity(); public abstract Task ExecuteIdentityAsync(); public abstract List ExecuteInserted(); diff --git a/FreeSql/SqlServer/Curd/SqlServerInsert.cs b/FreeSql/SqlServer/Curd/SqlServerInsert.cs index b928ce39..4d8468c9 100644 --- a/FreeSql/SqlServer/Curd/SqlServerInsert.cs +++ b/FreeSql/SqlServer/Curd/SqlServerInsert.cs @@ -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 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 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 ExecuteInserted() { - var sql = this.ToSql(); - if (string.IsNullOrEmpty(sql)) return new List(); + string output = null; + Func 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(); + case 1: + if (string.IsNullOrEmpty(sql[0].Item1)) return new List(); + return _orm.Ado.Query(_transaction, CommandType.Text, getOutputSql(sql[0].Item1), sql[0].Item2); + default: + var ret = new List(); + 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(tran, CommandType.Text, getOutputSql(s.Item1), s.Item2)); + tran.Commit(); + } catch { + tran.Rollback(); + } + } + } else { + foreach (var s in sql) + ret.AddRange(_orm.Ado.Query(_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(_transaction, CommandType.Text, sb.ToString(), _params); } async public override Task> ExecuteInsertedAsync() { - var sql = this.ToSql(); - if (string.IsNullOrEmpty(sql)) return new List(); + string output = null; + Func 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(); + case 1: + if (string.IsNullOrEmpty(sql[0].Item1)) return new List(); + return await _orm.Ado.QueryAsync(_transaction, CommandType.Text, getOutputSql(sql[0].Item1), sql[0].Item2); + default: + var ret = new List(); + 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(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(_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(_transaction, CommandType.Text, sb.ToString(), _params); + var sbsql = new StringBuilder().Append(sbh); + var sbsqlParams = new List(); + 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; } } }