Skip to content

Commit 45b785f

Browse files
2881028810
authored andcommitted
增加 Column.Unique 唯一键 #42
1 parent 5bb90a9 commit 45b785f

File tree

18 files changed

+300
-46
lines changed

18 files changed

+300
-46
lines changed

FreeSql.Tests/MySql/MySqlCodeFirstTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@
99
namespace FreeSql.Tests.MySql {
1010
public class MySqlCodeFirstTest {
1111

12+
[Fact]
13+
public void AddUniques() {
14+
var sql = g.mysql.CodeFirst.GetComparisonDDLStatements<AddUniquesInfo>();
15+
g.mysql.CodeFirst.SyncStructure<AddUniquesInfo>();
16+
}
17+
[Table(Name = "AddUniquesInfo", OldName = "AddUniquesInfo2")]
18+
class AddUniquesInfo {
19+
public Guid id { get; set; }
20+
[Column(Unique = "uk_phone")]
21+
public string phone { get; set; }
22+
23+
[Column(Unique = "uk_group_index")]
24+
public string group { get; set; }
25+
[Column(Unique = "uk_group_index11")]
26+
public int index { get; set; }
27+
[Column(Unique = "uk_group_index222")]
28+
public string index22 { get; set; }
29+
}
30+
1231
[Fact]
1332
public void AddField() {
1433
var sql = g.mysql.CodeFirst.GetComparisonDDLStatements<TopicAddField>();

FreeSql.Tests/Oracle/OracleCodeFirstTest.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@
99
namespace FreeSql.Tests.Oracle {
1010
public class OracleCodeFirstTest {
1111

12+
[Fact]
13+
public void AddUniques() {
14+
var sql = g.oracle.CodeFirst.GetComparisonDDLStatements<AddUniquesInfo>();
15+
g.oracle.CodeFirst.SyncStructure<AddUniquesInfo>();
16+
}
17+
[Table(Name = "AddUniquesInfo", OldName = "AddUniquesInfo2")]
18+
class AddUniquesInfo {
19+
public Guid id { get; set; }
20+
[Column(Unique = "uk_phone")]
21+
public string phone { get; set; }
22+
23+
[Column(Unique = "uk_group_index")]
24+
public string group { get; set; }
25+
[Column(Unique = "uk_group_index11")]
26+
public int index { get; set; }
27+
[Column(Unique = "uk_group_index222")]
28+
public string index22 { get; set; }
29+
}
1230
[Fact]
1331
public void AddField() {
1432
var sql = g.oracle.CodeFirst.GetComparisonDDLStatements<TopicAddField>();

FreeSql.Tests/PostgreSQL/PostgreSQLCodeFirstTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@
1616
namespace FreeSql.Tests.PostgreSQL {
1717
public class PostgreSQLCodeFirstTest {
1818

19+
[Fact]
20+
public void AddUniques() {
21+
var sql = g.pgsql.CodeFirst.GetComparisonDDLStatements<AddUniquesInfo>();
22+
g.pgsql.CodeFirst.SyncStructure<AddUniquesInfo>();
23+
}
24+
[Table(Name = "AddUniquesInfo", OldName = "AddUniquesInfo2")]
25+
class AddUniquesInfo {
26+
public Guid id { get; set; }
27+
[Column(Unique = "uk_phone")]
28+
public string phone { get; set; }
29+
30+
[Column(Unique = "uk_group_index")]
31+
public string group { get; set; }
32+
[Column(Unique = "uk_group_index11")]
33+
public int index { get; set; }
34+
[Column(Unique = "uk_group_index222")]
35+
public string index22 { get; set; }
36+
}
37+
1938
[Fact]
2039
public void AddField() {
2140
var sql = g.pgsql.CodeFirst.GetComparisonDDLStatements<TopicAddField>();

FreeSql.Tests/SqlServer/SqlServerCodeFirstTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@ public SqlServerCodeFirstTest(SqlServerFixture sqlserverFixture)
1919
_sqlserverFixture = sqlserverFixture;
2020
}
2121

22+
[Fact]
23+
public void AddUniques() {
24+
var sql = _sqlserverFixture.SqlServer.CodeFirst.GetComparisonDDLStatements<AddUniquesInfo>();
25+
_sqlserverFixture.SqlServer.CodeFirst.SyncStructure<AddUniquesInfo>();
26+
}
27+
[Table(Name = "AddUniquesInfo", OldName = "AddUniquesInfo2")]
28+
class AddUniquesInfo {
29+
public Guid id { get; set; }
30+
[Column(Unique = "uk_phone")]
31+
public string phone { get; set; }
32+
33+
[Column(Unique = "uk_group_index")]
34+
public string group { get; set; }
35+
[Column(Unique = "uk_group_index11")]
36+
public int index { get; set; }
37+
[Column(Unique = "uk_group_index222")]
38+
public string index22 { get; set; }
39+
}
40+
2241
[Fact]
2342
public void AddField() {
2443
var sql = _sqlserverFixture.SqlServer.CodeFirst.GetComparisonDDLStatements<TopicAddField>();

FreeSql.Tests/SqlServer/SqlServerDbFirstTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void GetDatabases() {
2424
[Fact]
2525
public void GetTablesByDatabase() {
2626

27-
var t2 = _sqlserverFixture.SqlServer.DbFirst.GetTablesByDatabase(_sqlserverFixture.SqlServer.DbFirst.GetDatabases()[0]);
27+
var t2 = _sqlserverFixture.SqlServer.DbFirst.GetTablesByDatabase();
2828

2929
}
3030
}

FreeSql.Tests/Sqlite/SqliteCodeFirstTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,25 @@ namespace FreeSql.Tests.Sqlite {
1010
public class SqliteCodeFirstTest {
1111

1212

13+
[Fact]
14+
public void AddUniques() {
15+
var sql = g.sqlite.CodeFirst.GetComparisonDDLStatements<AddUniquesInfo>();
16+
g.sqlite.CodeFirst.SyncStructure<AddUniquesInfo>();
17+
}
18+
[Table(Name = "AddUniquesInfo2", OldName = "AddUniquesInfo")]
19+
class AddUniquesInfo {
20+
public Guid id { get; set; }
21+
[Column(Unique = "uk_phone")]
22+
public string phone { get; set; }
23+
24+
[Column(Unique = "uk_group_index")]
25+
public string group { get; set; }
26+
[Column(Unique = "uk_group_index111")]
27+
public int index { get; set; }
28+
[Column(Unique = "uk_group_index222")]
29+
public string index22 { get; set; }
30+
}
31+
1332
public class Topic {
1433
public Guid Id { get; set; }
1534
public string Title { get; set; }

FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Text.RegularExpressions;
34

45
namespace FreeSql.Internal.CommonProvider {
@@ -10,10 +11,12 @@ public string Addslashes(string filter, params object[] parms) {
1011
var nparms = new object[parms.Length];
1112
for (int a = 0; a < parms.Length; a++) {
1213
if (parms[a] == null)
13-
filter = Regex.Replace(filter, @"\s*(=|IN)\s*\{" + a + @"\}", " IS {" + a + "}", RegexOptions.IgnoreCase);
14+
filter = _dicAddslashesReplaceIsNull.GetOrAdd(a, b => new Regex(@"\s*(=|IN)\s*\{" + b + @"\}", RegexOptions.IgnoreCase | RegexOptions.Compiled))
15+
.Replace(filter, $" IS {{{a}}}");
1416
nparms[a] = AddslashesProcessParam(parms[a], null);
1517
}
1618
try { string ret = string.Format(filter, nparms); return ret; } catch { return filter; }
1719
}
20+
static ConcurrentDictionary<int, Regex> _dicAddslashesReplaceIsNull = new ConcurrentDictionary<int, Regex>();
1821
}
1922
}

FreeSql/Internal/CommonUtils.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ internal ColumnAttribute GetEntityColumnAttribute(Type type, PropertyInfo proto)
101101
if (trycol._IsNullable != null) attr._IsNullable = trycol.IsNullable;
102102
if (trycol._IsIgnore != null) attr._IsIgnore = trycol.IsIgnore;
103103
if (trycol._IsVersion != null) attr._IsVersion = trycol.IsVersion;
104+
if (!string.IsNullOrEmpty(trycol.Unique)) attr.Unique = trycol.Unique;
104105
if (trycol.MapType != null) attr.MapType = trycol.MapType;
105106
if (trycol.DbDefautValue != null) attr.DbDefautValue = trycol.DbDefautValue;
106107
}
@@ -116,6 +117,7 @@ internal ColumnAttribute GetEntityColumnAttribute(Type type, PropertyInfo proto)
116117
if (tryattr._IsNullable != null) attr._IsNullable = tryattr.IsNullable;
117118
if (tryattr._IsIgnore != null) attr._IsIgnore = tryattr.IsIgnore;
118119
if (tryattr._IsVersion != null) attr._IsVersion = tryattr.IsVersion;
120+
if (!string.IsNullOrEmpty(tryattr.Unique)) attr.Unique = tryattr.Unique;
119121
if (tryattr.MapType != null) attr.MapType = tryattr.MapType;
120122
if (tryattr.DbDefautValue != null) attr.DbDefautValue = tryattr.DbDefautValue;
121123
}
@@ -128,6 +130,7 @@ internal ColumnAttribute GetEntityColumnAttribute(Type type, PropertyInfo proto)
128130
if (attr._IsNullable != null) ret = attr;
129131
if (attr._IsIgnore != null) ret = attr;
130132
if (attr._IsVersion != null) ret = attr;
133+
if (!string.IsNullOrEmpty(attr.Unique)) ret = attr;
131134
if (attr.MapType != null) ret = attr;
132135
if (attr.DbDefautValue != null) ret = attr;
133136
if (ret != null && ret.MapType == null) ret.MapType = proto.PropertyType;

FreeSql/Internal/UtilsExpressionTree.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ public class Utils {
1818

1919
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>> _cacheGetTableByEntity = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>>();
2020
internal static void RemoveTableByEntity(Type entity, CommonUtils common) {
21-
if (entity.FullName.StartsWith("<>f__AnonymousType")) return;
21+
if (entity.FullName.StartsWith("<>f__AnonymousType") ||
22+
entity.IsValueType ||
23+
entity.IsNullableType() ||
24+
entity.NullableTypeOrThis() == typeof(BigInteger)
25+
) return;
2226
var tbc = _cacheGetTableByEntity.GetOrAdd(common._orm.Ado.DataType, k1 => new ConcurrentDictionary<Type, TableInfo>()); //区分数据库类型缓存
2327
if (tbc.TryRemove(entity, out var trytb) && trytb?.TypeLazy != null) tbc.TryRemove(trytb.TypeLazy, out var trylz);
2428
}
@@ -75,6 +79,7 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
7579
IsNullable = tp.Value.isnullable ?? true,
7680
IsPrimary = false,
7781
IsIgnore = false,
82+
Unique = null,
7883
MapType = p.PropertyType
7984
};
8085
if (colattr._IsNullable == null) colattr._IsNullable = tp?.isnullable;
@@ -84,8 +89,14 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
8489
if (tp != null && tp.Value.isnullable == null) colattr.IsNullable = tp.Value.dbtypeFull.Contains("NOT NULL") == false;
8590
if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false;
8691
if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name;
87-
if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower();
88-
if (common.CodeFirst.IsSyncStructureToUpper) colattr.Name = colattr.Name.ToUpper();
92+
if (common.CodeFirst.IsSyncStructureToLower) {
93+
colattr.Name = colattr.Name.ToLower();
94+
if (!string.IsNullOrEmpty(colattr.Unique)) colattr.Unique = colattr.Unique.ToLower();
95+
}
96+
if (common.CodeFirst.IsSyncStructureToUpper) {
97+
colattr.Name = colattr.Name.ToUpper();
98+
if (!string.IsNullOrEmpty(colattr.Unique)) colattr.Unique = colattr.Unique.ToUpper();
99+
}
89100

90101
if ((colattr.IsNullable != true || colattr.IsIdentity == true || colattr.IsPrimary == true) && colattr.DbType.Contains("NOT NULL") == false) {
91102
colattr.IsNullable = false;
@@ -177,8 +188,8 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
177188
} catch { }
178189
trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray();
179190
}
180-
trytb.Uniques = trytb.Columns.Values.Where(a => !string.IsNullOrEmpty(a.Attribute.Unique))
181-
.ToDictionary(a => a.Attribute.Unique, a => trytb.Columns.Values.Where(b => b.Attribute.Unique == a.Attribute.Unique).ToList());
191+
trytb.Uniques = trytb.Columns.Values.Where(a => !string.IsNullOrEmpty(a.Attribute.Unique)).Select(a => a.Attribute.Unique).Distinct()
192+
.ToDictionary(a => a, a => trytb.Columns.Values.Where(b => b.Attribute.Unique == a).ToList());
182193
tbc.AddOrUpdate(entity, trytb, (oldkey, oldval) => trytb);
183194

184195
#region 查找导航属性的关系、virtual 属性延时加载,动态产生新的重写类

FreeSql/MySql/MySqlCodeFirst.cs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,17 @@ public string GetComparisonDDLStatements(params Type[] entityTypes) {
133133
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
134134
sb.Append(",");
135135
}
136-
if (tb.Primarys.Any() == false)
137-
sb.Remove(sb.Length - 1, 1);
138-
else {
136+
if (tb.Primarys.Any()) {
139137
sb.Append(" \r\n PRIMARY KEY (");
140138
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
141-
sb.Remove(sb.Length - 2, 2).Append(")");
139+
sb.Remove(sb.Length - 2, 2).Append("),");
142140
}
141+
foreach (var uk in tb.Uniques) {
142+
sb.Append(" \r\n UNIQUE KEY ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append("(");
143+
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
144+
sb.Remove(sb.Length - 2, 2).Append("),");
145+
}
146+
sb.Remove(sb.Length - 1, 1);
143147
sb.Append("\r\n) Engine=InnoDB CHARACTER SET utf8;\r\n");
144148
continue;
145149
}
@@ -176,7 +180,7 @@ from information_schema.columns a
176180
}, StringComparer.CurrentCultureIgnoreCase);
177181

178182
if (istmpatler == false) {
179-
var existsPrimary = ExecuteScalar(tbname[0], "select 1 from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where table_schema={0} and table_name={1} limit 1".FormatMySql(tbname));
183+
var existsPrimary = ExecuteScalar(tbname[0], "select 1 from information_schema.key_column_usage where table_schema={0} and table_name={1} and constraint_name = 'PRIMARY' limit 1".FormatMySql(tbname));
180184
foreach (var tbcol in tb.Columns.Values) {
181185
var isIdentityChanged = tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1;
182186
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
@@ -202,6 +206,23 @@ from information_schema.columns a
202206
if (isIdentityChanged) sbalter.Append(" AUTO_INCREMENT").Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
203207
sbalter.Append(";\r\n");
204208
}
209+
var dsuksql = @"
210+
select
211+
a.column_name,
212+
a.constraint_name 'index_id'
213+
from information_schema.key_column_usage a
214+
where a.constraint_schema IN ({0}) and a.table_name IN ({1})".FormatMySql(tboldname ?? tbname);
215+
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
216+
foreach (var uk in tb.Uniques) {
217+
if (uk.Key == "PRIMARY" || string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
218+
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
219+
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
220+
if (dsukfind1.Any()) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP INDEX ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(";\r\n");
221+
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
222+
foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
223+
sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
224+
}
225+
}
205226
}
206227
if (istmpatler == false) {
207228
sb.Append(sbalter);
@@ -219,13 +240,17 @@ from information_schema.columns a
219240
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
220241
sb.Append(",");
221242
}
222-
if (tb.Primarys.Any() == false)
223-
sb.Remove(sb.Length - 1, 1);
224-
else {
243+
if (tb.Primarys.Any()) {
225244
sb.Append(" \r\n PRIMARY KEY (");
226245
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
227-
sb.Remove(sb.Length - 2, 2).Append(")");
246+
sb.Remove(sb.Length - 2, 2).Append("),");
247+
}
248+
foreach (var uk in tb.Uniques) {
249+
sb.Append(" \r\n UNIQUE KEY ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append("(");
250+
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
251+
sb.Remove(sb.Length - 2, 2).Append("),");
228252
}
253+
sb.Remove(sb.Length - 1, 1);
229254
sb.Append("\r\n) Engine=InnoDB CHARACTER SET utf8;\r\n");
230255
sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
231256
foreach (var tbcol in tb.Columns.Values)

0 commit comments

Comments
 (0)