Skip to content

Commit 84cfa65

Browse files
committed
- 增加 IsVersion string 字符串乐观锁;#1178
1 parent a219b39 commit 84cfa65

File tree

11 files changed

+169
-12
lines changed

11 files changed

+169
-12
lines changed

Examples/base_entity/Program.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,9 @@ static void Main(string[] args)
311311
//.UseConnectionString(FreeSql.DataType.Firebird, @"database=localhost:D:\fbdata\EXAMPLES.fdb;user=sysdba;password=123456;max pool size=5")
312312

313313

314-
.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=2")
314+
//.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=2")
315315

316-
.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3;TrustServerCertificate=true")
316+
//.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3;TrustServerCertificate=true")
317317

318318
//.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=2")
319319
//.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=toc;Pooling=true;Maximum Pool Size=2")
@@ -344,6 +344,8 @@ static void Main(string[] args)
344344
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
345345
#endregion
346346

347+
var sqlToYear = fsql.Select<User1>().ToSql(a => a.CreateTime.Year);
348+
347349
TestExp(fsql);
348350

349351
fsql.CodeFirst.GetTableByEntity(typeof(TestComment01));

FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerCodeFirstTest.cs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,131 @@ namespace FreeSql.Tests.SqlServer
1212
{
1313
public class SqlServerCodeFirstTest
1414
{
15+
[Fact]
16+
public void VersionInt()
17+
{
18+
var fsql = g.sqlserver;
19+
fsql.Delete<VersionInt01>().Where("1=1").ExecuteAffrows();
20+
var item = new VersionInt01 { name = "name01" };
21+
fsql.Insert(item).ExecuteAffrows();
22+
23+
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
24+
Assert.NotNull(item);
25+
Assert.Equal(0, item.version);
26+
27+
item.name = "name02";
28+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
29+
WHERE ([id] = '{item.id}') AND [version] = 0", fsql.Update<VersionInt01>().SetSource(item).ToSql());
30+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name02', [version] = isnull([version], 0) + 1
31+
WHERE ([id] = '{item.id}') AND [version] = 0", fsql.Update<VersionInt01>().SetSource(item).NoneParameter().ToSql());
32+
Assert.Equal(1, fsql.Update<VersionInt01>().SetSource(item).ExecuteAffrows());
33+
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
34+
Assert.NotNull(item);
35+
Assert.Equal("name02", item.name);
36+
Assert.Equal(1, item.version);
37+
38+
item.name = "name03";
39+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
40+
WHERE ([id] = '{item.id}') AND [version] = 1", fsql.Update<VersionInt01>().SetSource(item).ToSql());
41+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name03', [version] = isnull([version], 0) + 1
42+
WHERE ([id] = '{item.id}') AND [version] = 1", fsql.Update<VersionInt01>().SetSource(item).NoneParameter().ToSql());
43+
Assert.Equal(1, fsql.Update<VersionInt01>().SetSource(item).ExecuteAffrows());
44+
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
45+
Assert.NotNull(item);
46+
Assert.Equal("name03", item.name);
47+
Assert.Equal(2, item.version);
48+
49+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
50+
WHERE ([id] = '{item.id}')", fsql.Update<VersionInt01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ToSql());
51+
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name04', [version] = isnull([version], 0) + 1
52+
WHERE ([id] = '{item.id}')", fsql.Update<VersionInt01>().NoneParameter().Set(a => a.name, "name04").Where(a => a.id == item.id).ToSql());
53+
Assert.Equal(1, fsql.Update<VersionInt01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
54+
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
55+
Assert.NotNull(item);
56+
Assert.Equal("name04", item.name);
57+
Assert.Equal(3, item.version);
58+
}
59+
class VersionInt01
60+
{
61+
public Guid id { get; set; }
62+
public string name { get; set; }
63+
[Column(IsVersion = true)]
64+
public int version { get; set; }
65+
}
66+
67+
[Fact]
68+
public void VersionBytes()
69+
{
70+
bool LocalEqualsVersion(byte[] v1, byte[] v2)
71+
{
72+
if (v1.Length == v2.Length)
73+
{
74+
for (var y = 0; y < v2.Length; y++)
75+
if (v1[y] != v2[y]) return false;
76+
return true;
77+
}
78+
return false;
79+
}
80+
81+
var fsql = g.sqlserver;
82+
fsql.Delete<VersionBytes01>().Where("1=1").ExecuteAffrows();
83+
var item = new VersionBytes01 { name = "name01" };
84+
fsql.Insert(item).ExecuteAffrows();
85+
var itemVersion = item.version;
86+
Assert.NotNull(itemVersion);
87+
88+
item = fsql.Select<VersionBytes01>().Where(a => a.id == item.id).First();
89+
Assert.NotNull(item);
90+
Assert.True(LocalEqualsVersion(itemVersion, item.version));
91+
92+
item.name = "name02";
93+
var sql = fsql.Update<VersionBytes01>().SetSource(item).ToSql();
94+
Assert.Equal(1, fsql.Update<VersionBytes01>().SetSource(item).ExecuteAffrows());
95+
96+
item.name = "name03";
97+
Assert.Equal(1, fsql.Update<VersionBytes01>().SetSource(item).ExecuteAffrows());
98+
99+
Assert.Equal(1, fsql.Update<VersionBytes01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
100+
}
101+
class VersionBytes01
102+
{
103+
public Guid id { get; set; }
104+
public string name { get; set; }
105+
[Column(IsVersion = true)]
106+
public byte[] version { get; set; }
107+
}
108+
109+
[Fact]
110+
public void VersionString()
111+
{
112+
var fsql = g.sqlserver;
113+
fsql.Delete<VersionString01>().Where("1=1").ExecuteAffrows();
114+
var item = new VersionString01 { name = "name01" };
115+
fsql.Insert(item).ExecuteAffrows();
116+
var itemVersion = item.version;
117+
Assert.NotNull(itemVersion);
118+
119+
item = fsql.Select<VersionString01>().Where(a => a.id == item.id).First();
120+
Assert.NotNull(item);
121+
Assert.Equal(itemVersion, item.version);
122+
123+
item.name = "name02";
124+
var sql = fsql.Update<VersionString01>().SetSource(item).ToSql();
125+
Assert.Equal(1, fsql.Update<VersionString01>().SetSource(item).ExecuteAffrows());
126+
127+
item.name = "name03";
128+
Assert.Equal(1, fsql.Update<VersionString01>().SetSource(item).ExecuteAffrows());
129+
130+
Assert.Equal(1, fsql.Update<VersionString01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
131+
}
132+
class VersionString01
133+
{
134+
public Guid id { get; set; }
135+
public string name { get; set; }
136+
[Column(IsVersion = true)]
137+
public string version { get; set; }
138+
}
139+
15140
[Fact]
16141
public void Test_0String()
17142
{

FreeSql.Tests/FreeSql.Tests/UnitTest1.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,8 @@ public void Test1()
734734
.ToList(a => new
735735
{
736736
a.Key,
737-
sss = a.Sum(a.Value.Item1.OptionsEntity04)
737+
sss = a.Sum(a.Value.Item1.OptionsEntity04),
738+
xxx = SqlExt.DistinctCount(a.Value.Item2.Title)
738739
});
739740

740741

FreeSql/FreeSql.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

FreeSql/Internal/CommonProvider/InsertProvider.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,22 @@ public static void AuditDataValue(object sender, T1 data, IFreeSql orm, TableInf
188188
col.SetValue(data, val = FreeUtil.NewMongodbId());
189189
}
190190
}
191+
if (col.Attribute.IsVersion)
192+
{
193+
if (col.Attribute.MapType == typeof(byte[]))
194+
{
195+
if (val == null || (val is byte[] bytes && bytes.Length == 0))
196+
col.SetValue(data, val = Utils.GuidToBytes(Guid.NewGuid()));
197+
}
198+
else if (col.Attribute.MapType == typeof(string))
199+
{
200+
var verval = col.GetDbValue(data) as string;
201+
if (string.IsNullOrWhiteSpace(verval))
202+
col.SetValue(data, val = Guid.NewGuid().ToString());
203+
}
204+
}
191205
if (val == null && col.Attribute.MapType == typeof(string) && col.Attribute.IsNullable == false)
192206
col.SetValue(data, val = "");
193-
if (col.Attribute.MapType == typeof(byte[]) && (val == null || (val is byte[] bytes && bytes.Length == 0)) && col.Attribute.IsVersion)
194-
col.SetValue(data, val = Utils.GuidToBytes(Guid.NewGuid()));
195207
}
196208
}
197209

FreeSql/Internal/CommonProvider/UpdateProvider.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public abstract partial class UpdateProvider
3737
public DbConnection _connection;
3838
public int _commandTimeout = 0;
3939
public Action<StringBuilder> _interceptSql;
40-
public byte[] _updateVersionValue;
40+
public object _updateVersionValue;
4141
}
4242

4343
public abstract partial class UpdateProvider<T1> : UpdateProvider, IUpdate<T1>
@@ -139,7 +139,9 @@ protected void ValidateVersionAndThrow(int affrows, string sql, DbParameter[] db
139139
throw new DbUpdateVersionException(CoreStrings.DbUpdateVersionException_RowLevelOptimisticLock(_source.Count, affrows), _table, sql, dbParms, affrows, _source.Select(a => (object)a));
140140
foreach (var d in _source)
141141
{
142-
if (_versionColumn.Attribute.MapType == typeof(byte[]))
142+
if (_versionColumn.Attribute.MapType == typeof(byte[]))
143+
_orm.SetEntityValueWithPropertyName(_table.Type, d, _versionColumn.CsName, _updateVersionValue);
144+
else if (_versionColumn.Attribute.MapType == typeof(string))
143145
_orm.SetEntityValueWithPropertyName(_table.Type, d, _versionColumn.CsName, _updateVersionValue);
144146
else
145147
_orm.SetEntityIncrByWithPropertyName(_table.Type, d, _versionColumn.CsName, 1);
@@ -1005,6 +1007,11 @@ public virtual void ToSqlExtension110(StringBuilder sb, bool isAsTableSplited)
10051007
_updateVersionValue = Utils.GuidToBytes(Guid.NewGuid());
10061008
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue));
10071009
}
1010+
else if (_versionColumn.Attribute.MapType == typeof(string))
1011+
{
1012+
_updateVersionValue = Guid.NewGuid().ToString();
1013+
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue));
1014+
}
10081015
else
10091016
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.IsNull(vcname, 0)).Append(" + 1");
10101017
}

FreeSql/Internal/UtilsExpressionTree.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common)
326326
break;
327327
}
328328
}
329+
if (colattr.MapType == typeof(string) && colattr.IsVersion == true) colattr.StringLength = 40;
329330
if (colattr.MapType == typeof(byte[]) && colattr.IsVersion == true) colattr.StringLength = 16;
330331
if (colattr.MapType == typeof(byte[]) && colattr.StringLength != 0)
331332
{
@@ -395,7 +396,8 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common)
395396
trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault();
396397
if (trytb.VersionColumn != null)
397398
{
398-
if (trytb.VersionColumn.Attribute.MapType.IsNullableType() || trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && trytb.VersionColumn.Attribute.MapType != typeof(byte[]))
399+
if (trytb.VersionColumn.Attribute.MapType.IsNullableType() ||
400+
trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && !new[] { typeof(byte[]), typeof(string) }.Contains(trytb.VersionColumn.Attribute.MapType))
399401
throw new Exception(CoreStrings.Properties_AsRowLock_Must_Numeric_Byte(trytb.VersionColumn.CsName));
400402
}
401403
tbattr?.ParseAsTable(trytb);

FreeSql/Properties/CoreStrings.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

FreeSql/Properties/CoreStrings.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@
433433
<value>FreeSql: The {policyName} status is unavailable and cannot be used until the background checker is restored. {UnavailableExceptionMessage}</value>
434434
</data>
435435
<data name="Properties_AsRowLock_Must_Numeric_Byte" xml:space="preserve">
436-
<value>FreeSql: The property {trytbVersionColumnCsName} is labeled as a row lock (optimistic lock) (IsVersion), but it must be a numeric type or byte[], and it cannot be Nullable</value>
436+
<value>FreeSql: The property {trytbVersionColumnCsName} is labeled as a row lock (optimistic lock) (IsVersion), but it must be a numeric type or byte[] or string, and it cannot be Nullable</value>
437437
</data>
438438
<data name="Properties_Cannot_Null" xml:space="preserve">
439439
<value>FreeSql: Properrties parameter cannot be empty</value>

FreeSql/Properties/CoreStrings.zh-Hans.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@
433433
<value>【{policyName}】状态不可用,等待后台检查程序恢复方可使用。{UnavailableExceptionMessage}</value>
434434
</data>
435435
<data name="Properties_AsRowLock_Must_Numeric_Byte" xml:space="preserve">
436-
<value>属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[],并且不可为 Nullable</value>
436+
<value>属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[] 或者 string,并且不可为 Nullable</value>
437437
</data>
438438
<data name="Properties_Cannot_Null" xml:space="preserve">
439439
<value>properties 参数不能为空</value>

0 commit comments

Comments
 (0)