亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解如何獲取C#類中發(fā)生數(shù)據(jù)變化的屬性信息

 更新時(shí)間:2020年05月22日 10:12:47   作者:墨墨墨墨小宇  
這篇文章主要介紹了詳解如何獲取C#類中發(fā)生數(shù)據(jù)變化的屬性信息,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、前言#

在平時(shí)的開發(fā)中,當(dāng)用戶修改數(shù)據(jù)時(shí),一直沒(méi)有很好的辦法來(lái)記錄具體修改了那些信息,只能暫時(shí)采用將類序列化成 json 字符串,然后全塞入到日志中的方式,此時(shí)如果我們想要知道用戶具體改變了哪幾個(gè)字段的值的話就很困難了。因此,趁著這個(gè)假期,就來(lái)解決這個(gè)一直遺留的小問(wèn)題,本篇文章記錄了我目前實(shí)現(xiàn)的方法,如果你有不同于文中所列出的方案的話,歡迎指出。

代碼倉(cāng)儲(chǔ)地址:https://github.com/Lanesra712/ingos-common/tree/master/sample/csharp/get-data-changed-properties

二、Step by Step#

1、需求場(chǎng)景#

一個(gè)經(jīng)常遇到的使用場(chǎng)景,用戶 A 修改了某個(gè)表單頁(yè)面上的數(shù)據(jù)信息,然后提交到我們的服務(wù)端完成數(shù)據(jù)的更新,對(duì)于具有某些權(quán)限的用戶來(lái)說(shuō),則是期望可以看到所有用戶對(duì)于該表單進(jìn)行操作前后的數(shù)據(jù)變更。

2、解決方法#

既然想要得知用戶操作前后的數(shù)據(jù)差異,我們肯定需要去對(duì)用戶操作前后的數(shù)據(jù)進(jìn)行比對(duì),這里就落到我們承接數(shù)據(jù)的類身上。

在我們定義類中的屬性時(shí),更多的是使用自動(dòng)屬性的方式來(lái)完成屬性的 getter、setter 聲明,而完整的屬性聲明方式則需要我們定義一個(gè)字段用來(lái)承接對(duì)于該屬性的變更。

// 自動(dòng)屬性聲明
public class Entity1
{
  public Guid Id { get; set; }
}

// 完整的屬性聲明
public class Entity2
{
  private Guid _id;

  public Guid Id
  {
    get => _id;
    set => _id = value;
  }
}

因?yàn)樵诮o屬性進(jìn)行賦值的時(shí)候,需要調(diào)用屬性的 set 構(gòu)造器,因此,在 set 構(gòu)造器內(nèi)部我們是不是就可以直接對(duì)新賦的值進(jìn)行判斷,從而記錄下屬性的變更過(guò)程,改造后的類屬性聲明代碼如下。

public class Sample
{
  private string _a;

  public string A
  {
    get => _a;
    set
    {
      if (_a == value)
        return;

      string old = _a;
      _a = value;
      propertyChangelogs.Add(new PropertyChangelog<Sample>(nameof(A), old, _a));
    }
  }

  private double _b;

  public double B
  {
    get => _b;
    set
    {
      if (_b == value)
        return;

      double old = _b;
      _b = value;
      propertyChangelogs.Add(new PropertyChangelog<Sample>(nameof(B), old.ToString(), _b.ToString()));
    }
  }

  private IList<PropertyChangelog<Sample>> propertyChangelogs = new List<PropertyChangelog<Sample>>();

  public IEnumerable<PropertyChangelog<Sample>> Changelogs() => propertyChangelogs;
}

在改造后的類屬性聲明中,我們?cè)趯傩缘?set 構(gòu)造器中將新賦的值與原先的值進(jìn)行判斷,當(dāng)存在兩次值不一樣時(shí),就寫入到變更記錄的集合中,從而實(shí)現(xiàn)記錄數(shù)據(jù)變更的目的。這里對(duì)于變更記錄的實(shí)體類屬性定義如下所示。

public class PropertyChangelog<T>
{
  /// <summary>
  /// ctor
  /// </summary>
  public PropertyChangelog()
  { }

  /// <summary>
  /// ctor
  /// </summary>
  /// <param name="propertyName">屬性名稱</param>
  /// <param name="oldValue">舊值</param>
  /// <param name="newValue">新值</param>
  public PropertyChangelog(string propertyName, string oldValue, string newValue)
  {
    PropertyName = propertyName;
    OldValue = oldValue;
    NewValue = newValue;
  }

  /// <summary>
  /// ctor
  /// </summary>
  /// <param name="className">類名</param>
  /// <param name="propertyName">屬性名稱</param>
  /// <param name="oldValue">舊值</param>
  /// <param name="newValue">新值</param>
  /// <param name="changedTime">修改時(shí)間</param>
  public PropertyChangelog(string className, string propertyName, string oldValue, string newValue, DateTime changedTime)
    : this(propertyName, oldValue, newValue)
  {
    ClassName = className;
    ChangedTime = changedTime;
  }

  /// <summary>
  /// 類名稱
  /// </summary>
  public string ClassName { get; set; } = typeof(T).FullName;

  /// <summary>
  /// 屬性名稱
  /// </summary>
  public string PropertyName { get; set; }

  /// <summary>
  /// 舊值
  /// </summary>
  public string OldValue { get; set; }

  /// <summary>
  /// 新值
  /// </summary>
  public string NewValue { get; set; }

  /// <summary>
  /// 修改時(shí)間
  /// </summary>
  public DateTime ChangedTime { get; set; } = DateTime.Now;
}

可以看到,在我們對(duì) Sample 類進(jìn)行初始化賦值時(shí),記錄了兩次關(guān)于類屬性的數(shù)據(jù)變更記錄,而當(dāng)我們進(jìn)行重新賦值時(shí),只有屬性 A 發(fā)生了數(shù)據(jù)改變,因此只記錄了屬性 A 的數(shù)據(jù)變更記錄。

雖然這里已經(jīng)達(dá)到我們的目的,但是如果采用這種方式的話,相當(dāng)于原先項(xiàng)目中需要實(shí)現(xiàn)數(shù)據(jù)記錄功能的類的屬性聲明方式全部需要重寫,同時(shí),基于 C# 本身已經(jīng)提供了自動(dòng)屬性的方式來(lái)簡(jiǎn)化屬性聲明,結(jié)果現(xiàn)在我們又回到了傳統(tǒng)屬性的聲明方式,似乎顯得有些不太聰明的樣子。因此,既然通過(guò)一個(gè)個(gè)屬性進(jìn)行比較的方式過(guò)于繁瑣,這里我們通過(guò)反射的方式直接對(duì)比修改前后的兩個(gè)實(shí)體類,批量獲取發(fā)生數(shù)據(jù)變更的屬性信息。

我們最終想要實(shí)現(xiàn)的是用戶可以看到關(guān)于某個(gè)表單的字段屬性數(shù)據(jù)變化的過(guò)程,而我們定義在 C# 類中的屬性有時(shí)候需要與實(shí)際頁(yè)面上顯示的字段名稱進(jìn)行映射,以及某些屬性其實(shí)沒(méi)有必要記錄數(shù)據(jù)變化的情況,這里我通過(guò)添加自定義特性的方式,完善功能的實(shí)現(xiàn)。

/// <summary>
/// 為指定的屬性設(shè)定數(shù)據(jù)變更記錄
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class PropertyChangeTrackingAttribute : Attribute
{
  /// <summary>
  /// 指定 PropertyChangeTrackingAttribute 屬性的默認(rèn)值
  /// </summary>
  public static readonly PropertyChangeTrackingAttribute Default = new PropertyChangeTrackingAttribute();

  /// <summary>
  /// 構(gòu)造一個(gè)新的 PropertyChangeTrackingAttribute 特性實(shí)例
  /// </summary>
  public PropertyChangeTrackingAttribute()
  { }

  /// <summary>
  /// 構(gòu)造一個(gè)新的 PropertyChangeTrackingAttribute 特性實(shí)例
  /// </summary>
  /// <param name="ignore">是否忽略該字段的數(shù)據(jù)變化</param>
  public PropertyChangeTrackingAttribute(bool ignore = false)
  {
    IgnoreValue = ignore;
  }

  /// <summary>
  /// 構(gòu)造一個(gè)新的 PropertyChangeTrackingAttribute 特性實(shí)例
  /// </summary>
  /// <param name="displayName">屬性對(duì)應(yīng)頁(yè)面顯示名稱</param>
  public PropertyChangeTrackingAttribute(string displayName)
    : this(false)
    {
      DisplayNameValue = displayName;
    }

  /// <summary>
  /// 構(gòu)造一個(gè)新的 PropertyChangeTrackingAttribute 特性實(shí)例
  /// </summary>
  /// <param name="displayName">屬性對(duì)應(yīng)頁(yè)面顯示名稱</param>
  /// <param name="ignore">是否忽略該字段的數(shù)據(jù)變化</param>
  public PropertyChangeTrackingAttribute(string displayName, bool ignore)
    : this(ignore)
    {
      DisplayNameValue = displayName;
    }

  /// <summary>
  /// 獲取特性中的屬性對(duì)應(yīng)頁(yè)面上顯示名稱參數(shù)信息
  /// </summary>
  public virtual string DisplayName => DisplayNameValue;

  /// <summary>
  /// 獲取特性中的是否忽略該字段的數(shù)據(jù)變化參數(shù)信息
  /// </summary>
  public virtual bool Ignore => IgnoreValue;

  /// <summary>
  /// 修改屬性對(duì)應(yīng)頁(yè)面顯示名稱參數(shù)值
  /// </summary>
  protected string DisplayNameValue { get; set; }

  /// <summary>
  /// 修改是否忽略該字段的數(shù)據(jù)變化
  /// </summary>
  protected bool IgnoreValue { get; set; }
}

考慮到我們的類中可能會(huì)包含很多的屬性信息,如果一個(gè)個(gè)的給屬性添加特性會(huì)很麻煩,因此這里可以直接針對(duì)類添加該特性。同時(shí),針對(duì)我們可能會(huì)排除類中的某些屬性,或者設(shè)定屬性在頁(yè)面中顯示的名稱,這里我們可以針對(duì)特定的類屬性進(jìn)行單獨(dú)添加特性。

完成了自定義特性之后,考慮到我們后續(xù)使用的方便,這里我采用創(chuàng)建擴(kuò)展方法的形式來(lái)聲明我們的函數(shù)方法,同時(shí)我在 PropertyChangelog 類中添加了 DisplayName 屬性用來(lái)存放屬性對(duì)應(yīng)于頁(yè)面上存放的名稱,最終完成后的代碼如下所示。

/// <summary>
/// 獲取類屬性數(shù)據(jù)變化記錄
/// </summary>
/// <typeparam name="T">監(jiān)聽的類類型</typeparam>
/// <param name="oldObj">包含原始值的類</param>
/// <param name="newObj">變更屬性值后的類</param>
/// <param name="propertyName">指定的屬性名稱</param>
/// <returns></returns>
public static IEnumerable<PropertyChangelog<T>> GetPropertyLogs<T>(this T oldObj, T newObj, string propertyName = null)
{
  IList<PropertyChangelog<T>> changelogs = new List<PropertyChangelog<T>>();

  // 1、獲取需要添加數(shù)據(jù)變更記錄的屬性信息
  //
  IList<PropertyInfo> properties = new List<PropertyInfo>();

  // PropertyChangeTracking 特性的類型
  var attributeType = typeof(PropertyChangeTrackingAttribute);

  // 對(duì)應(yīng)的類中包含的屬性信息
  var classProperties = typeof(T).GetProperties();

  // 獲取類中需要添加變更記錄的屬性信息
  //
  bool flag = Attribute.IsDefined(typeof(T), attributeType);

  foreach (var i in classProperties)
  {
    // 獲取當(dāng)前屬性添加的特性信息
    var attributeInfo = (PropertyChangeTrackingAttribute)i.GetCustomAttribute(attributeType);

    // 類未添加特性,并且該屬性也未添加特性
    if (!flag && attributeInfo == null)
      continue;

    // 類添加特性,該屬性未添加特性
    if (flag && attributeInfo == null)
      properties.Add(i);

    // 不管類有沒(méi)有添加特性,只要類中的屬性添加特性,并且 Ignore 為 false
    if (attributeInfo != null && !attributeInfo.Ignore)
      properties.Add(i);
  }

  // 2、判斷指定的屬性數(shù)據(jù)是否發(fā)生變更
  //
  foreach (var property in properties)
  {
    var oldValue = property.GetValue(oldObj) ?? "";
    var newValue = property.GetValue(newObj) ?? "";

    if (oldValue.Equals(newValue))
      continue;

    // 獲取當(dāng)前屬性在頁(yè)面上顯示的名稱
    //
    var attributeInfo = (PropertyChangeTrackingAttribute)property.GetCustomAttribute(attributeType);
    string displayName = attributeInfo == null ? property.Name
      : attributeInfo.DisplayName;

    changelogs.Add(new PropertyChangelog<T>(property.Name, displayName, oldValue.ToString(), newValue.ToString()));
  }

  return string.IsNullOrEmpty(propertyName) ? changelogs
    : changelogs.Where(i => i.PropertyName.Equals(propertyName));
}

在下面的這個(gè)測(cè)試案例中,Entity 類實(shí)際上只會(huì)記錄 5 個(gè)屬性的數(shù)據(jù)變化,我們手動(dòng)創(chuàng)建兩個(gè) Entity 類實(shí)例,同時(shí)改變兩個(gè)類實(shí)例對(duì)應(yīng)的屬性值。從我們運(yùn)行的示意圖中可以看到,雖然兩個(gè)類實(shí)例的 Id 屬性值不同,但是因?yàn)楸晃覀兪謩?dòng)忽略了,所以最終只顯示我們?cè)O(shè)定的幾個(gè)屬性的變化信息。

[PropertyChangeTracking]
public class Entity
{
  [PropertyChangeTracking(ignore: true)]
  public Guid Id { get; set; }

  [PropertyChangeTracking(displayName: "序號(hào)")]
  public string OId { get; set; }

  [PropertyChangeTracking(displayName: "第一個(gè)字段")]
  public string A { get; set; }

  public double B { get; set; }

  public bool C { get; set; }

  public DateTime Date { get; set; } = DateTime.Now;
}

三、總結(jié)#

這一章是針對(duì)我之前在工作中遇到的一個(gè)問(wèn)題,趁著假期考慮的一個(gè)解決方法,雖然只是一個(gè)小問(wèn)題,但是還是挺有借鑒意義的,如果能夠給你在日常的開發(fā)中提供些許的幫助,不勝榮幸。

作者:墨墨墨墨小宇

出處:https://www.cnblogs.com/danvic712/p/how-to-get-the-data-changed-properties-in-csharp-class.html

到此這篇關(guān)于詳解如何獲取C#類中發(fā)生數(shù)據(jù)變化的屬性信息的文章就介紹到這了,更多相關(guān)C#獲取類屬性信息內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 大白話講解C# 中的委托

    大白話講解C# 中的委托

    這篇文章主要介紹了C# 中的委托的相關(guān)資料,幫助初學(xué)者更好的理解和使用c#,感興趣的朋友可以了解下
    2020-11-11
  • C#使用channel實(shí)現(xiàn)Plc異步任務(wù)之間的通信

    C#使用channel實(shí)現(xiàn)Plc異步任務(wù)之間的通信

    在C#的并發(fā)編程中,Channel是一種非常強(qiáng)大的數(shù)據(jù)結(jié)構(gòu),用于在生產(chǎn)者和消費(fèi)者之間進(jìn)行通信,本文將給大家介紹C#使用channel實(shí)現(xiàn)Plc異步任務(wù)之間的通信,文中有相關(guān)的代碼示例供大家參考,感興趣的朋友跟著小編一起來(lái)看看吧
    2024-05-05
  • C#集合之可觀察集合的用法

    C#集合之可觀察集合的用法

    這篇文章介紹了C#集合之可觀察集合的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • C#引用類型轉(zhuǎn)換的常見(jiàn)方式總結(jié)

    C#引用類型轉(zhuǎn)換的常見(jiàn)方式總結(jié)

    這篇文章主要介紹了C#引用類型轉(zhuǎn)換的常見(jiàn)方式,包括子類轉(zhuǎn)換成父類,父類轉(zhuǎn)換成子類,以及不是子父級(jí)關(guān)系類之間的轉(zhuǎn)換,需要的朋友可以參考下
    2014-09-09
  • C#之多余控件事件及代碼刪除問(wèn)題

    C#之多余控件事件及代碼刪除問(wèn)題

    這篇文章主要介紹了C#之多余控件事件及代碼刪除問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • C#中進(jìn)程的掛起與恢復(fù)

    C#中進(jìn)程的掛起與恢復(fù)

    這篇文章主要介紹了C#中進(jìn)程的掛起與恢復(fù)操作方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • C#遍歷集合與移除元素的方法

    C#遍歷集合與移除元素的方法

    這篇文章主要介紹了C#遍歷集合與移除元素的方法,結(jié)合實(shí)例形式分析了C#使用for循環(huán)遍歷集合以及add與Remove方法進(jìn)行元素添加與移除的使用技巧,需要的朋友可以參考下
    2016-06-06
  • C#使用TensorFlow.NET訓(xùn)練自己的數(shù)據(jù)集的方法

    C#使用TensorFlow.NET訓(xùn)練自己的數(shù)據(jù)集的方法

    這篇文章主要介紹了C#使用TensorFlow.NET訓(xùn)練自己的數(shù)據(jù)集的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 常用C#正則表達(dá)式匯總介紹

    常用C#正則表達(dá)式匯總介紹

    c#正則表達(dá)式,用于字符串處理、表單驗(yàn)證等場(chǎng)合,實(shí)用高效?,F(xiàn)將一些常用的表達(dá)式收集于此,以備不時(shí)之需。
    2016-01-01
  • 使用GPS經(jīng)緯度定位附近地點(diǎn)(某一點(diǎn)范圍內(nèi)查詢)

    使用GPS經(jīng)緯度定位附近地點(diǎn)(某一點(diǎn)范圍內(nèi)查詢)

    目前的工作是需要手機(jī)查找附近N米以內(nèi)的商戶,致想法是已知一個(gè)中心點(diǎn),一個(gè)半徑,求圓包含于圓拋物線里所有的點(diǎn),經(jīng)緯度是一個(gè)點(diǎn),半徑是一個(gè)距離,不能直接加減,下面提供C#的解決方法
    2013-12-12

最新評(píng)論