Skip to content
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

ParseException for DynamicFunctions.Like with amazing workaround? #840

Open
Wasserwecken opened this issue Sep 7, 2024 · 0 comments
Open

Comments

@Wasserwecken
Copy link

1. Description

Using the example from the documentation page to generate 'Like' conditions results in an exception:
System.Linq.Dynamic.Core.Exceptions.ParseException: "No applicable method 'Like' exists in type 'DynamicFunctions'"

TLDR my Workaround: The string methods .StartsWith, .EndsWith, .Contains work fine and EFCore can translate them to like-conditions.

2. Any further technical details

  1. You might want to add in the documentation that that for using the DynamicFunctions.Like the nuget Microsoft.EntityFrameworkCore.DynamicLinq is required. Without that, the example returns System.Linq.Dynamic.Core.Exceptions.ParseException: "Type 'DynamicFunctions' not found"

  2. Looking into DynamicFunctions.cs, I am not sure why the Like-methods are limited to .Net 2.0 and 2.1? I guess heres the root of the exception because im using .net8 so the methods are not available?

  3. But the previous point does not matter, because when using EF.FunctionsLike directly with a CustomTypeProvider it leads to the next exception: System.Linq.Dynamic.Core.Exceptions.ParseException: "No applicable method 'Like' exists in type 'DbFunctions'".. When you look into DbFunctions.cs there is actually no Like-Method, because it is an extension method in DbFunctionsExtensions.cs

3. Workarounds

I found two workarounds to achive my goal, where my goal:

  1. I dont know when EFCore added support for .StartsWith, .EndsWith, .Contains, but they just work! So there is no need for me to use CustomTypes :)
  2. If someone still needs other EF.Functions, you have to create a custom class that redirects to the EF.Functions like DynamicFunctions.cs and it will wok again.

3. Fiddle or Project

Heres the code with comments to show all the information from above.

// Vanilla approach of generating a Like-condition with EFCore.
var context = new CustomContext();
var example1 = context.Cars.Where(c => EF.Functions.Like(c.Brand, "%FooBar%"));
Console.WriteLine(example1.ToQueryString());
Console.WriteLine();

// Workaround to make EF.Functions work again.
// The 'CustomTypeProvicer' has a Like-method like 'DynamicFunctions'
var config3 = new ParsingConfig();
config3.ResolveTypesBySimpleName = true;
config3.CustomTypeProvider = new CustomTypeProvicer(config3);
var example4 = context.Cars.Where(config3, "CustomTypeProvicer.Like(Brand, \"%t%\")");
Console.WriteLine(example1.ToQueryString());
Console.WriteLine();

// Workaround of generating Like-condtions directly
var example5 = context.Cars.Where("Brand.Contains(\"FooBar\")");
Console.WriteLine(example5.ToQueryString());
Console.WriteLine();

var example6 = context.Cars.Where("Brand.StartsWith(\"FooBar\")");
Console.WriteLine(example6.ToQueryString());
Console.WriteLine();

var example7 = context.Cars.Where("Brand.EndsWith(\"FooBar\")");
Console.WriteLine(example7.ToQueryString());
Console.WriteLine();

// Unable to find the 'Like'-method because im using an unsuported .net version?
// Comment this block to go to the next block.
var config1 = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = context.Cars.Where(config1, "DynamicFunctions.Like(Brand, \"%t%\")");
Console.WriteLine(example1.ToQueryString());
Console.WriteLine();

// Unable to find the 'Like'-method because it is an extension method?
var config2 = new ParsingConfig();
config2.ResolveTypesBySimpleName = true;
config2.CustomTypeProvider = new CustomTypeProvicer(config2);
var example3 = context.Cars.Where(config2, "EF.Functions.Like(Brand, \"%t%\")");
Console.WriteLine(example1.ToQueryString());
Console.WriteLine();




public class Car
{
    public int Id { get; set; }
    public string Brand { get; set; }
}


public class CustomTypeProvicer(ParsingConfig config)
    : DefaultDynamicLinqCustomTypeProvider(config)
{
    public override HashSet<Type> GetCustomTypes()
        => [typeof(EF), typeof(CustomTypeProvicer)];

    public static bool Like(string? matchExpression, string? pattern)
        => EF.Functions.Like(matchExpression, pattern);
}


public class CustomContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder builder)
        => builder.UseSqlite(@"Data Source=.\data.db;");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant