- 增加 IsVersion string 字符串乐观锁;#1178

This commit is contained in:
2881099 2022-07-04 17:15:30 +08:00
parent a219b39e5f
commit 84cfa65281
11 changed files with 169 additions and 12 deletions

View File

@ -311,9 +311,9 @@ namespace base_entity
//.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")
@ -344,6 +344,8 @@ namespace base_entity
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
#endregion
var sqlToYear = fsql.Select<User1>().ToSql(a => a.CreateTime.Year);
TestExp(fsql);
fsql.CodeFirst.GetTableByEntity(typeof(TestComment01));

View File

@ -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()
{

View File

@ -734,7 +734,8 @@ namespace FreeSql.Tests
.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)
});

View File

@ -5236,7 +5236,7 @@
</member>
<member name="M:FreeSql.CoreStrings.Properties_AsRowLock_Must_Numeric_Byte(System.Object)">
<summary>
属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[],并且不可为 Nullable
属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[] 或者 string,并且不可为 Nullable
</summary>
</member>
<member name="P:FreeSql.CoreStrings.Properties_Cannot_Null">

View File

@ -188,10 +188,22 @@ namespace FreeSql.Internal.CommonProvider
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()));
}
}

View File

@ -37,7 +37,7 @@ namespace FreeSql.Internal.CommonProvider
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>
@ -141,6 +141,8 @@ namespace FreeSql.Internal.CommonProvider
{
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);
}
@ -1005,6 +1007,11 @@ namespace FreeSql.Internal.CommonProvider
_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");
}

View File

@ -326,6 +326,7 @@ namespace FreeSql.Internal
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)
{
@ -395,7 +396,8 @@ namespace FreeSql.Internal
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);

View File

@ -827,7 +827,7 @@ namespace FreeSql
policyName, UnavailableExceptionMessage);
/// <summary>
/// 属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[],并且不可为 Nullable
/// 属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[] 或者 string,并且不可为 Nullable
/// </summary>
public static string Properties_AsRowLock_Must_Numeric_Byte(object trytbVersionColumnCsName)
=> string.Format(

View File

@ -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>

View File

@ -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>

View File

@ -206,6 +206,14 @@ namespace FreeSql.ClickHouse.Curd
_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");
}