تکمیلی مورد قبل:
عذرخواهی میکنم این مورد را کلا فراموش کرده بودم یکی از دوستان پرسید ادامه اش را اینجا میگذارم:
این نسخه کامل شده همون موارد بالایی هست

دو کلاس ایجاد کردم به نام Audit و AuditEntry



public class Audit
{
public long Id { get; set; }
public string CommitValue { get; set; }
public DateTime DateTime { get; set; }


public string UserId { get; set; }
public string UserName { get; set; }
public string Type { get; set; }
public string TableName { get; set; }

public string OldValues { get; set; }
public string NewValues { get; set; }
public string AffectedColumns { get; set; }
public string PrimaryKey { get; set; }
public string IdKey { get; set; }
public string ActionName { get; set; }

public string PCommitValue { get; set; }
public string PersianTableName { get; set; }
public string PAffectedColumns { get; set; }
}


public class AuditEntry
{
public AuditEntry(EntityEntry entry)
{
Entry = entry;
}
public EntityEntry Entry { get; }
public string UserId { get; set; }
public string UserName { get; set; }
public string TableName { get; set; }
public string PersianTableName { get; set; }
public Dictionary<string, object> KeyValues { get; } = new Dictionary<string, object>();
public Dictionary<string, object> OldValues { get; } = new Dictionary<string, object>();
public Dictionary<string, object> NewValues { get; } = new Dictionary<string, object>();
public AuditType AuditType { get; set; }
public List<string> ChangedColumns { get; } = new List<string>();
public List<string> PChangedColumns { get; } = new List<string>();
public Audit ToAudit()
{
var audit = new Audit();
audit.UserId = UserId;
audit.UserName = UserName;
audit.Type = AuditType.ToString();
audit.TableName = TableName;
audit.PersianTableName= PersianTableName;
audit.DateTime = DateTime.Now;
audit.PrimaryKey = JsonSerializer.Serialize(KeyValues);
audit.OldValues = OldValues.Count == 0 ? null : JsonSerializer.Serialize(OldValues);
audit.NewValues = NewValues.Count == 0 ? null : JsonSerializer.Serialize(NewValues);
audit.AffectedColumns = ChangedColumns.Count == 0 ? null : JsonSerializer.Serialize(ChangedColumns);
audit.PAffectedColumns = ChangedColumns.Count == 0 ? null : JsonSerializer.Serialize(PChangedColumns);


audit.IdKey = KeyValues.Values.FirstOrDefault()?.ToString();
foreach (var newItem in NewValues)
{
foreach (var oldItem in OldValues.Where(w=>w.Key == newItem.Key).Where(w=>!object.Equals(w.Value , newItem.Value)))
{
//var xxx = object.ReferenceEquals(oldItem.Value, newItem.Value);
audit.CommitValue = audit.CommitValue + $" {oldItem.Key} : " + oldItem.Value + $" -> " + newItem.Value + ";\n";
audit.PCommitValue = audit.PCommitValue + $" {EnumExtensions.GetDisplayProperty(oldItem.Key.Get Type())} : "
+ oldItem.Value + $" -> " + newItem.Value + "; \n";
}

}
return audit;
}
}


این کلاسها برای ثبت موارد تغییر کرده / ویرایش شده در بانک هست.
در حالت کلی هر رکوردی که ایجاد میشه خودکار ایجاد کننده و ویرایش کننده در کنار همان رکورد ثبت میشود.
این کلاس جدید برای این هست که بدانیم هر رکورد چند بار ویرایش شده و چه کسانی ویرایش را انجام داده اند. در حالت قبل فقط آخرین نفر قابل رهگیری بود ولی با این روش همه کسانی که ویرایش کرده اند را رصد میکنید.

به همین ترتیب متد AuditFields که در تاپیک قبلی هست را هم تغییر میدهیم:



private void AuditFields()
{
ChangeTracker.DetectChanges();


var userId = _accessor.HttpContext?.User?.Identity?.GetUserId() ;
var auditUser = _accessor.HttpContext?.User?.Identity?.Name;
var auditDate = DateTime.Now;
var ipAddress = HttpContextExtensionsGetRemoteIP.GetRemoteIPAddres s(_accessor.HttpContext).ToString();


var auditEntries = new List<AuditEntry>();


foreach (var entry in this.ChangeTracker.Entries<BaseEntity>())
{
var auditEntry = new AuditEntry(entry);
switch (entry.State)
{
case EntityState.Added:
entry.Entity.InsertDate = auditDate;
entry.Entity.InsertBy = auditUser;


entry.Entity.InsertedBy = userId;
entry.Entity.IsActive = true;
entry.Entity.InsertIpAddress = ipAddress;
break;


case EntityState.Modified:
entry.Entity.UpdateDate = auditDate;
entry.Entity.UpdateBy = auditUser;
entry.Entity.UpdatedBy = userId;
entry.Entity.UpdateIpAddress = ipAddress;


foreach (var property in entry.Properties)
{
auditEntry.TableName = entry.Entity.GetType().Name;
auditEntry.PersianTableName = EnumExtensions.GetClassDescription(entry.Entity.Ge tType());
auditEntries.Add(auditEntry);


string propertyName = property.Metadata.Name;
string propertyDisplayName = propertyName;
string propertyPersianDisplayName = EnumExtensions.GetDisplayProperty(property.GetType ());
if (property.Metadata.IsPrimaryKey())
{
auditEntry.KeyValues[propertyName] = property.CurrentValue;
continue;
}
if (property.IsModified)
{
auditEntry.UserId = userId;
auditEntry.UserName = auditUser;
var clx = auditEntries.Select(x => x.Entry.Entity).FirstOrDefault();
auditEntry.ChangedColumns.Add(propertyDisplayName) ;
auditEntry.PChangedColumns.Add(propertyPersianDisp layName);
auditEntry.AuditType = AuditType.Update;
auditEntry.OldValues[propertyName] = property.OriginalValue;
auditEntry.NewValues[propertyName] = property.CurrentValue;
}
}


break;
case EntityState.Deleted:
entry.Entity.DeleteDate = auditDate;
entry.Entity.DeleteBy = auditUser;
entry.Entity.UpdateIpAddress = ipAddress;
break;
}
}


if (auditEntries.Any())
{
foreach (var auditEntry in auditEntries)
{
ChangeLogs.Add(auditEntry.ToAudit());
}
}
}



متد SaveChanges که قبلا override کرده بودیم به حالت قبل باقی میماند و تغییری ندارد.

یک نکته اینجا هست جهت یادآوردی عرض میکنم. کلاس BaseEntity یک کلاس عمومی هست که همه کلاسها از اون ارث بری میکنند. همان ستونهای Audit که میبینید به این طریق ایجاد شده اند.



public class BaseEntity
{
[MaxLength(500), Display(Name = "توضیحات : "), JsonPropertyName("توضیحات")]
public string Description { get; set; }


[MaxLength(450), Display(Name = "ایجادکننده:")]
public string InsertBy { get; set; }




[MaxLength(450), Display(Name = "InsertedByUser")]
public string InsertedBy { get; set; }


[MaxLength(450), Display(Name = "UpdatedBy")]
public string UpdatedBy { get; set; }




[Display(Name = "زمان ایجاد:")]
[DefaultValue("CONVERT(DATETIME, CONVERT(VARCHAR(20),GetDate(), 120))")]
public DateTime InsertDate { get; set; }




[MaxLength(450), Display(Name = "بروز رسانی کننده:")]
public string UpdateBy { get; set; }


[Display(Name = "زمان بروزرسانی:")]
public DateTime? UpdateDate { get; set; }


[MaxLength(450), Display(Name = "حذف کننده:")]
public string DeleteBy { get; set; }


[Display(Name = "زمان حذف:")]
public DateTime? DeleteDate { get; set; }


[Display(Name = "وضعیت فعال / غیر فعال"), JsonPropertyName("IsActive")]
public bool IsActive { get; set; }


[Timestamp, JsonIgnore()]
public byte[] RowVersion { get; set; }


public string GuidClient { get; set; }


public string GuidController { get; set; }


[MaxLength(50)]
public string InsertIpAddress { get; set; }

[MaxLength(50)]
public string UpdateIpAddress { get; set; }
}




ممکنه سوال بشه که چرا UpdateBy , UpdateDate روی همه رکوردها مجدد مقدار دهی میشه! دلیلش این هست که من میخواهم هر کاربری که توی فرم داره کار میکنه هم بتونه ببینه این رکورد توسط چه کسی ایجاد شده و هم آخریش ویرایش را بتواند ببیند.
اون جدول AuditEntry که بعد از هر ویرایش ساخته میشه در واقع یک قسمت مدیریتی هست و قرار نیست که کاربران عادی بهش دسترسی داشته باشند ولی برای اینکه مراجعه به مدیریت را کم کنم این فیلدها رو هم قرار دادم.

امیدوارم که شفاف توضیح داده باشم و کمکی کرده باشم.
موفق باشید