Skip to content

Using AddMap with a custom generic key throws when calling a static method #257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
hahn-kev opened this issue Mar 6, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@hahn-kev
Copy link

hahn-kev commented Mar 6, 2025

Version

2.15.0, I can't use 2.16 since I use dotnet 9 but EF 8, the bug should be present in latest also.

Details

I was trying to add a map like this:

Mapper.AddMap(nameof(Entry.LexemeForm), (entry, key) => Json.Value(entry.LexemeForm, ms => ms[key]));

and when I try to generate an expression it throws. I would expect it to just work. As a workaround I was able to disable null index checking.

Steps to reproduce

  1. Add a map like this Mapper.AddMap(nameof(Entry.LexemeForm), (entry, key) => Json.Value(entry.LexemeForm, ms => ms[key]));
  2. use that property in a filter like LexemeForm[en]=test
  3. convert to express
  4. get exception:
Object reference not set to an instance of an object.
   at Gridify.Builder.BaseQueryBuilder`2.AddIndexerNullCheck(LambdaExpression mapTarget, Object query)
   at Gridify.Builder.BaseQueryBuilder`2.ConvertBinaryExpressionSyntaxToQuery(BinaryExpressionSyntax binarySyntax)
   at Gridify.Builder.BaseQueryBuilder`2.BuildQuery(ExpressionSyntax expression, Boolean isParenthesisOpen)
   at Gridify.Builder.BaseQueryBuilder`2.Build(ExpressionSyntax expression)

debugging it I found that line 147 throws because methodCallExpression.Object is null.

private object AddIndexerNullCheck(LambdaExpression mapTarget, object query)
{
if (mapper.Configuration.DisableCollectionNullChecks || mapper.Configuration.EntityFrameworkCompatibilityLayer)
return query;
var body = mapTarget.Body;
if (body is UnaryExpression unaryExpression)
body = unaryExpression.Operand;
if (body is not MethodCallExpression methodCallExpression) return query;
var containsKeyMethod = methodCallExpression.Object!.Type.GetMethod("ContainsKey", [mapTarget.Parameters[1].Type]);
if (containsKeyMethod == null) return query;
var mainQuery = (LambdaExpression)query;
var keyNullCheck = Expression.Call(methodCallExpression.Object!, containsKeyMethod, methodCallExpression.Arguments);
var newExp = Expression.AndAlso(keyNullCheck, mainQuery.Body);
return Expression.Lambda(newExp, mainQuery.Parameters);
}

@hahn-kev hahn-kev added the bug Something isn't working label Mar 6, 2025
@alirezanet
Copy link
Owner

Hi @hahn-kev,
I'm not sure If I fully understand the problem, Please provide a reproducable example (maybe include the model and explain what is the Json.Value that you're trying to use and why?)

if you could create a Test-case showing the problem, it is much easier to debug and resolve generally.

@alirezanet alirezanet added the need more information Further information is requested label Mar 6, 2025
@hahn-kev
Copy link
Author

hahn-kev commented Mar 6, 2025

Sure thing, in this case that call is just transformed from an Expression by Linq2Db into SQL. But I can come up with a simple reproduction to make it easy to test the bug.

@hahn-kev
Copy link
Author

@alirezanet here's a working example:

var query = new GridifyQuery()
{
    Filter = "MetaDataDefined[CreatedOn]=false"
};
var mapper = new GridifyMapper<Person>();
mapper.Configuration.DisableCollectionNullChecks = false;//true works around the issue
mapper.AddMap("MetaDataDefined", (person, key) => Utils.IsDefined(person.MetaData, key));
var expression = query.GetFilteringExpression(mapper);//throws

class Person
{
    public Dictionary<string, string> MetaData { get; set; }
}

class Utils
{
    public static bool IsDefined(Dictionary<string, string> dict, string key)
    {
        return dict.TryGetValue(key, out _);
    }
}

In my case I'm using Gridify with Linq2db which I can configure to rewrite Utils.IsDefined into SQL, but that's not important here.

Another workaround but doesn't actually work for me would be to move IsDefined to be an instance method on Person and then configure the mapper like this:

mapper.AddMap("MetaDataDefined", (person, key) => person.IsDefined(person.MetaData, key));

@alirezanet alirezanet removed the need more information Further information is requested label Mar 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants