仓储层,进行先缓存后 更新的操作的时候. 如果实体字段值是null,会导致识别失败,导致数据库的值被清空.
easy999000 opened this issue · comments
问题描述及重现代码:
重大BUG
今天我在使用仓储功能进行,统计数值的更新的时候, 发现数据内部的全部字符串字段被清空, 相当于删库跑路了.
我的操作流程是,先从数据库里面把统计数据查询出来,
然后循环把这个要更新的数据源,附加到仓储模块的缓存中(Attach).
在缓存之前,把统计字段(MaxQuantity)设置成0.缓存完成后,再给改回来.
这样这个要更新的数据源和仓储模块中的缓存.应该只有这一个统计字段(MaxQuantity)的值是不一样的.当然只会更新这一个字段的值.
但是我操作完成之后,发现数据库对应的整条数据,文字内容全被清空了.
经过调试我发现,数据源,的被清空的字段,全是null. 但是正常对比字段值是否变化,前后是一样的,应该不会造成数据库值被清空的问题.
我分析的原因是.
数据库的字段,和对应的实体字符串属性是非空类型. [JsonProperty, Column(StringLength = 100, IsNullable = false)].
但是我手动做这个待更新实体的时候,并没有给所有的字段赋值,我只赋值了,我需要用到的2个字段.id和MaxQuantity.
那么这就导致其他字段在数据源中的值是null.
正常来说这样也不会有问题.
但是,当我把数据attach到仓储层后, 仓储层把原始数据 也就是null的值保留了下来.
但是当我更新数据的时候,执行.repo.Update 的时候,在update的过程中, 数据源中的null值,被修改成了 "",空字符串.
这就导致,对比数据字段变化的时候. 所有的null值的字段都发生了变化,就导致了异常数据的覆盖.
数据库实体
[JsonObject(MemberSerialization.OptIn), Table(DisableSyncStructure = true)]
public partial class ProjectItem {
[JsonProperty, Column(DbType = "bigint", IsPrimary = true, IsIdentity = true)]
public long ID { get; set; }
/// <summary>
/// 辅材单价
/// </summary>
[JsonProperty, Column(DbType = "decimal(14,4)")]
public decimal AuxiliaryUnitPrice { get; set; } = 0.0000M;
/// <summary>
/// 编码
/// </summary>
[JsonProperty, Column(StringLength = 100, IsNullable = false)]
public string Code { get; set; }
/// <summary>
/// 实际最大用量
/// </summary>
[JsonProperty, Column(DbType = "decimal(14,4)")]
public decimal MaxQuantity { get; set; } = 0.0000M;
/// <summary>
/// 名称
/// </summary>
[JsonProperty, Column(StringLength = 50, IsNullable = false)]
public string Name { get; set; }
}
数据库操作方法,也就是这个方法导致的数据被覆盖.
private void UpdateProjectItemQuantity(int LaborInfoID)
{
var ProjectItemIDList = SqlHelper.Select<LaborItem>()
.Where(w => w.InfoID == LaborInfoID)
.ToList(s => s.ProjectItemID);
var dbItemMaterialList2 = SqlHelper.Select<LaborItem, LaborInfo>()
.InnerJoin(j => j.t1.InfoID == j.t2.ID)
.Where(w => ProjectItemIDList.Contains(w.t1.ProjectItemID))
// .Where(w => w.t2.Status == 7)
.GroupBy(g => g.t1.ProjectItemID)
.ToList(s => new ProjectItem
{
ID = s.Key,
MaxQuantity = s.Sum(s.Value.Item1.Quantity),
Code = ""
});
var repo = SqlHelper.GetRepository<ProjectItem>();
//var updateData = repo.Where(w=> ItemMaterialIDList.Contains(w.ID)).ToList();
foreach (var item in dbItemMaterialList2)
{
var MaxQuantity = item.MaxQuantity;
item.MaxQuantity = 0;
repo.Attach(item);
item.MaxQuantity = MaxQuantity;
}
var n1 = repo.Update(dbItemMaterialList2);
}
图片传不上来,都发在群里了.
数据库版本
mysql 8
安装的Nuget包
.net framework/. net core? 及具体版本
ConsoleApp1.zip
测试代码,可以复现.
经过不断的排除,终于定位具体的bug点位.
当freesql的.Aop.AuditValue.事件挂上一个监控事件后.哪怕是挂一个空方法.都会导致.实体null的字段对比失败.
下面附上,测试代码.
只要.Aop.AuditValue事件有赋值,仓储功能变不能正确对比null字段.
但是奇怪的事情是,2个独立的freesql实体, 既然会互相影响. 这部分代码也在下面的复现代码里面.
在AuditValue 逻辑中
如果遇到了设置了 IsNullable = false
的逻辑,如果数据是null,会主动更新成“”,这样可以避免NULL值数据无法插入的问题
但使用仓储的基于变化的Attach,会把NULL改成“”,会把数据更新成"",原数据库中的值会被清空。
[JsonProperty, Column(StringLength = 100, IsNullable = false)]
public string? Code { get; set; }
var t1 = new ProjectItem { ID = 53, MaxQuantity = 0};
var repo2 = fsql2.GetRepository<ProjectItem>();
repo2.Attach(t1);
t1.MaxQuantity = 111;
var n2 = repo2.Update(t1);
生成的SQL是
UPDATE "ProjectItem" SET "Code" = @p_0, "MaxQuantity" = @p_1, "Name" = @p_2 WHERE ("ID" = 53)
基于仓储的Attach,正确的SQL应该是
UPDATE "ProjectItem" SET "MaxQuantity" = @p_1 WHERE ("ID" = 53)
在AuditValue 逻辑中
如果遇到了设置了
IsNullable = false
的逻辑,如果数据是null,会主动更新成“”,这样可以避免NULL值数据无法插入的问题但使用仓储的基于变化的Attach,会把NULL改成“”,会把数据更新成"",原数据库中的值会被清空。
[JsonProperty, Column(StringLength = 100, IsNullable = false)] public string? Code { get; set; }var t1 = new ProjectItem { ID = 53, MaxQuantity = 0}; var repo2 = fsql2.GetRepository<ProjectItem>(); repo2.Attach(t1); t1.MaxQuantity = 111; var n2 = repo2.Update(t1);生成的SQL是
UPDATE "ProjectItem" SET "Code" = @p_0, "MaxQuantity" = @p_1, "Name" = @p_2 WHERE ("ID" = 53)基于仓储的Attach,正确的SQL应该是
UPDATE "ProjectItem" SET "MaxQuantity" = @p_1 WHERE ("ID" = 53)
有道理.