问题描述
我在执行我的计划时遇到了一些错误,如下所述.在这一点上,我对解决特定错误不太感兴趣,因为我很关心这是否是一个好主意.
I am running into some errors implementing my plan as described below. I am not so interested at this point in resolving particular errors as I am in whether or not this is a good idea.
所有具有历史功能的对象都来自具有单个属性的公共类 AuditableObject
public Guid ID { get;放;}
.
All history-capable objects descend from a common class AuditableObject
with a single property public Guid ID { get; set; }
.
后代可能是:
public class Taco : AuditableObject { public string Seasoning { get;放;} }
现在,我想实现保存事件处理程序来写入下表(类)
Now, I would like to implement the save event handler to write to the following table (class)
public class AuditItem
{
public Guid ID { get; set; }
public virtual AuditableObject Object { get; set; }
public string ObjectClassName { get; set; } //ugly
public string OldObjectXMLData { get; set; }
public string NewObjectXMLData { get; set; }
public DateTime Timestamp { get; set; }
}
我不确定是否需要 ObjectClassName
,因为我可以在运行时检查对象的类型,但它在那里以防万一.
I am not sure if I need the ObjectClassName
as I can check the type of the object at runtime but it's there just in case.
在保存时,我基本上会将对象前后序列化为相应的属性,保存时间戳,并设置对象 - 即映射 FK.
On save I would basically serialize the object before and after to the respective properties, save a timestamp, and set the object - ie map the FK.
这是一种丑陋的方法吗?像我这样使用 EF Code First 从单个类下降是否有任何明显的缺点?
Is this an ugly way to go about it? Are there any obvious drawbacks to descending from a single class with EF Code First as I am doing?
推荐答案
我认为在脱盐时您将需要对象类型的全限定名称,因此这将是强制性的.
I think you will need the full-qualified-name of the object's type when desalinizing, so that will be mandatory.
或者序列化对象会给你带来问题.
假设我们要使用approch审计Taco类的TacoOject1,序列化后的数据会放到数据库中,后面因为业务变化需要给Taco增加一个属性,重新编译后需要反序列化TacoOject1我们将得到 TypeMissMatchException
(不确定异常名称).
Alternatively serializing the object will be cause you problems.
Assume that we are going to audit TacoOject1 of Taco class using the approch, the serialized data will be put in data base, later due to business changes we need to add another property to Taco, after recompilation when we need to deserilazed TacoOject1 we will get TypeMissMatchException
(not sure of exception name).
另一个设计反对意见是在审计过程中使用继承.
第一:实际上 Taco 不是
AuditableObject ,它是由 Taco 播放的卷,使用继承将违反 Liskov 替换原则.
第二:不能使用多重继承,想想如果我们有一个TacoSupperClass,那我们怎么审计Taco呢?
Another design objection is using inheritance for audit process.
First: In reality Taco is not a
AuditableObject , Its a roll played by Taco, using inheritance will violate Liskov Substitution Principle.
Second: You can not use multiple inheritance, think that if we had a TacoSupperClass, how we could audit Taco then?
如果我要去设计审计流程,我会使用 实体-属性-值模型
使AuditItem
成为标记界面,并将其重命名为IAuditableEntity
.
拥有名为 AuditableProperty
的属性将增强我们的流程.
任何需要被审计的实体都会被标记为IAuditableEntity,任何需要参与审计的实体的属性都会被标记为AuditableProperty 属性.
If I where going to design auditing process, I would use Entity–attribute–value model
Making AuditItem
a marker interface and rename it to IAuditableEntity
.
Having an attribute called AuditableProperty
would enhance our process.
Any entity needs to be audited will be marked by IAuditableEntity, any property of the entity needed to be partcipated in audit will be marked by AuditableProperty attribute.
public class Taco : IAuditableEntity
{
[AuditableProperty]
public string Seasoning { get; set; }
[AuditableProperty]
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
}
AuditLog
表将包含以下列:
1. EntityFullTypeName
: (String) 我们将审核不同的实体,该字段将用于获取有意义的报告.(强制)
2. ObjectIdentifier
:被操作的实体标识符,实体的主键或业务键.
3. FieldName
:(字符串)实体字段名称.
4. OldValue
:(字符串)实体字段旧值.
5. NewValue
:(字符串)实体字段新值.
6. TransactionUser
:进行更改的应用程序用户.(必填)
7. TransactionID
:任何改变实体的操作都需要有一个唯一的交易ID(如GUID)(强制),如果实体更新改变多个字段,这些列将是关键指向跟踪更新中的所有更改(事务)
8. ChangeDate
:交易日期.(必填)
9. FieldType
:枚举或文本显示字段类型,如 TEXT 或 Double.(必填)
The AuditLog
table will have these columns:
1. EntityFullTypeName
: (String) We are going to audit different entities, the field will be used to get meaningful reports .(mandatory)
2. ObjectIdentifier
: Entity identifier that is being manipulated, primary key or business key of the entity.
3. FieldName
: (String) Entity field name.
4. OldValue
: (String) Entity field old value.
5. NewValue
: (String) Entity field new value.
6. TransactionUser
: Application user that makes the change. (mandatory)
7. TransactionID
: Any operation changing the entities will need to have a unique transaction ID (like GUID) (mandatory), In case of an update on an entity changing multiple fields,these column will be the key point to trace all changes in the update(transcation)
8. ChangeDate
: Transaction date. (mandatory)
9. FieldType
: enumeration or text showing the field type like TEXT or Double. (mandatory)
在服务层,当 Taco1 将被更新(或插入)时,我们将使用反射检查 Taco1 类型是否被 IAuditableEntity 标记(使用惰性 chash 存储反射数据),如果是,哪些属性已被更改(我们需要一个单独的数据库调用来获取旧值).
例如:
In service layer when Taco1 is going to be updated(or inserted) we will check if Taco1 type is marked by IAuditableEntity using reflection(using a lazy chash to store reflection data), if so which properties have been changed(we need a separate DB call to fetch old values).
e.g :
Taco1 = new Taco();
Taco1.Seasoning = "old Seasoning value";
Taco1.OtherProperty1 = "Old Other Property1 value";
Taco1.OtherProperty2 = "Old Other Property2 value";
之前保存,现在更新:
Saved before,now updating:
Taco1.Seasoning = "New Seasoning value";
Taco1.OtherProperty1 = "New Other Property1 value";
Taco1.OtherProperty2 = "New Other Property2 value";
我们将在 AuditLog 中插入两条具有相同 TransactionID 的记录:
We will insert two records in AuditLog with the same TransactionID:
采用这种方法
可以跟踪任何实体(表)
报告将是可读的
只会记录更改.
Having this approach
Any entity (table) could be traced
Reports will be readable
Only changes will be logged.
这篇关于EF Code-First 继承单个基类以实现简单的历史记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!