win7user10 / Laraue.EfCoreTriggers

Library to write triggers in C# with EF.Core
MIT License
112 stars 20 forks source link

Custom MethodCallConverter `expression.Object` is null #26

Closed Meberem closed 2 years ago

Meberem commented 2 years ago

I want to be able to run a select statement as part of an update trigger, in this case COUNT the number of rows when a change has been made and write this to an audit table. I am attempting to write a MethodCallConverter but I am not having much luck. My structure looks something like this:

public class TypeConfiguration :    IEntityTypeConfiguration<AuditableThing>
{
    public void Configure(EntityTypeBuilder<AuditableThing> builder)
    {
        builder
            .AfterUpdate(trigger => trigger.Action(action => action.Insert((previous, next) =>
                new AuditEntry
                {
                    Total = next.Rows.Count(),
                    TotalX = next.Rows.Count(x => x > 1),
                }
            )));
    }
}

public class CountConverter : MethodCallConverter
{
    public override SqlBuilder BuildSql(BaseExpressionProvider provider, MethodCallExpression expression, Dictionary<string, ArgumentType> argumentTypes)
    {
        Debugger.Launch();
        // Generate SQL for arguments, they can be SQL expressions
        var argumentSql = provider.GetMethodCallArgumentsSql(expression, argumentTypes)[0];

        // Generate SQL for this context, it also can be a SQL expression
        var sqlBuilder = provider.GetExpressionSql(expression.Object, argumentTypes);

        // Combine SQL for object and SQL for arguments
        // Output will be like "thisValueSql LIKE 'passedArgumentValueSql'"
        return new(sqlBuilder.AffectedColumns, $"(SELECT COUNT(1) FROM ??? WHERE )");
    }

    public override string MethodName { get; } = nameof(Enumerable.Count);
    public override Type ReflectedType { get; } = typeof(Enumerable);
}

provider.GetMethodCallArgumentsSql(expression, argumentTypes)[0] yields System.ArgumentNullException: 'Value cannot be null. Arg_ParamName_Name' and provider.GetExpressionSql(expression.Object, argumentTypes) yields System.ArgumentNullException: 'Value cannot be null. Arg_ParamName_Name' do you have any advice or pointers on how to implement this?

Many thanks

win7user10 commented 2 years ago

LINQ expressions support is more complex than described in the documentation of method call converters. I added support of Count and Count with a predicate on the related entity in the latest library version.