博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
EF Audit
阅读量:5034 次
发布时间:2019-06-12

本文共 9324 字,大约阅读时间需要 31 分钟。

需求比较复杂,真心不想多说。每次数据库操作都要记录原始值和新值到Audit表。而且其中一些外键字段不能存外键值而必须存其业务对应值,也就是其对应的导航属性的某个值

既然用了EF,哪个属性是普通属性,哪个是导航属性,哪个属性能对应到导航属性,都是可以得到的不是问题。但最终记录的是哪个导航属性的哪个值就不好说了,只能上配置信息。另外就是app规定不需要延迟加载,于是需要手动加载导航属性值

 

先定义一下配置信息相关类

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace EFAuditComponet{    public class AuditConfigItem    {        public string EntityTypeName { set; get; }        public string EntityPropertyName { set; get; }                public string TargetNavigationPropertyPath { set; get; }        public bool NeedToRecordAudit { set; get; }            }    public static class AuditConfigExtention    {        public static bool CheckContains(this List
list,string entityTypeFullName, string propertyName) { var result = list.GetConfigItem(entityTypeFullName, propertyName); if (result == null) return false; else return true; } public static AuditConfigItem GetConfigItem(this List
list, string entityTypeFullName, string propertyName) { return list.Find(p => p.EntityTypeName == entityTypeFullName && p.EntityPropertyName == propertyName); } }}
View Code

没啥好说的,POCO+扩展方法,简单好用。需要一提的是TargetNavigationPropertyPath的格式,是属性名.属性名.属性名....属性名。前面的都是导航属性的属性名,最后是实际值的属性名。具体用法后面示例中有。

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace EFAuditComponet{    public class EFAuditConfig    {        public static List
ConfigData = new List
(); }}
View Code

将配置信息存于一个静态List中,写个类是为了方便将来扩展

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace EFAuditComponet{    public class AuditRecord    {        public string PropertyName { set; get; }        public string OldValue { set; get; }        public string NewValue { set; get; }    }}
View Code

这个也没啥好说的,Audit记录POCO

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data.Entity;using System.Data.Entity.Infrastructure;using System.Data.Objects;using System.Data.Metadata.Edm;using System.Collections;namespace EFAuditComponet{    public class EFAuditManager    {        public void GetNavigationProperty
(DbContext context) where T : class { if (context == null) { throw new ArgumentNullException("context"); } ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext; var set = objContext.CreateObjectSet
(); var entitySet = set.EntitySet; Console.WriteLine("类型名称:"); Console.WriteLine(entitySet.ElementType.FullName); Console.WriteLine(entitySet.ElementType.Name); Console.WriteLine("属性名称:"); foreach (var pro in entitySet.ElementType.Properties) { Console.WriteLine(pro.Name); } Console.WriteLine("导航属性名称:"); foreach (var pro in entitySet.ElementType.NavigationProperties) { Console.WriteLine(pro.Name); } Console.WriteLine("成员名称:"); foreach (var pro in entitySet.ElementType.Members) { Console.WriteLine(pro.Name); } Console.WriteLine("属性与对应导航属性:"); foreach (var pro in entitySet.ElementType.NavigationProperties) { foreach (var fpro in pro.GetDependentProperties()) { Console.WriteLine(fpro.Name + ":" + pro.ToEndMember.Name); } } Console.ReadKey(); } public List
GetObjectDataAndIncludeDataByConfig
(DbContext context, T objInstance) where T : class { List
auditList = new List
(); ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext; var set = objContext.CreateObjectSet
(); var entitySet = set.EntitySet; Dictionary
naviCollection = new Dictionary
(); foreach (var pro in entitySet.ElementType.NavigationProperties) { foreach (var fpro in pro.GetDependentProperties()) { naviCollection.Add(fpro.Name, pro); } } foreach (var pro in entitySet.ElementType.Properties) { string propertyName = pro.Name; AuditConfigItem configItem = EFAuditConfig.ConfigData.GetConfigItem(entitySet.ElementType.Name, propertyName); if (configItem != null && !configItem.NeedToRecordAudit) { continue; } AuditRecord record = new AuditRecord(); DbEntityEntry
entry = Attach
(context, objInstance); //DbPropertyValues dbValues = entry.GetDatabaseValues(); object originalValue = entry.Property(propertyName).OriginalValue; object currentValue = entry.Property(propertyName).CurrentValue; if (configItem == null || string.IsNullOrEmpty(configItem.TargetNavigationPropertyPath)) { record.PropertyName = propertyName; record.OldValue = originalValue.ToString(); record.NewValue = currentValue.ToString(); } else { record.PropertyName = propertyName; entry.Property(propertyName).CurrentValue = originalValue; record.OldValue = LoadReferenceValue(context,entry, configItem.TargetNavigationPropertyPath); entry.Property(propertyName).CurrentValue = currentValue; record.NewValue = LoadReferenceValue(context,entry, configItem.TargetNavigationPropertyPath); } auditList.Add(record); } return auditList; } private string LoadReferenceValue(DbContext context,DbEntityEntry entry,string targetNavigationPropertyPath) { string returnValue = null; string[] propertyNames = targetNavigationPropertyPath.Split('.'); for (int i = 0; i < propertyNames.Length; i++) { if (i != propertyNames.Length - 1) { if (!entry.Reference(propertyNames[i]).IsLoaded) entry.Reference(propertyNames[i]).Load(); entry = context.Entry(entry.Reference(propertyNames[i]).CurrentValue); } else returnValue = entry.Property(propertyNames[i]).CurrentValue == null ? null : entry.Property(propertyNames[i]).CurrentValue.ToString(); } return returnValue; } public DbEntityEntry
Attach
(DbContext context, T obj) where T : class { if (context == null) { throw new ArgumentNullException("context"); } DbEntityEntry
entry; try { entry =context.Entry
(obj); entry.State = System.Data.EntityState.Modified; } catch { var set = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet
(); var entitySet = set.EntitySet; string[] keyNames = entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray(); string precidate = string.Empty; foreach (string filter in keyNames) { precidate += "it." + filter + "=" + context.Entry
(obj).Property(filter).CurrentValue.ToString(); } var entity = set.Where(precidate).First(); //context.ChangeTracker.Entries
().ToList().Remove(GetEntityKeyNames(context, pE); entry = context.Entry
(entity); entry.CurrentValues.SetValues(obj); } return entry; } }}
View Code

这个是真正的核心类,提供了所有我们需要的方法。方法名乱起了,其实第一个方法教你如何获得普通属性,如何获得导航属性,如何获得普通属性与导航属性的对应。

可以对照代码看看结果图,用PatronIdentification对象做的示例:

GetObjectDataAndIncludeDataByConfig则是真正的能将一个对象与Context中的对象比较,并得到AduitRecord集合。

这是修改了一个查询得到的对象后进行比对得到结果:

using (BE50Beta_AdminEntities1 context = new BE50Beta_AdminEntities1())            {                PatronIdentification result = context.PatronIdentification.Where(p => p.IdentificatinNumber == "aaaa").First();                result.CountryID = 2;                result.IdentificatinNumber = "1234567";                EFAuditManager manager = new EFAuditManager();                List
list = manager.GetObjectDataAndIncludeDataByConfig
(context, result); foreach (var record in list) { Console.WriteLine(record.PropertyName + " OldValue:" + record.OldValue + " NewValue:" + record.NewValue); } Console.ReadKey(); }
View Code

思路其实也很简单,遍历一个实体的普通属性,如果针对每一个属性,有一个配置信息存在且该配置信息的TargetNavigationPropertyPath不为空。那么就需要记录TargetNavigationPropertyPath所指的属性值而不是本身值

记录本身值无难度,难点在记录导航属性的值,可能会导航多次。

于是用了LoadReferenceValue,遍历TargetNavigationPropertyPath路径,在最后一个路径点去取出真正的值,之前只是将导航属性对象手动Load。

以下是模拟了有配置数据的情况,注意TargetNavigationPropertyPath的用法

EFAuditConfig.ConfigData.Add(new AuditConfigItem() { EntityTypeName = "PatronIdentification", EntityPropertyName = "CountryID", NeedToRecordAudit = true, TargetNavigationPropertyPath = "Country.CountryName" });            using (BE50Beta_AdminEntities1 context = new BE50Beta_AdminEntities1())            {                PatronIdentification result = context.PatronIdentification.Where(p => p.IdentificatinNumber == "aaaa").First();                result.CountryID = 2;                result.IdentificatinNumber = "1234567";                EFAuditManager manager = new EFAuditManager();                List
list = manager.GetObjectDataAndIncludeDataByConfig
(context, result); foreach (var record in list) { Console.WriteLine(record.PropertyName + " OldValue:" + record.OldValue + " NewValue:" + record.NewValue); } Console.ReadKey(); }
View Code

可以看到CountryID被成功替换成CountryName

转载于:https://www.cnblogs.com/vincentsun1234/archive/2013/05/28/3102892.html

你可能感兴趣的文章
MongoDB的查询索引
查看>>
u-boot简单学习笔记(二)——AR9331 uboot.lds分析
查看>>
文件操作:根据现有类生成所需要的类
查看>>
pdf中内嵌字体问题
查看>>
Debian搭建WordPress
查看>>
Xstream 解析xml文件内容
查看>>
JavaScript正则表达式方法总结
查看>>
pub/sub的实际应用总结
查看>>
多媒体标签
查看>>
TCP的应用编程服务器端重要笔记
查看>>
【题解】 Codeforces 919F A Game With Numbers(拓扑排序+博弈论+哈希)
查看>>
分布式技术追踪 2018年第二十一期
查看>>
random模块
查看>>
HTML5基础知识笔记(更新)
查看>>
windows 无法启动SQL Server FullText Search(MSSQLSERVER)服务
查看>>
js 获取get参数值(url参数)
查看>>
C++ STL内存池
查看>>
9.Hibernate 缓存
查看>>
Spring mvc使用不了jstl 或者 Spring mvc不解析jstl
查看>>
看看 Delphi XE2 为 VCL 提供的 14 种样式
查看>>