问题
I have an entity model that has audit information on every table (50+ tables)
CreateDate
CreateUser
UpdateDate
UpdateUser
Currently we are programatically updating audit information.
Ex:
if(changed){
entity.UpdatedOn = DateTime.Now;
entity.UpdatedBy = Environment.UserName;
context.SaveChanges();
}
But I am looking for a more automated solution. During save changes, if an entity is created/updated I would like to automatically update these fields before sending them to the database for storage.
Any suggestion on how i could do this? I would prefer to not do any reflection, so using a text template is not out of the question.
A solution has been proposed to override SaveChanges and do it there, but in order to achieve this i would either have to use reflection (in which I don't want to do ) or derive a base class. Assuming i go down this route how would I achieve this?
For example
EXAMPLE_DB_TABLE
CODE
NAME
--Audit Tables
CREATE_DATE
CREATE_USER
UPDATE_DATE
UPDATE_USER
And if i create a base class
public abstract class IUpdatable{
public virtual DateTime CreateDate {set;}
public virtual string CreateUser { set;}
public virtual DateTime UpdateDate { set;}
public virtual string UpdateUser { set;}
}
The end goal is to be able to do something like...
public overrride void SaveChanges(){
//Go through state manager and update audit infromation
//FOREACH changed entity in state manager
if(entity is IUpdatable){
//If state is created... update create audit.
//if state is updated... update update audit
}
}
But I am not sure how I go about generating the code that would extend the interface.
回答1:
Here was the final solution.
I basically check my model for the presence of 4 properties. If the entity contains
InsertedOn and InsertedBy and UpdatedOn and UpdatedBy I generate (using a tt file) the following entity that implements an IAuditable interface.
public interface IAuditable
{
void SetInsertedOn(DateTime date);
void SetInsertedBy(string user);
void SetUpdatedOn(DateTime date);
void SetUpdatedBy(string user);
}
public partial class ExampleDBtable: EntityObject, Audit.IAuditable
{
//Normal EDMX stuff here..
public static ExampleDBtable CreateExampleDBtable(int id, string code, string name, DateTime insertedOn, string insertedBy, DateTime updatedOn, string updatedBy)
{
}
//Extension points.
#region IAuditable Members
public void SetInsertedOn(DateTime date)
{
this._InsertedOn = date;
}
public void SetInsertedBy(string user)
{
this._InsertedBy = user;
}
public void SetUpdatedOn(DateTime date)
{
this._UpdatedOn = date;
}
public void SetUpdatedBy(string user)
{
this._UpdatedBy = user;
}
#endregion
}
I also generate code to handle the updating of audit information
public ExampleDBObjectContext(string connectionString) : base(connectionString, "publicExampleDBObjectContext")
{
this.SavingChanges += new EventHandler(UpdateAuditInformation);
this.OnContextCreated();
}
And then finally I implemented the UpdateAuditInformation
foreach (ObjectStateEntry entry in
context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
//Start pseudo code..
if(entry.Entity is IAuditable){
IAuditable temp = entry.Entity as IAuditable;
temp.SetInsertedOn(DateTime.Now);
temp.SetInsertedBy(Env.GetUser());
...
}
}
I opted not to show the .tt file because it looks horrible in the editor.
回答2:
Yes, I think you can do something like this. The general idea would be
- Handle ObjectContext.SavingChanges.
- In that event, call ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)
- Iterate the results and set properties if needed.
回答3:
Look to PostSharp. Very nice solution for such tasks.
回答4:
Is it not simpler to 'stamp' the updated rows via database triggers? Are the information provided in the database not adequate enough? (database user vs. os user)
来源:https://stackoverflow.com/questions/2588544/extend-base-type-and-automatically-update-audit-information-on-entity