2014-01-13 30 views
1

我正在使用實體框架來創建審計線索。而不是審計的每個屬性,我想我會創建一個自定義屬性如何在實體框架5中使用Attribute.IsDefined?

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
    public class DoNotAudit : Attribute 
    { 
    } 

然後,我會在我的代碼將此我的模型

[Table("AuditZone")] 
    public class AuditZone 
    { 
     public AuditZone() 
     { 
      AuditZoneUploadedCOESDetails = new List<UploadedCOESDetails>(); 
      AuditZonePostcode = new List<Postcodes>(); 
     } 

     [Key] 
     public int Id { get; set; } 
     public string Description { get; set; }  
     public bool Valid { get; set; }  

     public DateTime CreatedDate { get; set; } 
     public int? CreatedBy { get; set; } 
     [DoNotAudit] 
     public DateTime? ModifiedDate { get; set; } 
     public int? ModifiedBy { get; set; } 

     public virtual UserProfile CreatedByUser { get; set; } 
     public virtual UserProfile ModifiedByUser { get; set; } 

     public virtual ICollection<UploadedCOESDetails> AuditZoneUploadedCOESDetails { get; set; } 
     public virtual ICollection<Postcodes> AuditZonePostcode { get; set; } 
    } 

然後審計跟蹤

// This is overridden to prevent someone from calling SaveChanges without specifying the user making the change 
     public override int SaveChanges() 
     { 
      throw new InvalidOperationException("User ID must be provided"); 
     } 
     public int SaveChanges(int userId) 
     { 
      // Get all Added/Deleted/Modified entities (not Unmodified or Detached) 
      foreach (var ent in this.ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Added || p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified)) 
      { 
       // For each changed record, get the audit record entries and add them 
       foreach (AuditLog x in GetAuditRecordsForChange(ent, userId)) 
       { 
        this.AuditLogs.Add(x); 
       } 
      } 

      // Call the original SaveChanges(), which will save both the changes made and the audit records 
      return base.SaveChanges(); 
     } 

     private List<AuditLog> GetAuditRecordsForChange(DbEntityEntry dbEntry, int userId) 
     { 
      List<AuditLog> result = new List<AuditLog>(); 

      DateTime changeTime = DateTime.UtcNow; 

      // Get the Table() attribute, if one exists 
      //TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute; 

      TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), true).SingleOrDefault() as TableAttribute; 

      // Get table name (if it has a Table attribute, use that, otherwise get the pluralized name) 
      string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name; 

      // Get primary key value (If you have more than one key column, this will need to be adjusted) 
      var keyNames = dbEntry.Entity.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).ToList(); 

      string keyName = keyNames[0].Name; 

      var test = dbEntry.Entity.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Count() > 0).ToList(); 



      // //dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name; 

      if (dbEntry.State == System.Data.EntityState.Added) 
      { 
       // For Inserts, just add the whole record 
       // If the entity implements IDescribableEntity, use the description from Describe(), otherwise use ToString() 

       foreach (string propertyName in dbEntry.CurrentValues.PropertyNames) 
       { 


        result.Add(new AuditLog() 
        { 
         AuditLogId = Guid.NewGuid(), 
         UserId = userId, 
         EventDateUTC = changeTime, 
         EventType = "A", // Added 
         TableName = tableName, 
         RecordId = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(), 
         ColumnName = propertyName, 
         NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString() 
        } 
          ); 
       } 
      } 
      else if (dbEntry.State == System.Data.EntityState.Deleted) 
      { 
       // Same with deletes, do the whole record, and use either the description from Describe() or ToString() 
       result.Add(new AuditLog() 
       { 
        AuditLogId = Guid.NewGuid(), 
        UserId = userId, 
        EventDateUTC = changeTime, 
        EventType = "D", // Deleted 
        TableName = tableName, 
        RecordId = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(), 
        ColumnName = "*ALL"//, 
        // NewValue = (dbEntry.OriginalValues.ToObject() is IDescribableEntity) ? (dbEntry.OriginalValues.ToObject() as IDescribableEntity).Describe() : dbEntry.OriginalValues.ToObject().ToString() 
       } 
        ); 
      } 
      else if (dbEntry.State == System.Data.EntityState.Modified) 
      { 
       foreach (string propertyName in dbEntry.OriginalValues.PropertyNames) 
       { 

        var doNotAUditDefined = dbEntry.Property(propertyName).GetType().GetCustomAttributes(typeof(DoNotAudit), false); 

        // var test1 = dbEntry.Property(propertyName).GetType().Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Count() > 0).ToList(); 

       // var test = dbEntry.Entity.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Count() > 0).ToList(); 


        // For updates, we only want to capture the columns that actually changed 
        if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName))) 
        { 
         result.Add(new AuditLog() 
         { 
          AuditLogId = Guid.NewGuid(), 
          UserId = userId, 
          EventDateUTC = changeTime, 
          EventType = "M", // Modified 
          TableName = tableName, 
          RecordId = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(), 
          ColumnName = propertyName, 
          OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? null : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(), 
          NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString() 
         } 
          ); 
        } 
       } 
      } 
      // Otherwise, don't do anything, we don't care about Unchanged or Detached entities 

      return result; 
     } 

在修改部分我有以下代碼行

var doNotAUditDefined = dbEntry.Property(propertyName).GetType().GetCustomAttributes(typeof(DoNotAudit), false); 

當我逐句通過代碼時,即使對於modifiedDate屬性,它也顯示爲空。怎麼可能?任何幫助表示讚賞

感謝

回答

2

你用下面的代碼得到什麼:

dbEntry.Property(propertyName).GetType() 

是修改後的財產在ModifiedType的情況下的類型,比如DateTime?。因此在DateTime?類中沒有定義屬性。 (因爲屬性是在你的AuditZone類中定義的)

我要做的是保存不應審計的屬性列表,然後再輸入修改的審計代碼部分(至少在循環修改列表之前屬性)。然後,循環修改的屬性,檢查屬性名稱是否在審計中排除的屬性列表中。事情是這樣的:

var auditExcludedProps = dbEntry.Entity.GetType() 
             .GetProperties() 
             .Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Any()) 
             .Select(p => p.Name) 
             .ToList(); 

foreach (string propertyName in dbEntry.OriginalValues.PropertyNames) 
{ 

    var doNotAUditDefined = auditExcludedProps.Contains(propertyName); 

    ... 
} 

您可能要仔細檢查dbEntry.Entity.GetType()收益類AuditZone和列表auditExcludedProps包含ModifiedDate財產。

希望它能幫助!

+0

是的,這將工作..但還有另一種稱爲dbEntry.member(屬性名稱)的方法,並再次沒有提供任何東西。所以只需要通過在屬性上使用自定義屬性來清除,就沒有辦法檢查它了嗎? – user2206329

+0

對於'ModifiedDate',這會給你一個'DbPropertyEntry'對象,請參閱[mdsn](http://msdn.microsoft.com/zh-cn/library/system.data.entity.infrastructure.dbentityentry。構件(v = vs.113)的.aspx)。因此,您將與使用'dbEntry.Property(propertyName)'時相同的場景' –

+0

檢查自定義屬性的方法是獲取用於聲明該屬性('ModifiedDate')的類的類型('AuditZone')自定義屬性。這意味着反射需要開始獲取AuditZone的類型信息,然後查看其屬性。 EF似乎沒有提供一種獲得'PropertyInfo'(這將允許您檢查自定義屬性)的方式,給定一個'DbPropertyEntry'對象。 –