Skip to content

Commit

Permalink
- 增加 IsVersion string 字符串乐观锁;#1178
Browse files Browse the repository at this point in the history
  • Loading branch information
2881099 committed Jul 4, 2022
1 parent a219b39 commit 84cfa65
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 12 deletions.
6 changes: 4 additions & 2 deletions Examples/base_entity/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,9 @@ static void Main(string[] args)
//.UseConnectionString(FreeSql.DataType.Firebird, @"database=localhost:D:\fbdata\EXAMPLES.fdb;user=sysdba;password=123456;max pool size=5")


.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")
//.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")

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

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

var sqlToYear = fsql.Select<User1>().ToSql(a => a.CreateTime.Year);

TestExp(fsql);

fsql.CodeFirst.GetTableByEntity(typeof(TestComment01));
Expand Down
125 changes: 125 additions & 0 deletions FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerCodeFirstTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,131 @@ namespace FreeSql.Tests.SqlServer
{
public class SqlServerCodeFirstTest
{
[Fact]
public void VersionInt()
{
var fsql = g.sqlserver;
fsql.Delete<VersionInt01>().Where("1=1").ExecuteAffrows();
var item = new VersionInt01 { name = "name01" };
fsql.Insert(item).ExecuteAffrows();

item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.Equal(0, item.version);

item.name = "name02";
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}') AND [version] = 0", fsql.Update<VersionInt01>().SetSource(item).ToSql());
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name02', [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}') AND [version] = 0", fsql.Update<VersionInt01>().SetSource(item).NoneParameter().ToSql());
Assert.Equal(1, fsql.Update<VersionInt01>().SetSource(item).ExecuteAffrows());
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.Equal("name02", item.name);
Assert.Equal(1, item.version);

item.name = "name03";
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}') AND [version] = 1", fsql.Update<VersionInt01>().SetSource(item).ToSql());
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name03', [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}') AND [version] = 1", fsql.Update<VersionInt01>().SetSource(item).NoneParameter().ToSql());
Assert.Equal(1, fsql.Update<VersionInt01>().SetSource(item).ExecuteAffrows());
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.Equal("name03", item.name);
Assert.Equal(2, item.version);

Assert.Equal($@"UPDATE [VersionInt01] SET [name] = @p_0, [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}')", fsql.Update<VersionInt01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ToSql());
Assert.Equal($@"UPDATE [VersionInt01] SET [name] = N'name04', [version] = isnull([version], 0) + 1
WHERE ([id] = '{item.id}')", fsql.Update<VersionInt01>().NoneParameter().Set(a => a.name, "name04").Where(a => a.id == item.id).ToSql());
Assert.Equal(1, fsql.Update<VersionInt01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
item = fsql.Select<VersionInt01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.Equal("name04", item.name);
Assert.Equal(3, item.version);
}
class VersionInt01
{
public Guid id { get; set; }
public string name { get; set; }
[Column(IsVersion = true)]
public int version { get; set; }
}

[Fact]
public void VersionBytes()
{
bool LocalEqualsVersion(byte[] v1, byte[] v2)
{
if (v1.Length == v2.Length)
{
for (var y = 0; y < v2.Length; y++)
if (v1[y] != v2[y]) return false;
return true;
}
return false;
}

var fsql = g.sqlserver;
fsql.Delete<VersionBytes01>().Where("1=1").ExecuteAffrows();
var item = new VersionBytes01 { name = "name01" };
fsql.Insert(item).ExecuteAffrows();
var itemVersion = item.version;
Assert.NotNull(itemVersion);

item = fsql.Select<VersionBytes01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.True(LocalEqualsVersion(itemVersion, item.version));

item.name = "name02";
var sql = fsql.Update<VersionBytes01>().SetSource(item).ToSql();
Assert.Equal(1, fsql.Update<VersionBytes01>().SetSource(item).ExecuteAffrows());

item.name = "name03";
Assert.Equal(1, fsql.Update<VersionBytes01>().SetSource(item).ExecuteAffrows());

Assert.Equal(1, fsql.Update<VersionBytes01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
}
class VersionBytes01
{
public Guid id { get; set; }
public string name { get; set; }
[Column(IsVersion = true)]
public byte[] version { get; set; }
}

[Fact]
public void VersionString()
{
var fsql = g.sqlserver;
fsql.Delete<VersionString01>().Where("1=1").ExecuteAffrows();
var item = new VersionString01 { name = "name01" };
fsql.Insert(item).ExecuteAffrows();
var itemVersion = item.version;
Assert.NotNull(itemVersion);

item = fsql.Select<VersionString01>().Where(a => a.id == item.id).First();
Assert.NotNull(item);
Assert.Equal(itemVersion, item.version);

item.name = "name02";
var sql = fsql.Update<VersionString01>().SetSource(item).ToSql();
Assert.Equal(1, fsql.Update<VersionString01>().SetSource(item).ExecuteAffrows());

item.name = "name03";
Assert.Equal(1, fsql.Update<VersionString01>().SetSource(item).ExecuteAffrows());

Assert.Equal(1, fsql.Update<VersionString01>().Set(a => a.name, "name04").Where(a => a.id == item.id).ExecuteAffrows());
}
class VersionString01
{
public Guid id { get; set; }
public string name { get; set; }
[Column(IsVersion = true)]
public string version { get; set; }
}

[Fact]
public void Test_0String()
{
Expand Down
3 changes: 2 additions & 1 deletion FreeSql.Tests/FreeSql.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ public void Test1()
.ToList(a => new
{
a.Key,
sss = a.Sum(a.Value.Item1.OptionsEntity04)
sss = a.Sum(a.Value.Item1.OptionsEntity04),
xxx = SqlExt.DistinctCount(a.Value.Item2.Title)
});


Expand Down
2 changes: 1 addition & 1 deletion FreeSql/FreeSql.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions FreeSql/Internal/CommonProvider/InsertProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,22 @@ public static void AuditDataValue(object sender, T1 data, IFreeSql orm, TableInf
col.SetValue(data, val = FreeUtil.NewMongodbId());
}
}
if (col.Attribute.IsVersion)
{
if (col.Attribute.MapType == typeof(byte[]))
{
if (val == null || (val is byte[] bytes && bytes.Length == 0))
col.SetValue(data, val = Utils.GuidToBytes(Guid.NewGuid()));
}
else if (col.Attribute.MapType == typeof(string))
{
var verval = col.GetDbValue(data) as string;
if (string.IsNullOrWhiteSpace(verval))
col.SetValue(data, val = Guid.NewGuid().ToString());
}
}
if (val == null && col.Attribute.MapType == typeof(string) && col.Attribute.IsNullable == false)
col.SetValue(data, val = "");
if (col.Attribute.MapType == typeof(byte[]) && (val == null || (val is byte[] bytes && bytes.Length == 0)) && col.Attribute.IsVersion)
col.SetValue(data, val = Utils.GuidToBytes(Guid.NewGuid()));
}
}

Expand Down
11 changes: 9 additions & 2 deletions FreeSql/Internal/CommonProvider/UpdateProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public abstract partial class UpdateProvider
public DbConnection _connection;
public int _commandTimeout = 0;
public Action<StringBuilder> _interceptSql;
public byte[] _updateVersionValue;
public object _updateVersionValue;
}

public abstract partial class UpdateProvider<T1> : UpdateProvider, IUpdate<T1>
Expand Down Expand Up @@ -139,7 +139,9 @@ protected void ValidateVersionAndThrow(int affrows, string sql, DbParameter[] db
throw new DbUpdateVersionException(CoreStrings.DbUpdateVersionException_RowLevelOptimisticLock(_source.Count, affrows), _table, sql, dbParms, affrows, _source.Select(a => (object)a));
foreach (var d in _source)
{
if (_versionColumn.Attribute.MapType == typeof(byte[]))
if (_versionColumn.Attribute.MapType == typeof(byte[]))
_orm.SetEntityValueWithPropertyName(_table.Type, d, _versionColumn.CsName, _updateVersionValue);
else if (_versionColumn.Attribute.MapType == typeof(string))
_orm.SetEntityValueWithPropertyName(_table.Type, d, _versionColumn.CsName, _updateVersionValue);
else
_orm.SetEntityIncrByWithPropertyName(_table.Type, d, _versionColumn.CsName, 1);
Expand Down Expand Up @@ -1005,6 +1007,11 @@ public virtual void ToSqlExtension110(StringBuilder sb, bool isAsTableSplited)
_updateVersionValue = Utils.GuidToBytes(Guid.NewGuid());
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue));
}
else if (_versionColumn.Attribute.MapType == typeof(string))
{
_updateVersionValue = Guid.NewGuid().ToString();
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue));
}
else
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.IsNull(vcname, 0)).Append(" + 1");
}
Expand Down
4 changes: 3 additions & 1 deletion FreeSql/Internal/UtilsExpressionTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common)
break;
}
}
if (colattr.MapType == typeof(string) && colattr.IsVersion == true) colattr.StringLength = 40;
if (colattr.MapType == typeof(byte[]) && colattr.IsVersion == true) colattr.StringLength = 16;
if (colattr.MapType == typeof(byte[]) && colattr.StringLength != 0)
{
Expand Down Expand Up @@ -395,7 +396,8 @@ internal static TableInfo GetTableByEntity(Type entity, CommonUtils common)
trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault();
if (trytb.VersionColumn != null)
{
if (trytb.VersionColumn.Attribute.MapType.IsNullableType() || trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && trytb.VersionColumn.Attribute.MapType != typeof(byte[]))
if (trytb.VersionColumn.Attribute.MapType.IsNullableType() ||
trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && !new[] { typeof(byte[]), typeof(string) }.Contains(trytb.VersionColumn.Attribute.MapType))
throw new Exception(CoreStrings.Properties_AsRowLock_Must_Numeric_Byte(trytb.VersionColumn.CsName));
}
tbattr?.ParseAsTable(trytb);
Expand Down
2 changes: 1 addition & 1 deletion FreeSql/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion FreeSql/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@
<value>FreeSql: The {policyName} status is unavailable and cannot be used until the background checker is restored. {UnavailableExceptionMessage}</value>
</data>
<data name="Properties_AsRowLock_Must_Numeric_Byte" xml:space="preserve">
<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>
<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>
</data>
<data name="Properties_Cannot_Null" xml:space="preserve">
<value>FreeSql: Properrties parameter cannot be empty</value>
Expand Down
2 changes: 1 addition & 1 deletion FreeSql/Properties/CoreStrings.zh-Hans.resx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@
<value>【{policyName}】状态不可用,等待后台检查程序恢复方可使用。{UnavailableExceptionMessage}</value>
</data>
<data name="Properties_AsRowLock_Must_Numeric_Byte" xml:space="preserve">
<value>属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[],并且不可为 Nullable</value>
<value>属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[] 或者 string,并且不可为 Nullable</value>
</data>
<data name="Properties_Cannot_Null" xml:space="preserve">
<value>properties 参数不能为空</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ public override void ToSqlExtension110(StringBuilder sb, bool isAsTableSplited)
_updateVersionValue = Utils.GuidToBytes(Guid.NewGuid());
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _table.VersionColumn, _table.VersionColumn.Attribute.MapType, _updateVersionValue));
}
else if (_versionColumn.Attribute.MapType == typeof(string))
{
_updateVersionValue = Guid.NewGuid().ToString();
sb.Append(", ").Append(vcname).Append(" = ").Append(_noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue) :
_commonUtils.QuoteWriteParamterAdapter(_versionColumn.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
if (_noneParameter == false)
_commonUtils.AppendParamter(_paramsSource, null, _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue);
}
else
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.IsNull(vcname, 0)).Append(" + 1");
}
Expand Down

0 comments on commit 84cfa65

Please sign in to comment.