diff --git a/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs b/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs
index 914f75161..dea7c54ee 100644
--- a/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs
+++ b/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs
@@ -11,7 +11,31 @@ namespace FreeSql
///
/// 工作单元管理器
///
- public class UnitOfWorkManager : IDisposable
+ public interface IUnitOfWorkManager : IDisposable
+ {
+ IFreeSql Orm { get; }
+ ///
+ /// 当前的工作单元
+ ///
+ IUnitOfWork Current { get; }
+ ///
+ /// 将仓储的事务交给我管理
+ ///
+ ///
+ void Binding(IBaseRepository repository);
+ ///
+ /// 创建工作单元
+ ///
+ /// 事务传播方式
+ /// 事务隔离级别
+ ///
+ IUnitOfWork Begin(Propagation propagation = Propagation.Required, IsolationLevel? isolationLevel = null);
+ }
+
+ ///
+ /// 工作单元管理器
+ ///
+ public class UnitOfWorkManager : IUnitOfWorkManager
{
internal DbContextScopedFreeSql _ormScoped;
internal IFreeSql OrmOriginal => _ormScoped?._originalFsql;
diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs
index fb0b8be92..45f141891 100644
--- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs
+++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs
@@ -98,11 +98,37 @@ ON CONFLICT(""id"") DO UPDATE SET
var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList();
Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count());
}
+
class tbiou02
{
public int id { get; set; }
public string name { get; set; }
}
+
+ [Fact]
+ public void InsertOrUpdate_TempPrimary()
+ {
+ fsql.Delete().Where("1=1").ExecuteAffrows();
+ var iou = fsql.InsertOrUpdate().SetSource(new tbiou_temp { name = "01", description = "testval" }, m => new { m.name });
+ var sql = iou.ToSql();
+ Assert.Equal(@"INSERT INTO ""tbiou_temp""(""name"", ""description"") VALUES('01', 'testval')
+ON CONFLICT(""name"") DO UPDATE SET
+""description"" = EXCLUDED.""description""", sql);
+ Assert.Equal(1, iou.ExecuteAffrows());
+
+ var iou2 = fsql.InsertOrUpdate().SetSource(new tbiou_temp { name = "01", description = "testval2" }, m => new { m.name }).ExecuteAffrows();
+ Assert.Equal(1, iou2);
+
+ }
+ [Index("uix_tbiou_temp_name", "name", true)]
+ class tbiou_temp
+ {
+ [Column(IsPrimary = true, IsIdentity = true)]
+ public int id { get; set; }
+
+ public string name { get; set; }
+ public string description { get; set; }
+ }
[Fact]
public void InsertOrUpdate_OnePrimaryAndIdentity()
{
diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
index 14f8eb1d5..135953b65 100644
--- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
+++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
@@ -420,21 +420,102 @@ public static partial class FreeSqlGlobalExtensions
case DataType.SqlServer:
case DataType.OdbcSqlServer:
case DataType.CustomSqlServer:
+ {
+ var oldalias = selectProvider._aliasRule;
+ selectProvider._aliasRule = (type, old) =>
+ {
+ if (oldalias != null) old = oldalias(type, old);
+ if (string.IsNullOrWhiteSpace(indexName) == false && type == selectProvider._tables[0].Table.Type) return LocalAppendWithString(old, $"index={indexName}");
+ if (rule == null) return old;
+ return rule.TryGetValue(type, out var tryidxName) && string.IsNullOrWhiteSpace(tryidxName) == false ? LocalAppendWithString(old, $"index={tryidxName}") : old;
+ };
+ }
+ break;
+ case DataType.MySql:
+ case DataType.OdbcMySql:
+ case DataType.CustomMySql:
+ {
+ var oldalias = selectProvider._aliasRule;
+ selectProvider._aliasRule = (type, old) =>
+ {
+ if (oldalias != null) old = oldalias(type, old);
+ if (string.IsNullOrWhiteSpace(indexName) == false && type == selectProvider._tables[0].Table.Type) return LocalAppendMySqlIndex(old, indexName);
+ if (rule == null) return old;
+ return rule.TryGetValue(type, out var tryidxName) && string.IsNullOrWhiteSpace(tryidxName) == false ? LocalAppendMySqlIndex(old, indexName) : old;
+ };
+ }
+ break;
+ case DataType.Oracle:
+ case DataType.OdbcOracle:
+ case DataType.CustomOracle:
+ {
+ var hintParts = new List();
+ if (!string.IsNullOrEmpty(indexName) && !string.IsNullOrEmpty(selectProvider._tables[0].Alias))
+ {
+ string alias = selectProvider._tables[0].Alias;
+ hintParts.Add($"INDEX({alias} {indexName})");
+ }
+ if (rule != null && rule.Count > 0)
+ {
+ var tablesMap = selectProvider._tables
+ .Skip(1)
+ .Where(t => !string.IsNullOrEmpty(t.Alias) && t.Table?.Type != null)
+ .ToDictionary(t => t.Table.Type, t => t);
+ foreach (var indexRule in rule)
+ {
+ if (tablesMap.TryGetValue(indexRule.Key, out var tableInfo))
+ {
+ string otherIndexName = indexRule.Value;
+ if (!string.IsNullOrEmpty(otherIndexName))
+ hintParts.Add($"INDEX({tableInfo.Alias} {otherIndexName})");
+ }
+ }
+ }
+ if (hintParts.Count > 0)
+ {
+ string finalHint = $"/*+ {string.Join(" ", hintParts)} */";
+ var _select = selectProvider._select;
+ int selectKeywordIndex = _select.IndexOf("SELECT ", StringComparison.OrdinalIgnoreCase);
+ if (selectKeywordIndex != -1)
+ {
+ int insertionPoint = selectKeywordIndex + "SELECT ".Length;
+ selectProvider._select = _select.Insert(insertionPoint, $"{finalHint} ");
+ }
+ }
+ }
+ break;
+ case DataType.Sqlite:
+ {
+ var oldalias = selectProvider._aliasRule;
+ selectProvider._aliasRule = (type, old) =>
+ {
+ if (oldalias != null) old = oldalias(type, old);
+ if (string.IsNullOrWhiteSpace(indexName) == false && type == selectProvider._tables[0].Table.Type) return LocalAppendSqliteIndex(old, indexName);
+ if (rule == null) return old;
+ return rule.TryGetValue(type, out var tryidxName) && string.IsNullOrWhiteSpace(tryidxName) == false ? LocalAppendSqliteIndex(old, indexName) : old;
+ };
+ }
break;
- default:
- return query;
}
- var oldalias = selectProvider._aliasRule;
- selectProvider._aliasRule = (type, old) =>
- {
- if (oldalias != null) old = oldalias(type, old);
- if (string.IsNullOrWhiteSpace(indexName) == false && type == selectProvider._tables[0].Table.Type) return LocalAppendWithString(old, $"index={indexName}");
- if (rule == null) return old;
- return rule.TryGetValue(type, out var tryidxName) && string.IsNullOrWhiteSpace(tryidxName) == false ? LocalAppendWithString(old, $"index={tryidxName}") : old;
- };
+
return query;
}
static string LocalAppendWithString(string old, string str) => old?.Contains(" With(") == true ? old.Replace(" With(", $" With({str}, ") : $"{old} With({str})";
+ static string LocalAppendMySqlIndex(string old, string indexName)
+ {
+ if (string.IsNullOrEmpty(old)) return $"FORCE INDEX({indexName})";
+ int forceIndexPosition = old.IndexOf("FORCE INDEX(", StringComparison.OrdinalIgnoreCase);
+ if (forceIndexPosition == -1) return $"{old} FORCE INDEX({indexName})";
+ int closingParenPosition = old.IndexOf(')', forceIndexPosition);
+ if (closingParenPosition != -1) return old.Insert(closingParenPosition, $",{indexName}");
+ return old;
+ }
+ static string LocalAppendSqliteIndex(string old, string indexName)
+ {
+ if (string.IsNullOrEmpty(old)) return $"INDEXED BY {indexName}";
+ if (old.IndexOf("INDEXED BY", StringComparison.OrdinalIgnoreCase) != -1) return old;
+ return $"{old} INDEXED BY {indexName}";
+ }
///
/// 设置全局 SqlServer: with(nolock)
diff --git a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
index 4997b0c9b..3731b762b 100644
--- a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
@@ -185,14 +185,21 @@ namespace FreeSql.ClickHouse.Curd
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, null, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
var data = ToDataTable();
+ var columns = new string[_table.ColumnsByPosition.Length];
+ for (var i = 0; i < columns.Length; i++)
+ {
+ columns[i] = _table.ColumnsByPosition[i].CsName;
+ }
using (var conn = await _orm.Ado.MasterPool.GetAsync())
{
using (var bulkCopyInterface = new ClickHouseBulkCopy(conn.Value as ClickHouseConnection)
{
DestinationTableName = data.TableName,
- BatchSize = _source.Count
+ BatchSize = _source.Count,
+ ColumnNames = columns,
})
{
+ await bulkCopyInterface.InitAsync();
await bulkCopyInterface.WriteToServerAsync(data, default);
}
}
@@ -201,7 +208,7 @@ namespace FreeSql.ClickHouse.Curd
catch (Exception ex)
{
exception = ex;
- throw ex;
+ throw;
}
finally
{
diff --git a/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLInsertOrUpdate.cs b/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLInsertOrUpdate.cs
index ed3716f17..03725d1ea 100644
--- a/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLInsertOrUpdate.cs
+++ b/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLInsertOrUpdate.cs
@@ -56,7 +56,7 @@ namespace FreeSql.Custom.PostgreSQL
if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
else
{
- var ocdu = new CustomPostgreSQLOnConflictDoUpdate(insert.InsertIdentity());
+ var ocdu = new CustomPostgreSQLOnConflictDoUpdate(_tempPrimarys?.Length > 0 ? insert : insert.InsertIdentity());
ocdu._tempPrimarys = _tempPrimarys;
var cols = _table.Columns.Values.Where(a => _updateSetDict.ContainsKey(a.Attribute.Name) ||
_tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
diff --git a/Providers/FreeSql.Provider.Duckdb/FreeSql.Provider.Duckdb.csproj b/Providers/FreeSql.Provider.Duckdb/FreeSql.Provider.Duckdb.csproj
index 68526bf4d..32fce5cd2 100644
--- a/Providers/FreeSql.Provider.Duckdb/FreeSql.Provider.Duckdb.csproj
+++ b/Providers/FreeSql.Provider.Duckdb/FreeSql.Provider.Duckdb.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESInsertOrUpdate.cs b/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESInsertOrUpdate.cs
index 511c09617..13da52b11 100644
--- a/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESInsertOrUpdate.cs
+++ b/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESInsertOrUpdate.cs
@@ -56,7 +56,7 @@ namespace FreeSql.KingbaseES
if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
else
{
- var ocdu = new KingbaseESOnConflictDoUpdate(insert.InsertIdentity());
+ var ocdu = new KingbaseESOnConflictDoUpdate(_tempPrimarys?.Length > 0 ? insert : insert.InsertIdentity());
ocdu._tempPrimarys = _tempPrimarys;
var cols = _table.Columns.Values.Where(a => _updateSetDict.ContainsKey(a.Attribute.Name) ||
_tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs
index 588b91f1f..1a2e09de6 100644
--- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs
+++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs
@@ -149,8 +149,17 @@ namespace FreeSql.KingbaseES
protected override string GetComparisonDDLStatements(params TypeSchemaAndName[] objects)
{
InitIsSysV8R3();
+ var builder = new System.Data.Common.DbConnectionStringBuilder
+ {
+ ConnectionString = _orm.Ado.ConnectionString
+ };
+ var searchPath = builder.ContainsKey("SearchPath") ? builder["SearchPath"].ToString() : "PUBLIC"; //读取链接字符串中的SearchPath 来确定架构模式
+ if (searchPath.Contains(','))
+ {
+ searchPath = searchPath.Split(',')[0];
+ }
var pg_ = _isSysV8R3 == true ? "sys_" : "pg_";
- var public_ = _isSysV8R3 == true ? "PUBLIC" : "public";
+ var public_ = _isSysV8R3 == true ? searchPath.ToUpper() : searchPath;
var sb = new StringBuilder();
var seqcols = new List>(); //序列
@@ -182,11 +191,11 @@ namespace FreeSql.KingbaseES
var sbalter = new StringBuilder();
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
- if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format($" select 1 from {pg_}tables a inner join {pg_}namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{{0}}.{{1}}'", tbname)) == null)
+ if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format($" select 1 from {pg_}tables a inner join {pg_}namespace b on b.nspname = a.schemaname where b.nspname ='{{0}}' && a.tablename = '{{1}}'", tbname)) == null)//原判断V9版本存在问题
{ //表不存在
if (tboldname != null)
{
- if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format($" select 1 from {pg_}tables a inner join {pg_}namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{{0}}.{{1}}'", tboldname)) == null)
+ if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format($" select 1 from {pg_}tables a inner join {pg_}namespace b on b.nspname = a.schemaname where b.nspname ='{{0}}' && a.tablename = '{{1}}'", tboldname)) == null)
//旧表不存在
tboldname = null;
}
@@ -388,7 +397,7 @@ from {pg_}class a
inner join {pg_}namespace b on b.oid = a.relnamespace
left join {pg_}description d on d.objoid = a.oid and objsubid = 0
where upper(b.nspname) not in ('SYS_CATALOG', 'INFORMATION_SCHEMA', 'TOPOLOGY', 'SYSAUDIT', 'SYSLOGICAL', 'SYS_TEMP_1', 'SYS_TOAST', 'SYS_TOAST_TEMP_1', 'XLOG_RECORD_READ') and a.relkind in ('r') and b.nspname = {{0}} and a.relname = {{1}}
-and upper(b.nspname || '.' || a.relname) not in ('PUBLIC.GEOGRAPHY_COLUMNS','PUBLIC.GEOMETRY_COLUMNS','PUBLIC.RASTER_COLUMNS','PUBLIC.RASTER_OVERVIEWS')", tbname[0], tbname[1])));
+and upper(text(b.nspname || '.' || a.relname)) not in ('PUBLIC.GEOGRAPHY_COLUMNS','PUBLIC.GEOMETRY_COLUMNS','PUBLIC.RASTER_COLUMNS','PUBLIC.RASTER_OVERVIEWS')", tbname[0], tbname[1])));//解决报错 function upper(boolean) is not unique 错误的问题
if (dbcomment != (tb.Comment ?? ""))
sbalter.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n");
diff --git a/Providers/FreeSql.Provider.KingbaseES/lib/Kdbndp.dll b/Providers/FreeSql.Provider.KingbaseES/lib/Kdbndp.dll
index b2884ab9a..a60fab345 100644
Binary files a/Providers/FreeSql.Provider.KingbaseES/lib/Kdbndp.dll and b/Providers/FreeSql.Provider.KingbaseES/lib/Kdbndp.dll differ
diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs
index e0a60d74e..340db0ce1 100644
--- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs
+++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs
@@ -56,7 +56,7 @@ namespace FreeSql.Odbc.PostgreSQL
if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
else
{
- var ocdu = new OdbcPostgreSQLOnConflictDoUpdate(insert.InsertIdentity());
+ var ocdu = new OdbcPostgreSQLOnConflictDoUpdate(_tempPrimarys?.Length > 0 ? insert : insert.InsertIdentity());
ocdu._tempPrimarys = _tempPrimarys;
var cols = _table.Columns.Values.Where(a => _updateSetDict.ContainsKey(a.Attribute.Name) ||
_tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
diff --git a/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs b/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs
index da45a72ca..52e6bd4c4 100644
--- a/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs
+++ b/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs
@@ -58,7 +58,7 @@ namespace FreeSql.PostgreSQL.Curd
if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
else
{
- var ocdu = new OnConflictDoUpdate(insert.InsertIdentity());
+ var ocdu = new OnConflictDoUpdate(_tempPrimarys?.Length > 0 ? insert : insert.InsertIdentity());
ocdu._tempPrimarys = _tempPrimarys;
var cols = _table.Columns.Values.Where(a => _updateSetDict.ContainsKey(a.Attribute.Name) ||
_tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _updateIgnore.ContainsKey(a.Attribute.Name) == false);