我有一个包含5个属性的类.
如果任何值被分配给这些字段中的任何一个,则另一个值(例如IsDIrty)将改变为真.
public class Class1
{
bool IsDIrty {get;set;}
string Prop1 {get;set;}
string Prop2 {get;set;}
string Prop3 {get;set;}
string Prop4 {get;set;}
string Prop5 {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
Bin*_*ier 44
要做到这一点,你不能真正使用自动getter和setter,你需要在每个setter中设置IsDirty.
我通常有一个"setProperty"泛型方法,它接受ref参数,属性名称和新值.我在setter中调用它,允许我可以设置isDirty的单个点并提高Change通知事件,例如
protected bool SetProperty<T>(string name, ref T oldValue, T newValue) where T : System.IComparable<T>
{
if (oldValue == null || oldValue.CompareTo(newValue) != 0)
{
oldValue = newValue;
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
isDirty = true;
return true;
}
return false;
}
// For nullable types
protected void SetProperty<T>(string name, ref Nullable<T> oldValue, Nullable<T> newValue) where T : struct, System.IComparable<T>
{
if (oldValue.HasValue != newValue.HasValue || (newValue.HasValue && oldValue.Value.CompareTo(newValue.Value) != 0))
{
oldValue = newValue;
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}
}
Run Code Online (Sandbox Code Playgroud)
Shi*_*mmy 18
您可以实现现在包含在.NET Standard 2.0中的IChangeTracking
或IRevertibleChangeTracking
接口.
实施如下:
IChangeTracking
:
class Entity : IChangeTracking
{
string _FirstName;
public string FirstName
{
get => _FirstName;
set
{
if (_FirstName != value)
{
_FirstName = value;
IsChanged = true;
}
}
}
string _LastName;
public string LastName
{
get => _LastName;
set
{
if (_LastName != value)
{
_LastName = value;
IsChanged = true;
}
}
}
public bool IsChanged { get; private set; }
public void AcceptChanges() => IsChanged = false;
}
Run Code Online (Sandbox Code Playgroud)
IRevertibleChangeTracking
:
class Entity : IRevertibleChangeTracking
{
Dictionary<string, object> _Values = new Dictionary<string, object>();
string _FirstName;
public string FirstName
{
get => _FirstName;
set
{
if (_FirstName != value)
{
if (!_Values.ContainsKey(nameof(FirstName)))
_Values[nameof(FirstName)] = _FirstName;
_FirstName = value;
IsChanged = true;
}
}
}
string _LastName;
public string LastName
{
get => _LastName;
set
{
if (_LastName != value)
{
if (!_Values.ContainsKey(nameof(LastName)))
_Values[nameof(LastName)] = _LastName;
_LastName = value;
IsChanged = true;
}
}
}
public bool IsChanged { get; private set; }
public void RejectChanges()
{
foreach (var property in _Values)
GetType().GetRuntimeProperty(property.Key).SetValue(this, property.Value);
AcceptChanges();
}
public void AcceptChanges()
{
_Values.Clear();
IsChanged = false;
}
}
Run Code Online (Sandbox Code Playgroud)
我最喜欢的另一个选项是使用更改跟踪库,例如TrackerDog,它为您生成所有样板代码,同时只需要提供POCO实体.
如果您不想手动实现所有属性,还有更多方法可以实现此目的.一种选择是使用编织库,例如Fody.PropertyChanged和Fody.PropertyChanging,并处理更改方法以缓存旧值并跟踪对象状态.另一种选择是将对象的图形存储为MD5或其他一些哈希值,并在任何更改时重置它,您可能会感到惊讶,但如果您不期望有太多变化,并且如果您只是按需请求它,它可以真正起作用快速.
这是一个示例实现(注意:需要Json.NET和Fody/PropertyChanged:
[AddINotifyPropertyChangedInterface]
class Entity : IChangeTracking
{
public string UserName { get; set; }
public string LastName { get; set; }
public bool IsChanged { get; private set; }
string hash;
string GetHash()
{
if (hash == null)
using (var md5 = MD5.Create())
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
{
_JsonSerializer.Serialize(writer, this);
var hash = md5.ComputeHash(stream);
this.hash = Convert.ToBase64String(hash);
}
return hash;
}
string acceptedHash;
public void AcceptChanges() => acceptedHash = GetHash();
static readonly JsonSerializer _JsonSerializer = CreateSerializer();
static JsonSerializer CreateSerializer()
{
var serializer = new JsonSerializer();
serializer.Converters.Add(new EmptyStringConverter());
return serializer;
}
class EmptyStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(string);
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
=> throw new NotSupportedException();
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
if (value is string str && str.All(char.IsWhiteSpace))
value = null;
writer.WriteValue(value);
}
public override bool CanRead => false;
}
}
Run Code Online (Sandbox Code Playgroud)
Dan的解决方案非常完美.
另一个选择是考虑你是否必须在多个类上执行此操作(或者您希望外部类"监听"属性的更改):
INotifyPropertyChanged
在抽象类中实现接口IsDirty
属性移动到抽象类Class1
和所有其他需要此功能的类来扩展您的抽象类PropertyChanged
抽象类实现的事件,并将其名称传递给事件PropertyChanged
事件并IsDirty
在触发时设置为true最初创建抽象类有点工作,但它是一个更好的模型,用于监视数据更改,因为任何其他类都可以看到何时IsDirty
(或任何其他属性)更改.
我的基类如下所示:
public abstract class BaseModel : INotifyPropertyChanged
{
/// <summary>
/// Initializes a new instance of the BaseModel class.
/// </summary>
protected BaseModel()
{
}
/// <summary>
/// Fired when a property in this class changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Triggers the property changed event for a specific property.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Run Code Online (Sandbox Code Playgroud)
任何其他模型然后只是扩展BaseModel
,并NotifyPropertyChanged
在每个setter中调用.
归档时间: |
|
查看次数: |
34794 次 |
最近记录: |