C#中为含List属性的类实现HasChanged状态追踪异常问题
对象列表状态变更检测问题解决方案
问题描述
我正在将数据集(Dataset)转换为类对象列表,想要模仿DataTable的HasChanged方法,通过对象追踪检测列表中对象的新增、修改或删除操作。但当前实现存在以下问题:
- 调用
SetOriginalState保存初始状态后,HasChanged返回true(预期应为false) - 修改对象属性后,
HasChanged仍返回true
经排查,当前对象与originalState的属性值完全一致,无法定位原因,寻求解决方案。
相关代码
VendorInvoiceData Model
public class VendorInvoiceData { public VendorInvoiceData() { } public VendorInvoiceData(VendorInvoiceData vendorInvoiceData) { VendorInvoiceDataId = vendorInvoiceData.VendorInvoiceDataId; VendorInvoiceId = vendorInvoiceData.VendorInvoiceId; ChargeId = vendorInvoiceData.ChargeId; ChargeCode = vendorInvoiceData.ChargeCode; InvoiceAmount = vendorInvoiceData.InvoiceAmount; CreatedBy = vendorInvoiceData.CreatedBy; CreationDate = vendorInvoiceData.CreationDate; SpreadGroup = vendorInvoiceData.SpreadGroup; SpreadOrder = vendorInvoiceData.SpreadOrder; } public int VendorInvoiceDataId { get; set; } public int? VendorInvoiceId { get; set; } public int? ChargeId { get; set; } public string ChargeCode { get; set; } public decimal? InvoiceAmount { get; set; } public string CreatedBy { get; set; } public DateTime? CreationDate { get; set; } public string SpreadGroup { get; set; } public short? SpreadOrder { get; set; } }
VendorInvoiceStatusLog Model
public class VendorInvoiceStatusLog { public VendorInvoiceStatusLog() { } public VendorInvoiceStatusLog(VendorInvoiceStatusLog vendorInvoiceStatusLog) { VendorInvoiceStatusLogId = vendorInvoiceStatusLog.VendorInvoiceStatusLogId; VendorInvoiceId = vendorInvoiceStatusLog.VendorInvoiceId; VendorInvoiceStatus = vendorInvoiceStatusLog.VendorInvoiceStatus; VendorInvoiceStatusDescription = vendorInvoiceStatusLog.VendorInvoiceStatusDescription; CreatedBy = vendorInvoiceStatusLog.CreatedBy; CreationDate = vendorInvoiceStatusLog.CreationDate; IsAdded = vendorInvoiceStatusLog.IsAdded; IsUpdated = vendorInvoiceStatusLog.IsUpdated; IsDeleted = vendorInvoiceStatusLog.IsDeleted; } public int VendorInvoiceStatusLogId { get; set; } public int? VendorInvoiceId { get; set; } public string VendorInvoiceStatus { get; set; } public string VendorInvoiceStatusDescription { get; set; } public string CreatedBy { get; set; } public DateTime? CreationDate { get; set; } public bool IsAdded { get; set; } = false; public bool IsUpdated { get; set; } = false; public bool IsDeleted { get; set; } = false; }
VendorInvoiceModelsList Model
public class VendorInvoiceModelsList { public List<VendorInvoiceStatusLog> VendorInvoiceStatusLogList { get; set; } public List<VendorInvoiceData> VendorInvoiceDataList { get; set; } public VendorInvoiceModelsList originalState; public void SetOriginalState() { originalState = new VendorInvoiceModelsList { VendorInvoiceStatusLogList = VendorInvoiceStatusLogList.Select(x => new VendorInvoiceStatusLog(x)).ToList(), VendorInvoiceDataList = VendorInvoiceDataList.Select(x => new VendorInvoiceData(x)).ToList(), }; } public bool HasChanged() { if (originalState == null) return false; bool statusLog = !VendorInvoiceStatusLogList.SequenceEqual(originalState.VendorInvoiceStatusLogList); bool data = !VendorInvoiceDataList.SequenceEqual(originalState.VendorInvoiceDataList); return statusLog || data; } }
使用代码
VendorInvoiceModelsList vendorInvoiceModels = Find(vendorInvoiceId); vendorInvoiceModels.SetOriginalState(); var data = vendorInvoiceModels.HasChanged(); // 返回true(预期false) vendorInvoiceModels.VendorInvoiceStatusLogList[0].VendorInvoiceStatus = "newStatus"; // 修正原代码中的属性名错误 var data1 = vendorInvoiceModels.HasChanged(); // 返回true
问题根源
SequenceEqual方法对于引用类型,默认比较的是对象的引用地址,而非属性值。你的模型类是引用类型,即使通过复制构造函数创建了属性值完全一致的新对象,它们的引用地址仍然不同,因此SequenceEqual会判定两个对象不相等,导致HasChanged始终返回true。
解决方案
方案1:重写模型类的Equals和GetHashCode方法
为每个模型类实现基于属性值的相等比较逻辑:
修改VendorInvoiceData类
public class VendorInvoiceData { // 原有构造函数和属性不变... public override bool Equals(object obj) { if (obj is not VendorInvoiceData other) return false; return VendorInvoiceDataId == other.VendorInvoiceDataId && VendorInvoiceId == other.VendorInvoiceId && ChargeId == other.ChargeId && ChargeCode == other.ChargeCode && InvoiceAmount == other.InvoiceAmount && CreatedBy == other.CreatedBy && CreationDate == other.CreationDate && SpreadGroup == other.SpreadGroup && SpreadOrder == other.SpreadOrder; } public override int GetHashCode() { return HashCode.Combine(VendorInvoiceDataId, VendorInvoiceId, ChargeId, ChargeCode, InvoiceAmount, CreatedBy, CreationDate, SpreadGroup, SpreadOrder); } }
修改VendorInvoiceStatusLog类
public class VendorInvoiceStatusLog { // 原有构造函数和属性不变... public override bool Equals(object obj) { if (obj is not VendorInvoiceStatusLog other) return false; return VendorInvoiceStatusLogId == other.VendorInvoiceStatusLogId && VendorInvoiceId == other.VendorInvoiceId && VendorInvoiceStatus == other.VendorInvoiceStatus && VendorInvoiceStatusDescription == other.VendorInvoiceStatusDescription && CreatedBy == other.CreatedBy && CreationDate == other.CreationDate && IsAdded == other.IsAdded && IsUpdated == other.IsUpdated && IsDeleted == other.IsDeleted; } public override int GetHashCode() { return HashCode.Combine(VendorInvoiceStatusLogId, VendorInvoiceId, VendorInvoiceStatus, VendorInvoiceStatusDescription, CreatedBy, CreationDate, IsAdded, IsUpdated, IsDeleted); } }
方案2:使用自定义IEqualityComparer
如果不想修改模型类,可以创建自定义的相等比较器,在SequenceEqual中指定使用:
创建VendorInvoiceDataEqualityComparer
public class VendorInvoiceDataEqualityComparer : IEqualityComparer<VendorInvoiceData> { public bool Equals(VendorInvoiceData x, VendorInvoiceData y) { if (x == null && y == null) return true; if (x == null || y == null) return false; return x.VendorInvoiceDataId == y.VendorInvoiceDataId && x.VendorInvoiceId == y.VendorInvoiceId && x.ChargeId == y.ChargeId && x.ChargeCode == y.ChargeCode && x.InvoiceAmount == y.InvoiceAmount && x.CreatedBy == y.CreatedBy && x.CreationDate == y.CreationDate && x.SpreadGroup == y.SpreadGroup && x.SpreadOrder == y.SpreadOrder; } public int GetHashCode(VendorInvoiceData obj) { return HashCode.Combine(obj.VendorInvoiceDataId, obj.VendorInvoiceId, obj.ChargeId, obj.ChargeCode, obj.InvoiceAmount, obj.CreatedBy, obj.CreationDate, obj.SpreadGroup, obj.SpreadOrder); } }
创建VendorInvoiceStatusLogEqualityComparer
public class VendorInvoiceStatusLogEqualityComparer : IEqualityComparer<VendorInvoiceStatusLog> { public bool Equals(VendorInvoiceStatusLog x, VendorInvoiceStatusLog y) { if (x == null && y == null) return true; if (x == null || y == null) return false; return x.VendorInvoiceStatusLogId == y.VendorInvoiceStatusLogId && x.VendorInvoiceId == y.VendorInvoiceId && x.VendorInvoiceStatus == y.VendorInvoiceStatus && x.VendorInvoiceStatusDescription == y.VendorInvoiceStatusDescription && x.CreatedBy == y.CreatedBy && x.CreationDate == y.CreationDate && x.IsAdded == y.IsAdded && x.IsUpdated == y.IsUpdated && x.IsDeleted == y.IsDeleted; } public int GetHashCode(VendorInvoiceStatusLog obj) { return HashCode.Combine(obj.VendorInvoiceStatusLogId, obj.VendorInvoiceId, obj.VendorInvoiceStatus, obj.VendorInvoiceStatusDescription, obj.CreatedBy, obj.CreationDate, obj.IsAdded, obj.IsUpdated, obj.IsDeleted); } }
修改HasChanged方法
public bool HasChanged() { if (originalState == null) return false; bool statusLog = !VendorInvoiceStatusLogList.SequenceEqual(originalState.VendorInvoiceStatusLogList, new VendorInvoiceStatusLogEqualityComparer()); bool data = !VendorInvoiceDataList.SequenceEqual(originalState.VendorInvoiceDataList, new VendorInvoiceDataEqualityComparer()); return statusLog || data; }
额外说明
- 原使用代码中的
vendorInvoiceModels.VendorInvoiceList[0]是笔误,应改为VendorInvoiceStatusLogList或VendorInvoiceDataList - 两种方案都能解决问题,方案1更通用(所有需要比较对象相等的场景都能复用),方案2更灵活(不需要修改模型类)
内容的提问来源于stack exchange,提问作者Abhishek Singh
相关产品推荐
相关产品推荐

