- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Auditor Question
Joined: 23-Sep-2010
I've recently taken over a solution that uses LLBLGen Pro and I've got a question about the Auditor. It was commented out in the code, and I need to utilize it for a feature. I've un-commented it, and can step through it. I have also added a call to "AuditInsertOfNewEntity(IEntityCore entity)" on a method that creates a new entity - I've tried calling the Editor before and after the Entity.Save(), and then I step through the auditor, but it doesn't seem to save it out to the AuditInfo table.
Any ideas?
public class GeneralAuditor : AuditorBase
{
private enum AuditType
{
DeleteOfEntity = 1,
DirectDeleteOfEntities,
DirectUpdateOfEntities,
DereferenceOfRelatedEntity,
ReferenceOfRelatedEntity,
EntityFieldSet,
InsertOfNewEntity,
UpdateOfExistingEntity
}
private List<AuditInfoEntity> _auditInfoEntities;
/// <summary>CTor </summary>
public GeneralAuditor()
{
_auditInfoEntities = new List<AuditInfoEntity>();
}
/// <summary>Audits the successful delete of an entity from the database</summary>
/// <param name="entity">The entity which was deleted.</param>
/// <remarks>As the entity passed in was deleted succesfully, reading values from the
/// passed in entity is only possible in this routine. After this call, the
/// state of the entity will be reset to Deleted again and reading the fields
/// will result in an exception. It's also recommended not to reference
/// the passed in entity in any audit entity you might want to persist as the entity
/// doesn't exist anymore in the database.</remarks>
public override void AuditDeleteOfEntity(IEntityCore entity)
{
AuditInfoEntity auditInfo = new AuditInfoEntity();
auditInfo.ActionBy = FSPrincipal.GetCurrentPricipal().UserGuid;
auditInfo.CustomerGuid = FSPrincipal.GetCurrentPricipal().CustomerGuid;
IEntity obj = (IEntity)entity;
try
{
if (obj.Fields["Guid"] != null)
auditInfo.RequestGuid = (Guid)obj.Fields["Guid"].CurrentValue;
}
catch (Exception ee)
{
}
auditInfo.AffectedEntityName = entity.LLBLGenProEntityName;
auditInfo.ActionDateTime = DateTime.Now;
auditInfo.ActionType = (int)AuditType.DeleteOfEntity;
_auditInfoEntities.Add(auditInfo);
}
/// <summary>Audits the successful dereference of related entity from the entity passed in.</summary>
/// <param name="entity">The entity of which the related entity was dereferenced from.</param>
/// <param name="relatedEntity">The related entity which was dereferenced from entity</param>
/// <param name="mappedFieldName">Name of the mapped field onto the relation from entity to related
/// entity for which the related entity was dereferenced.</param>
public override void AuditDereferenceOfRelatedEntity(IEntityCore entity, IEntityCore relatedEntity,
string mappedFieldName)
{
AuditInfoEntity auditInfo = new AuditInfoEntity();
auditInfo.ActionBy = FSPrincipal.GetCurrentPricipal().UserGuid;
auditInfo.CustomerGuid = FSPrincipal.GetCurrentPricipal().CustomerGuid;
IEntity obj = (IEntity)entity;
try
{
if (obj.Fields["Guid"] != null)
auditInfo.RequestGuid = (Guid)obj.Fields["Guid"].CurrentValue;
}
catch (Exception ee)
{
}
auditInfo.AffectedEntityName = entity.LLBLGenProEntityName;
auditInfo.ActionDateTime = DateTime.Now;
auditInfo.ActionType = (int)AuditType.DereferenceOfRelatedEntity;
auditInfo.ActionData = string.Format("RelatedEntityName: {0}. MappedFieldName: {1}",
relatedEntity.LLBLGenProEntityName, mappedFieldName);
_auditInfoEntities.Add(auditInfo);
}
/// <summary>Audits the successful insert of a new entity into the database.</summary>
/// <param name="entity">The entity saved successfully into the database.</param>
public override void AuditInsertOfNewEntity(IEntityCore entity)
{
AuditInfoEntity auditInfo = new AuditInfoEntity();
auditInfo.ActionBy = FSPrincipal.GetCurrentPricipal().UserGuid;
auditInfo.CustomerGuid = FSPrincipal.GetCurrentPricipal().CustomerGuid;
IEntity obj = (IEntity)entity;
try
{
if (obj.Fields["Guid"] != null)
auditInfo.RequestGuid = (Guid)obj.Fields["Guid"].CurrentValue;
}
catch (Exception ee)
{
}
auditInfo.AffectedEntityName = entity.LLBLGenProEntityName;
auditInfo.ActionDateTime = DateTime.Now;
auditInfo.ActionType = (int)AuditType.InsertOfNewEntity;
_auditInfoEntities.Add(auditInfo);
}
/// <summary>
/// Audits the successful reference of related entity from the entity passed in.
/// </summary>
/// <param name="entity">The entity of which the related entity was dereferenced from.</param>
/// <param name="relatedEntity">The related entity which was dereferenced from entity</param>
/// <param name="mappedFieldName">Name of the mapped field onto the relation from entity to related
/// entity for which the related entity was referenced.</param>
public override void AuditReferenceOfRelatedEntity(IEntityCore entity, IEntityCore relatedEntity,
string mappedFieldName)
{
AuditInfoEntity auditInfo = new AuditInfoEntity();
auditInfo.ActionBy = FSPrincipal.GetCurrentPricipal().UserGuid;
auditInfo.CustomerGuid = FSPrincipal.GetCurrentPricipal().CustomerGuid;
IEntity obj = (IEntity)entity;
try
{
if (obj.Fields["Guid"] != null)
auditInfo.RequestGuid = (Guid)obj.Fields["Guid"].CurrentValue;
}
catch (Exception ee)
{
}
auditInfo.AffectedEntityName = entity.LLBLGenProEntityName;
auditInfo.ActionDateTime = DateTime.Now;
auditInfo.ActionType = (int)AuditType.ReferenceOfRelatedEntity;
auditInfo.ActionData = string.Format("RelatedEntityName: {0}. MappedFieldName: {1}",
relatedEntity.LLBLGenProEntityName, mappedFieldName);
_auditInfoEntities.Add(auditInfo);
}
/// <summary>
/// Audits the successful update of an existing entity in the database
/// </summary>
/// <param name="entity">The entity updated successfully in the database.</param>
public override void AuditUpdateOfExistingEntity(IEntityCore entity)
{
AuditInfoEntity auditInfo = new AuditInfoEntity();
auditInfo.ActionBy = FSPrincipal.GetCurrentPricipal().UserGuid;
auditInfo.CustomerGuid = FSPrincipal.GetCurrentPricipal().CustomerGuid;
IEntity obj = (IEntity)entity;
try
{
if (obj.Fields["Guid"] != null)
auditInfo.RequestGuid = (Guid)obj.Fields["Guid"].CurrentValue;
}
catch (Exception ee)
{
}
auditInfo.AffectedEntityName = entity.LLBLGenProEntityName;
auditInfo.ActionDateTime = DateTime.Now;
auditInfo.ActionType = (int)AuditType.UpdateOfExistingEntity;
_auditInfoEntities.Add(auditInfo);
}
/// <summary>
/// Gets the audit entities to save. Audit entities contain the audit information stored
/// inside this auditor.
/// </summary>
/// <returns>The list of audit entities to save, or null if there are no audit entities to save</returns>
/// <remarks>Do not remove the audit entities and audit information from this auditor when this method is
/// called, as the transaction in which the save takes place can fail and retried which will result in
/// another call to this method</remarks>
public override IList GetAuditEntitiesToSave()
{
return _auditInfoEntities;
}
/// <summary>
/// The transaction with which the audit entities requested from GetAuditEntitiesToSave were saved.
/// Use this method to clear any audit data in this auditor as all audit information is persisted
/// successfully.
/// </summary>
public override void TransactionCommitted()
{
_auditInfoEntities.Clear();
}
}
Update: It looks like I might have to iterated through the IList of AuditInfoEntities to save them one at a time.
Joined: 23-Sep-2010
daelmo wrote:
Sorry but I don't understand the problem. Is that the audit info entities doenst get persisted?
GetAuditEntitiesToSave() should do the work.
Please post more info.
You're right - I didn't realize I had to get the list, iterate through and save the List - I thought it happened automatically.. so that's why it wasn't saving. I am able to save now.
However, I have a question about order of execution;
Should I make a call to the Auditor before or after the Entity save? I'm guessing before, so the auditor can determine what is about to change.. then after the save, commit the audit.. or something like that. Is that right?
Thanks for the help,
Jay
Follow up question: It doesn't look like the GeneralAuditor is built to automatically audit/log every field change that occurs on an Update.. is that right? It looks I need to build a custom process to handle this... is there a generic way to determine what fields have changed? Or do I need to look up the entity to compare field by field with what is passed in to the Auditor method?
Joined: 23-Sep-2010
daelmo wrote:
Sorry but I don't understand the problem. Is that the audit info entities doenst get persisted?
GetAuditEntitiesToSave() should do the work.
Please post more info.
Let me back up.. the GeneralAuditor isn't being called automatically. I've found that creating a utility to create an instance of GeneralAuditor and calling it works, but I've read that this can be configured to be called automatically by all entity save events. How do I do that exatcly?
Is it just by adding this?
<add key="autoDependencyInjectionDiscovery" value="true"/>
Thanks,
Jay
Hi there,
First of all, your class (at least the code you posted) doesn't have any injection information. It should look like this:
[DependencyInjectionInfo(typeof(IEntity2), "AuditorToUse")]
[Serializable]
public class GeneralAuditor : AuditorBase
{
...
You also have other options to set your Auditor: Setting AuditorToUse in your entity, or override the CreateAuditor method on your entity (or EntityBase2).
You don't have to save anything in the Auditor, when you return the list of entities in the GetAuditEntitiesToSave, the entities are auto persisted.
If you want to see an example of this, please download the Auditor example from LLBLGen site.
Joined: 23-Sep-2010
daelmo wrote:
Hi there,
First of all, your class (at least the code you posted) doesn't have any injection information. It should look like this:
[DependencyInjectionInfo(typeof(IEntity2), "AuditorToUse")] [Serializable] public class GeneralAuditor : AuditorBase { ...
You also have other options to set your Auditor: Setting AuditorToUse in your entity, or override the CreateAuditor method on your entity (or EntityBase2).
You don't have to save anything in the Auditor, when you return the list of entities in the GetAuditEntitiesToSave, the entities are auto persisted.
If you want to see an example of this, please download the Auditor example from LLBLGen site.
Thanks, David
I read that about Entity2 in the DependencyInjectionInfo in the Online documentation. But with the info in the doc, I don't see what / how to enable an entity to be auditable.
How would I make an entity automatically audit (ie Setting AuditorToUse in your entity). I can't find an example in the doc.
I'll I really have a handle on is;
- Where the Entity is changed/saved through out the business application
- Where the entity class files are
- Where the GeneralAuditor class is
So is it possible to configure these 3 files to get going with Auditing?
Thanks for the help,
Jay
First thing you need to do is read about DI
Then you need to read Setting up and using Auditing
As David said, you enable an entity to be Audited by just decorating the Auditor class with the correct attribute.
If you don't want to Audit all entities, and just want to audit one entity (eg. CustomerEntity)
Do the following:
[DependencyInjectionInfo(typeof(CustomerEntity), "AuditorToUse")]
[Serializable]
public class GeneralAuditor : AuditorBase
{
...
}
The attribute can be repeated to use with other entities as well.
Joined: 23-Sep-2010
Thanks - I was able to get this going.
I found by debugging to see exactly what "AuditorToUse" value was being set on the entity property by the DependencyInjection, and found another auditor class in this solution that is using it - which was targeting all entities (IEntity) for injection.
It looks like competing DependencyInjection attributes will default to 1 of the Auditor classes, but it's not clear which one - I was looking for a generic auditor to defer to a specifically typed Auditor instead of using the generic one.
I updated that class to have attributes for only the entities I think that class is concerned with, created a new auditor class for the specific entity I'm trying to audit, and it's hitting it.
Thanks for the help,
Jay
Joined: 23-Sep-2010
daelmo wrote:
Jay. Good to hear you got that working. Come back here if you need further help
![]()
Thanks..
I have been issues with the DBValue and the CurrentValue on the IEntity object. The code below works, but I had to do a lot of trial and error. I found that if I accessed the obj, or the entity while moving through the fields, it would commit the entity to the database and from then on all current and dbvalues were the same.
But the auditor works great for field by field audit trails..
public override void AuditUpdateOfExistingEntity(IEntityCore entity)
{
try
{
IEntity obj = (IEntity)entity;
string entityname = obj.LLBLGenProEntityName;
ArrayList fieldchanges = new ArrayList();
switch (entityname)
{
case "PersonEntity":
{
PersonEntity person = (PersonEntity)obj;
foreach (object o in obj.Fields)
{
try
{
string fieldname = ((IEntityFieldCore)o).Name;
string currentvalue = ((IEntityFieldCore)o).CurrentValue.ToString();
string dbvalue = ((IEntityFieldCore)o).DbValue.ToString();
if (currentvalue != dbvalue)
{
string[] changes = entityname,fieldname,dbvalue,currentvalue};
fieldchanges.Add(changes);
}
}
catch
{
//old or new field might be null
}
}
foreach (string[] change in fieldchanges)
{
AddAuditInfo(AuditType.UpdateOfExistingEntity, "StudentMedical", change[0], change[1], change[2], change[3], semaForm.Guid);
}
}
break;
}
}
catch(Exception e)
{
Utilities.AddLogEntry("Audit Failed", e.Message, LogLevel.Error);
}
}
I understand your concern. That however (AuditUpdateOfExistingEntity) is not the ideal place to track field changes. Remember that AuditUpdateOfExistingEntity is called just after the entity is saved (no tx commited, but the entity is already saved). So, in AuditUpdateOfExistingEntity you have to set some entity specific info, not field specific info.
So the ideal setup is that you track field changes through AuditOfEntityFieldSet. Please see the Auditor example to see the ideal way to audit field changes.
Joined: 23-Sep-2010
daelmo wrote:
I understand your concern. That however (AuditUpdateOfExistingEntity) is not the ideal place to track field changes. Remember that AuditUpdateOfExistingEntity is called just after the entity is saved (no tx commited, but the entity is already saved). So, in AuditUpdateOfExistingEntity you have to set some entity specific info, not field specific info.
So the ideal setup is that you track field changes through AuditOfEntityFieldSet. Please see the Auditor example to see the ideal way to audit field changes.
Humm.. using the entity auditor seems to work in the state it's in, but was delicate to not touch something to refresh the dbvalue values - I'm hoping that it's stable and it's not an issue of timing or something like that.. I need to roll this feature out tomorrow.
But sounds like I need to redo it using the field auditing technique.. but I can't find it - I'm looking at this link http://www.llblgen.com/documentation/3.0/LLBLGen%20Pro%20RTF/hh_start.htm#Using%20the%20generated%20code/gencode_auditing.htm
Is there a different example?
Thanks,
Jay
From the Reference Manual, AuditorBase class has the following method:
public virtual void AuditEntityFieldSet(
IEntityCore entity,
int fieldIndex,
object originalValue
)
This you will have to override to catch EntityField's changes.
Joined: 23-Sep-2010
Walaa wrote:
From the Reference Manual, AuditorBase class has the following method:
public virtual void AuditEntityFieldSet( IEntityCore entity, int fieldIndex, object originalValue )
This you will have to override to catch EntityField's changes.
Thanks, That's perfect..