Delete, Direct Delete and Dereference Entity Auditing

Posts   
 
    
Posts: 10
Joined: 18-Feb-2024
# Posted on: 22-Feb-2024 20:26:55   

Hi all,

I'm deleting some entities as in the following code block but it wouldn't hit any of the delete, direct delete or dereference auditing methods.

Now I'm confused regarding what makes each of those 3 editors run and how to get this specific one work.

thanks in advance

var classesPredicate = new RelationPredicateBucket();
coursePredicate.PredicateExpression.AddWithAnd(DepartmentGradeClassesPerWeekFields.SchoolDepartmentId == request.Id);
await adapter.DeleteEntitiesDirectlyAsync(nameof(DepartmentGradeClassesPerWeekEntity), classesPredicate, cancellationToken);

Here's how my auditor class looks like

using EduInspection.Data.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
using System.Collections;

[DependencyInjectionInfo(typeof(DepartmentGradeClassesPerWeekEntity), "AuditorToUse")]
[Serializable]
public class DepartmentGradeClassesPerWeekAuditor() : AuditorBase
{
    private List<ActivityLogEntity> _auditInfoEntities = [];

    public override void AuditReferenceOfRelatedEntity(IEntityCore entity, IEntityCore relatedEntity,
        string mappedFieldName)
    {
//auditing logic goes here
    }

    public override void AuditDereferenceOfRelatedEntity(IEntityCore entity, IEntityCore relatedEntity,
        string mappedFieldName)
    {
//auditing logic goes here
    }


    public override void AuditDirectDeleteOfEntities(Type typeOfEntity, IPredicate filter, IRelationCollection relations, int numberOfEntitiesDeleted)
    {
//auditing logic goes here
    }
    public override void AuditDeleteOfEntity(IEntityCore deletedEntity)
    {
//auditing logic goes here
    }

    public override IList GetAuditEntitiesToSave()
    {
        return _auditInfoEntities;
    }

    public override void TransactionCommitted()
    {
        _auditInfoEntities.Clear();
    }
}
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 23-Feb-2024 08:46:49   

As stated in the reference manual entry for the method you're using:

This overload doesn't support Authorization or Auditing. It's recommended, if you want to use authorization and/or auditing on this method, use the overload of DeleteEntitiesDirectly which accepts a type.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 10
Joined: 18-Feb-2024
# Posted on: 25-Feb-2024 13:09:38   

One more question please, what's the recommended way to get the following items to use in the auditing logic:

  • The name of the related entity, "SchoolDepartmentEntity" in the above example.

  • The values and field names that were used in the predicate.

  • The value of any other field of the related entity.

Thanks again

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 26-Feb-2024 09:50:35   

Reham wrote:

One more question please, what's the recommended way to get the following items to use in the auditing logic:

  • The name of the related entity, "SchoolDepartmentEntity" in the above example.

  • The values and field names that were used in the predicate.

  • The value of any other field of the related entity.

Thanks again

The best way to discover these are really to attach a debugger and see how much information is available to you through the properties and methods in the classes. We have a reference manual which exposes these. E.g. casting an entity to IEntityCore gives you the field LLBLGenProEntityName which results in the name of the entity, like SchoolDepartmentEntity. All fields have names which you can obtain through the Fields property (and use the methods to avoid creation of the objects!, see the reference manual) https://www.llblgen.com/Documentation/5.11/ReferenceManuals/LLBLGenProRTF/html/5F98FA8E.htm

Frans Bouma | Lead developer LLBLGen Pro
Posts: 10
Joined: 18-Feb-2024
# Posted on: 26-Feb-2024 10:18:09   

Otis wrote:

casting an entity to IEntityCore gives you the field LLBLGenProEntityName which results in the name of the entity, like SchoolDepartmentEntity

But that's not not available for the direct delete auditor, did you mean that I should go for fetching the entity then normal delete?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 27-Feb-2024 09:10:38   

The type is passed in, so you can either instantiate an instance or use the type name (which is the same) as entity name?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 10
Joined: 18-Feb-2024
# Posted on: 27-Feb-2024 12:45:41   

Otis wrote:

The type is passed in, so you can either instantiate an instance or use the type name (which is the same) as entity name?

That works for the entity being deleted but I'm asking about the entities related to the deleted one. I need to get their names and/or some of their fields values, not necessarily all of them but at least the entity used in the predicate.

What are the recommended options that allows me to do so?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-Feb-2024 03:23:34   

One more question please, what's the recommended way to get the following items to use in the auditing logic:

The name of the related entity, "SchoolDepartmentEntity" in the above example.

There is no related entity in the provided example.

The values and field names that were used in the predicate.

The predicate used is passed to the AuditDirectDeleteOfEntities

AuditDirectDeleteOfEntities(
    Type typeOfEntity,
    IPredicate filter,
    IRelationCollection relations,
    int numberOfEntitiesDeleted
)

The value of any other field of the related entity.

There is no related entity in the provided example, and there is no entity or related entities when using DeleteEntitiesDirectly.

Posts: 10
Joined: 18-Feb-2024
# Posted on: 29-Feb-2024 01:38:37   

Looks like I can't attach a screenshot here, but I can't really find anything that I can use out of all those parameters. The only 2 properties with values in the predicate are the InstanceType and Negate. What I'm trying to achieve is to log that the parent "SchoolDepartmentEntity" with name [the name field value of this specific DB record] has its "DepartmentGradeClassesPerWeekEntity" children deleted and hence I need at least to know that "SchoolDepartmentId" was used to filter the entities being deleted and possibly pass the value of the name field I previously mentioned or even fetch the entity somewhere within the auditor class but I can't see how can I achieve that.

Is it not possible with direct delete and I should go for normal delete? still I won't be able to get the required field value of the parent entity but at least I'll have the "SchoolDepartmentId"

btw I tried passing those with [ThreadLocal] variable as mentioned here: https://www.llblgen.com/tinyforum/Thread/28844/1, for some reason it didn't go well, but I'll leave this part of the discussion for the other thread.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39617
Joined: 17-Aug-2003
# Posted on: 29-Feb-2024 09:23:20   

(You can attach a screenshot using the paperclip icon on the right?)

A predicate is sometimes a nested structure, expressions which have expressions as arguments. To obtain elements from a predicate you can traverse it. Below I'll give you the sourcecode of the PredicateFinder which is an internal type of the framework but which illustrates what you can do

    internal class PredicateFinder : QueryApiObjectTraverser
    {
        #region Class Member Declarations
        private List<IPredicate> _foundPredicates;
        private PredicateType _predicateTypeToFind;
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="PredicateFinder"/> class.
        /// </summary>
        /// <param name="traverseSetPredicateInnerSetElements">the flag to traverse the inner elements of a FieldCompareSetPredicate. (default is false). 
        /// Keep this value to false if you use the findings of this crawler to adjust an outer query, as the inner elements of a fieldcompareset are a 
        /// different scope.</param>
        /// <remarks>this ctor finds all predicates, regardless of type.</remarks>
        internal PredicateFinder(bool traverseSetPredicateInnerSetElements = false) : this(PredicateType.Undefined, traverseSetPredicateInnerSetElements)
        {
        }
    
        
        /// <summary>
        /// Initializes a new instance of the <see cref="PredicateFinder"/> class.
        /// </summary>
        /// <param name="predicateTypeToFind">The predicate type to find.</param>
        /// <param name="traverseSetPredicateInnerSetElements">the flag to traverse the inner elements of a FieldCompareSetPredicate. (default is false). 
        /// Keep this value to false if you use the findings of this crawler to adjust an outer query, as the inner elements of a fieldcompareset are a 
        /// different scope.</param>
        internal PredicateFinder(PredicateType predicateTypeToFind, bool traverseSetPredicateInnerSetElements = false)
            : base()
        {
            this.TraverseSetPredicateInnerSetElements = traverseSetPredicateInnerSetElements;
            _foundPredicates = new List<IPredicate>();
            _predicateTypeToFind = predicateTypeToFind;
        }

        
        /// <summary>
        /// Traverses the specified predicate and enclosed objects.
        /// </summary>
        /// <param name="objectToTraverse">The object to traverse.</param>
        public override void Traverse(IPredicate objectToTraverse)
        {
            if(objectToTraverse == null)
            {
                return;
            }
            if((_predicateTypeToFind==PredicateType.Undefined && objectToTraverse.InstanceType != (int)PredicateType.PredicateExpression) ||
                objectToTraverse.InstanceType == (int)_predicateTypeToFind)
            {
                _foundPredicates.Add(objectToTraverse);
            }
            base.Traverse(objectToTraverse);
        }


        #region Class Property Declarations
        /// <summary>
        /// Gets the found predicates.
        /// </summary>
        internal List<IPredicate> FoundPredicates
        {
            get { return _foundPredicates; }
        }
        #endregion
    }

Use it by calling Traverse() and passing in your IPredicate object.

obviously you want to do different things, like pull out information from the predicates for logging. So the IPredicate objects you run into have an instance type. You can use that to cast the IPredicate to a dedicated class like FieldCompareValuePredicate and use the values on that for your log string.

An alternative could be to generate the IPredicate to XML, by calling IPredicate.WriteXml(xmlwriter).

Another alternative would be to set its DatabaseSpecificCreator property to an instance of SqlServerSpecificCreator and call ToQueryText(). It might be this property is already set btw, if it's used in a query that was generated, so in that case you can just call ToQueryText() and you get the full SQL fragment for the predicate (with parameters, so you have to write these out manually).

Frans Bouma | Lead developer LLBLGen Pro