System.Linq.Dynamic.Core icon indicating copy to clipboard operation
System.Linq.Dynamic.Core copied to clipboard

Question: Is it possible to do case-insensitive search

Open rajeshmuraleedharan opened this issue 8 years ago • 18 comments

I would like to do case-insensitive search with startswith,endwith and Contains. Please let me know this is possible or not.

rajeshmuraleedharan avatar Dec 12 '17 23:12 rajeshmuraleedharan

This is related to your SQL-server settings ?

StefH avatar Dec 20 '17 08:12 StefH

@rajeshmuraleedharan When you install SQL Server you must specify the collation. CI is for case insensitive and AI is for accent insensitive. Anyway, It could be great to make requests in CI, AI, CS or AS "on demand".

mhosman avatar Jan 15 '18 18:01 mhosman

I think, you should able to use: title.Contains("string", StringComparison.OrdinalIgnoreCase); if it works is a question of your linq adapter

jogibear9988 avatar Apr 19 '18 12:04 jogibear9988

@jogibear9988 This will not work for this library I think.

StefH avatar Jul 24 '18 11:07 StefH

Sadly it doesn't work, the part "StringComparison" is treated as a property and you get an error saying, that the property or field 'StringComparison' exists in the currently used type. However, you can use the string value of the enum, described in the Enum Type Support Section.

users.Where("GivenName.Contains(\"sa\", \"OrdinalIgnoreCase\")");

Works perfectly for me :-)

ghost avatar Nov 06 '18 11:11 ghost

How do you do a case sensitive search with this library ???

Query.Where($"{dataPropertie.Name}.ToString().Trim().ToLower().Contains(@0)", cols.Values.ToStringNullSafe().Trim().ToLower()).OrderBy(dataPropertie.Name).ToList();

I tried something like this but is not working

wilari932 avatar Jan 30 '19 19:01 wilari932

@rajeshmuraleedharan With #450, StringComparison is now supported, so the following should work:

users.Where("GivenName.Contains(\"sa\", StringComparison.OrdinalIgnoreCase)");

@wilari932 How would you write a case-insensitive search using standard expression trees? If this would work:

var cols1 = cols.Values?.ToString().Trim();
Query = Query.Where(x => x.Contains(cols1, StringComparison.OrdinalIgnoreCase));

then the following should also work:

var cols1 = cols.Values?.ToString().Trim();
Query = Query.Where("Contains(@0, StringComparison.OrdinalIgnoreCase)", cols1);

zspitz avatar Nov 15 '20 16:11 zspitz

users.Where("GivenName.Contains("sa", StringComparison.OrdinalIgnoreCase)");

Still does not work for me in 1.2.6: No applicable method 'Contains' exists in type 'String'

khmurach avatar Dec 06 '20 10:12 khmurach

@khmurach How are you escaping the quotation marks? Also, is this being run against a database or other Queryable provider?

zspitz avatar Dec 06 '20 11:12 zspitz

@khmurach How are you escaping the quotation marks? Also, is this being run against a database or other Queryable provider?

Yes, I'm escaping. I'm using Where on List<User>().AsQueryable()

khmurach avatar Dec 06 '20 11:12 khmurach

@khmurach This works for me:

var lst = new List<User>();
var qry = lst.AsQueryable().Where("GivenName.Contains(\"sa\", StringComparison.OrdinalIgnoreCase)");

public class User {
    public string? GivenName { get; set; }
}

Visualizing qry.Expression: image Which framework are you targeting?

zspitz avatar Dec 06 '20 11:12 zspitz

@zspitz I have generic method so there is no properties to use in standard Where(x=>x.Name.Contains("value")).

IQueryable<T> ApplyFilter(IQueryable<T> dataAll, object filter)

This line works fine: dataQueryable = dataQueryable.Where("{0}.ToLower().Contains(@0)".FormatWith(item.Key), item.Value.ToString().ToLower()); But I would like to get rid of ToLower()

khmurach avatar Dec 06 '20 11:12 khmurach

@khmurach This works for me:

var lst = new List<User>();
var qry = lst.AsQueryable().Where("GivenName.Contains(\"sa\", StringComparison.OrdinalIgnoreCase)");

public class User {
    public string? GivenName { get; set; }
}

Visualizing qry.Expression: image Which framework are you targeting?

targetFramework="net451"

khmurach avatar Dec 06 '20 11:12 khmurach

@khmurach It seems that .NET Framework 4.5.1 doesn't support this overload of Contains: image How would you do this with a strongly-typed query?

N.B. The filter you are passing into ApplyFilter could also be generic:

IQueryable<T> ApplyFilter(IQueryable<T> dataAll, Func<T, bool> filter) {

}

and then you would have strong-typing on the filter itself. Although I don't quite see what it gives you over the standard .Where.

zspitz avatar Dec 06 '20 11:12 zspitz

Even in this simplified example, first two queries works fine but the last one throws error:

var items = new List<ListItem>() { new ListItem { Title = "John" }, new ListItem { Title = "Adam" }, new ListItem { Title = "john" } };
// works fine
var result1 = items.AsQueryable().Where("Title.ToLower().Contains(\"jo\")").ToList();
// works fine
var result2 = items.AsQueryable().Where(x => x.Title.Contains("jo", StringComparison.OrdinalIgnoreCase)).ToList();
// throws exception
var result3 = items.AsQueryable().Where("Title.Contains(\"jo\", StringComparison.OrdinalIgnoreCase)").ToList();

khmurach avatar Dec 06 '20 11:12 khmurach

I'm using object filter in IQueryable<T> ApplyFilter(IQueryable<T> dataAll, object filter) to be able to use any object (projection) as a filter and to use reflection to scan props and values and apply them as a filter to List<T>:

var usersCache = _repository.GetAll<User>();
var filter1 = new User{ FirstName = "jo", LastName = "wes" };
var filter2 = new UserQuery{ FirstName = "jo" };
var result1 = ApplyFilter(usersCache, filter1);
var result2 = ApplyFilter(usersCache, filter2);

khmurach avatar Dec 06 '20 11:12 khmurach

Any updates on how to do an case invariant search?

ButtiBBQ avatar Aug 03 '21 14:08 ButtiBBQ

I'm also having this problem with Contains with the System.Linq.Dynamic.Core package at version 1.2.18, but only when running my actual code. If I run Unit tests everything works perfectly.

[Fact]
    public void DynamicLinq_SingleContains_SimpleText_CaseInsensitiveOverload()
    {
      var filter = "(Name != null && Name.Contains(\"Blu\", StringComparison.CurrentCultureIgnoreCase))";
      var data = GetDynamicLinqTestData().AsQueryable();

      var filteredData = data.Where(filter).ToArray();
      Assert.Single(filteredData);
    }

    [Fact]
    public void DynamicLinq_SingleContains_ReplacementText_CaseInsensitiveOverload()
    {
      var filter = "(Name != null && Name.Contains(@0, StringComparison.CurrentCultureIgnoreCase))";
      var filterParams = new List<object>();
      filterParams.Add("Blu");
      var data = GetDynamicLinqTestData().AsQueryable();

      var filteredData = data.Where(filter, filterParams.ToArray()).ToArray();
      Assert.Single(filteredData);
    }

Both of these tests work fine in XUnit. But when I run my code that creates the exact same thing (either way, and I have unit tests on those create functions too) I get System.Linq.Dynamic.Core.Exceptions.ParseException No applicable method 'Contains' exists in type 'String'

I've tried also using the older method of doing just an escaped string of the enum like "CurrentCultureIgnoreCase" but that doesn't work either. Doesn't matter which enum I use, OrdinalIgnoreCase doesn't work either. It does appear to work if I let it be case sensitive, but that's not ideal.

I just don't understand why it works in unit tests and not in practice.

I will also note that string.StartsWith and EndsWith both DO work in unit tests and in practice using the case sensitive enums. Which is even weirder.

Update:

  • Nevermind, I forgot the Unit Test Project is using .Net 5.0 and the actual project is using Framework 4.8 where that String.Contains overload doesn't exist apparently. Yay old code making things hard.

Beamer92 avatar Apr 12 '22 18:04 Beamer92

Closing..

StefH avatar Dec 05 '23 18:12 StefH