New Posts New Posts RSS Feed: Auditing
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Auditing

 Post Reply Post Reply
Author
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Topic: Auditing
    Posted: 24-May-2010 at 8:44am
I came down to the EntityModel creation of the AuditValue table. For some reason the table kept being created as a view. It seems that because the table did not have a Key but did have multiple columns that would not all null values that each of those columns became keys when the edmx version was created.
 
I added the Key to the Id column and all worked out.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2010 at 6:01pm
I posted the wrong code. I have just fixed the link.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2010 at 9:16am
I am attaching my example. It is using NWDB with AuditTables, mdf files included, and the project. http://ws2.orcities.org/CEMS/AuditExample.zip
You help is greatly appreciated.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 20-May-2010 at 7:51am
Ok. Here is what happens. (Includes testing code). Im working on recreating this with the nw database.
It seems as if the EntityManager in the EntityServerSaveInterceptor is not the same EntityManager attached to the entity being saved.
 
 

protected override bool ExecuteSave()

        {

            //Get changes in system to any entity base from AuditEntity

            //Returns the one change needed

            List<DomainModel.AuditEntity> List = new List<DomainModel.AuditEntity>();

            List.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Modified));

            List.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Added));

            List.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Deleted));

 

            foreach (DomainModel.AuditEntity anEnt in List)

            {

                //Call auditself to create audit trail -> See next method below

                anEnt.AuditSelf(Principal);

            }

 

            //Verify that the EntityManager has the newly created AuditValues

            //This returns 1. The original change. Does not include the auditvalue created.

            List<IdeaBlade.EntityModel.Entity>  eList = new List<IdeaBlade.EntityModel.Entity>();

            eList.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Modified));

            eList.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Added));

            eList.AddRange(EntityManager.FindEntities<DomainModel.AuditEntity>(IdeaBlade.EntityModel.EntityState.Deleted));

            foreach (IdeaBlade.EntityModel.Entity anEnt in eList)

            {

                object o = anEnt;

            }

 

 

            return base.ExecuteSave();

        }

 
 
      private static AuditValue Create(

        AuditArgs pArgs, AuditChangeType pChangeType, AuditConfig.AuditConfigProperty pAuditProperty)

      {

 

          //Check that the passed in EntityManager from the AuditEntity has the changes requested.

          //This does return the one change.

          System.Collections.Generic.List<IdeaBlade.EntityModel.Entity> aList = new List<IdeaBlade.EntityModel.Entity>();

          aList.AddRange(pArgs.AuditableEntity.EntityAspect.EntityManager.FindEntities<IdeaBlade.EntityModel.Entity>(IbEm.EntityState.Modified));

          aList.AddRange(pArgs.AuditableEntity.EntityAspect.EntityManager.FindEntities<IdeaBlade.EntityModel.Entity>(IbEm.EntityState.Added));

 

          AuditValue newAudit = pArgs.AuditableEntity.EntityAspect.EntityManager.CreateEntity<AuditValue>();

          // Must Generate an Id.

          newAudit.EntityAspect[AuditValue.EntityPropertyNames.Id] = GetNextAuditId();

          newAudit.EntityAspect.AddToManager();

          newAudit.EntityPropertyId = pAuditProperty.Id;

          newAudit.EntityPrimaryKey = pArgs.PrimaryKeyString;

          newAudit.ModTs = pArgs.AuditTs;

          newAudit.ModUserId = (int)pArgs.UserId;

          //Added for Audit tracking

          newAudit.Approved = pArgs.IsApproved;

 

          // Remember: DataSet's DataColumn name is same as AuditProperty.PropertyName

          string columnName = pAuditProperty.PropertyName;

 

          switch (pChangeType)

          {

              case AuditChangeType.Added:

                  newAudit.Operation = "A";

                  newAudit.NewValue = pArgs.AuditableEntity.EntityAspect[columnName].ToString();

                  break;

              case AuditChangeType.Deleted:

                  newAudit.Operation = "D";

                  newAudit.OldValue = pArgs.AuditableEntity.EntityAspect[columnName, IbEm.EntityVersion.Original].ToString();

                  break;

              case AuditChangeType.Modified:

                  newAudit.Operation = "M";

                  newAudit.NewValue = pArgs.AuditableEntity.EntityAspect[columnName].ToString();

                  newAudit.OldValue = pArgs.AuditableEntity.EntityAspect[columnName, IbEm.EntityVersion.Original].ToString();

                  break;

              default:

                  throw new InvalidOperationException("Unexpected change type creating Audit");

          }

 

          //reject new change if submitted change is not approved.

          if (!newAudit.Approved && pChangeType != AuditChangeType.Added)

          {

              pArgs.AuditableEntity.EntityAspect[columnName] = pArgs.AuditableEntity.EntityAspect[columnName, IbEm.EntityVersion.Original];

          }

 

          //Verify that the one addition has been added to the AuditEntity EntityManager.

          //Successfully does.

          aList = new List<IdeaBlade.EntityModel.Entity>();

          aList.AddRange(pArgs.AuditableEntity.EntityAspect.EntityManager.FindEntities<IdeaBlade.EntityModel.Entity>(IbEm.EntityState.Modified));

          aList.AddRange(pArgs.AuditableEntity.EntityAspect.EntityManager.FindEntities<IdeaBlade.EntityModel.Entity>(IbEm.EntityState.Added));

         

          return newAudit;

      }

 

Back to Top
ting View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 27-Mar-2009
Location: San Francisco
Posts: 427
Post Options Post Options   Quote ting Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2010 at 6:54pm
Let's double check a few things:
1)  You should only be using this.EntityManager (the one provided for you in the EntityServerSaveInterceptor).  The DefaultManager is a different instance and shouldn't be used for this.
2)  You should make any changes or additions before calling base.ExecuteSave(), since that base class call is what will commit the objects to the datastore.
3)  Before calling base.ExecuteSave(), let's confirm that all the Entities of interest are in a modified or added state.
 
If that doesn't help, post the code for ExecuteSave() and your audit method and we'll have a look.
 
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2010 at 11:39am

I have found what is causing the issue.

In EntityServerSaveInterceptor where I override ExecuteSave. I attempt to create the Audit trail. I use the EntityServerSaveInterceptor.EntityManager to find all modified/added/deleted records. This returns the correct amount. I then walk those entities and call the audit trail method I ported from my classic version.
 
If I walk the creation of the audit trail I see the audit value created. I see it attach to the entitymanager. I verify before leaving the create method that it is added to the entitymanager. When the  method returns to EntityServerSaveInterceptor.ExecuteSave the EntityManager says there is only one change, the orginal. I have tried passing in the EntityManager to the auditvalue to make sure it is the right entitymanager. It still only returns the original entity. I have tried verifing that DefaultManager and the EntityServerSaveInterceptor.EntityManager are the same and they are not.
 
Please help. I am very stuck until this is resolved.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 19-May-2010 at 9:59am
I get the following error after supplying my audit info.
 
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
 
To reproduce.
 
I made a change to an entity (Contact.FirstName) when saving it calls ExecuteSave which then calls my Audit method to create audit trail. This creates an AuditValue with the new info. After the audit values are created it finishes ExecuteSave successfully then throws the above error.
 
Modified Contact.FirstName - Successful
AuditValues Created and added to the Manager. - Successful
SaveChanges - Error
 
Back to Top
ting View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 27-Mar-2009
Location: San Francisco
Posts: 427
Post Options Post Options   Quote ting Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 7:07pm

Chuck - Those samples were for our DevForce Classic product which does not use the Entity Framework or LINQ, so the code won't run on DevForce 2009/2010.  The concepts however, may be worth taking a look at.  If you want to see them, someone in support can send them to you if you open a support request and reference this forum thread.

As for your code, that is one way to do it.  Another would be to archive (copy/insert) the old version of the object to a history table and record the user and time of the change.  Then you can perform structured queries on a user, time window, or get a change history for a particular record.  I'm no auditing expert, so someone else might want to chime in about what they have done.
 
Back to Top
chuckc View Drop Down
Groupie
Groupie


Joined: 27-Feb-2010
Posts: 54
Post Options Post Options   Quote chuckc Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 5:44pm
Some older postings for previous DF versions make reference to Audit examples, specifically "Abstract Classes" and another called "Keeping an Audit Trail".  I do not see any such examples in my full, paid subscription install.  Am I just missing them, or are those examples no longer included with DF 2010?  If they are still useful, where can I get them?
 
On a related note, I've started to put together some audit logging code, shown below.  This will end up server side at some point, and only applied to a subset of my entities that implement an IAuditable interface.
 
Any suggestions or observations? 
 

static void DefaultEntityManager_Saving(object sender, EntitySavingEventArgs e)

{

 

    foreach (EntityBase entity in e.Entities)

    {

        // Build up change description

        StringBuilder sb = new StringBuilder();

        foreach (DataEntityProperty ep in entity.EntityAspect.EntityMetadata.DataProperties)

        {

            object currentValue = ep.GetValue(entity, EntityVersion.Current);

            object originalValue = ep.GetValue(entity, EntityVersion.Original);

            string current = currentValue != null ? currentValue.ToString() : string.Empty;

            string original = originalValue != null ? originalValue.ToString() : string.Empty;

            if (current != original)

            {

                sb.AppendFormat("{0}:{1}->{2}, ", ep.Name, original, current);

            }

        }

 

        string diffs = sb.ToString().Substring(0, sb.Length - 2);

        // Log diffs...

    }

}

 

Back to Top
ting View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 27-Mar-2009
Location: San Francisco
Posts: 427
Post Options Post Options   Quote ting Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 4:48pm
I'm not familiar with the details of your Audit method, but that seems reasonable.  You probably know this already, but for anyone else following the thread, you can both modify entities as well as add additional entities to be saved as part of the transaction.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 4:00pm

I started to implement Auditing in the ExecuteSave. I am calling the EntityManager and getting all changed entities for the AuditEntity type. Then calling the Audit method from there. I wasn't sure if there was a better way to do this.

Back to Top
ting View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 27-Mar-2009
Location: San Francisco
Posts: 427
Post Options Post Options   Quote ting Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 3:44pm

The EntityServerSaveInterceptor is where you accomplish this now.  You will probably want to override ExecuteSave(), but here's the full list of overridable methods (this is also in the API reference and release notes):

AuthorizeSave()
ClientCanSave()
ExecuteSave()
OnError()
ValidateSave()
 
 
For completeness, on the query side, we have the EntityServerQueryInterceptor and its corresponding methods:
 
AuthorizeQuery()
AuthorizeQueryResult()
ClientCanQuery()
ExecuteQuery()
FilterQuery()
OnError()
 
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 18-May-2010 at 12:04pm
I used Wards example in classic to create my Audit history for my entities. Now that I am on this version I am not sure how to implement. There doesn't seem to be a SaveSecurityCheck type method for each entity. From what I have noticed it is global to the server.
 
Are there methods in place to do auditing.
 
I ran across  SetValueWithChangeTracking but couldn't find any docs yet. A nudge in the right direction would be great. I wouldn't want to recreate the wheel if I didn't have to.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down