简体   繁体   English

Linq,Expressions,NHibernate和Like比较

[英]Linq, Expressions, NHibernate and Like comparison

I am trying to do a like comparison based on an outside parameter (passed by a search form) that determines type of comparison ( "%string" or "string%" or "%string%" ) 我试图做一个基于外部参数(通过搜索表单传递)的比较来确定比较类型( "%string""string%""%string%"

I was thinking in the following direction: 我正朝着以下方向思考:

query = query.Where(
    Entity.StringProperty.Like("SearchString", SelectedComparsionType)
)

Like method would than based on selected type return .StartsWith() or .EndsWith() or .SubString() 类似方法将基于所选类型返回.StartsWith().EndsWith().SubString()

My knowledge of expressions is apparently far from great, since i haven't been able to construct a method that could yield the right result (server side comparison in SQL just like with StartsWith method). 我对表达式的了解显然远远不够,因为我无法构造一个可以产生正确结果的方法(SQL中的服务器端比较与StartsWith方法一样)。

The easy way 简单的方法

Just use 只是用

if (comparison == ComparisonType.StartsWith)
    query = query.Where(e => e.StringProperty.StartsWith("SearchString"));
else if ...

The hard way 艰难的方式

If you want to do something like this, either make sure your LINQ provider can be told of this new method somehow, and how it translates to SQL (unlikely), or prevent your method from ever reaching the LINQ provider, and provide the provider something it understands (hard). 如果你想做这样的事情,要么确保你的LINQ提供程序能以某种方式被告知这个新方法,以及它如何转换为SQL(不太可能),或者阻止你的方法到达LINQ提供者,并为提供者提供一些东西它理解(很难)。 For example, instead of 例如,而不是

query.Where(e => CompMethod(e.StringProperty, "SearchString", comparsionType))

you can create something like 你可以创造类似的东西

var query = source.WhereLike(e => e.StringProperty, "SearchString", comparsionType)

with the following code 使用以下代码

public enum ComparisonType { StartsWith, EndsWith, Contains }

public static class QueryableExtensions
{
    public static IQueryable<T> WhereLike<T>(
        this IQueryable<T> source,
        Expression<Func<T, string>> field, 
        string value,
        SelectedComparisonType comparisonType)
    {
        ParameterExpression p = field.Parameters[0];
        return source.Where(
            Expression.Lambda<Func<T, bool>>(
                Expression.Call(
                    field.Body, 
                    comparisonType.ToString(), 
                    null, 
                    Expression.Constant(value)),
            p));
    }
}

You can even add additional criteria this way 您甚至可以通过这种方式添加其他条件

var query = from e in source.WhereLike(
                e => e.StringProperty, "SearchString", comparsionType)
            where e.OtherProperty == 123
            orderby e.StringProperty
            select e;

The very, very hard way 非常非常努力的方式

It would (technically) be possible to rewrite the expression tree before the provider sees it, so you can use the query you had in mind in the first place, but you'd have to (技术上)可以在提供者看到之前重写表达式树,因此您可以首先使用您想到的查询,但是您必须

  • create a Where(this IQueryable<EntityType> source, Expression<Func<EntityType, bool>> predicate) to intercept the Queryable.Where , 创建一个Where(this IQueryable<EntityType> source, Expression<Func<EntityType, bool>> predicate)来拦截Queryable.Where
  • rewrite the expression tree, replacing your CompMethod , wherever it is, with one of the String methods, 重写表达式树,用一个String方法替换你的CompMethod ,无论它在哪里,
  • call the original Queryable.Where with the rewritten expression, 使用重写的表达式调用原始的Queryable.Where
  • and first of all, be able to follow the extension method above in the first place! 首先,能够首先遵循上面的扩展方法!

But that's probably way too complicated for what you had in mind. 但这对你想到的东西来说可能太复杂了。

Sounds like you should be wanting to use: 听起来你应该想要使用:

query = query.Where(
Entity.StringProperty.Contains("SearchString")
)

This should map to: 这应该映射到:

WHERE StringProperty LIKE '%SearchString%'

This should also work for more advanced search masks such as "Mr? Sm%th", but I haven't had to test any search strings like that myself yet. 这也适用于更高级的搜索掩码,例如“Mr?Sm%th”,但我还没有必须测试任何类似的搜索字符串。


UPDATE: Based on OPs edit 更新:基于OPs编辑

It sounds like what you are asking for is something like the following: 这听起来像你要求的是如下:

   public enum SelectedComparsionType
    {
        StartsWith,
        EndsWith,
        Contains
    }

public static bool Like(this string searchString, string searchPattern, SelectedComparsionType searchType)
{
    switch (searchType)
    {
        case SelectedComparsionType.StartsWith:
            return searchString.StartsWith(searchPattern);
        case SelectedComparsionType.EndsWith:
            return searchString.EndsWith(searchPattern);
        case SelectedComparsionType.Contains:
        default:
            return searchString.Contains(searchPattern);
    }
}

This would allow you to write code as you require, ie: 这将允许您根据需要编写代码,即:

query = query.Where(
Entity.StringProperty.Like("SearchString", SelectedComparsionType.StartsWith)
)

However, personally , I would replace any use of SelectedComparsionType, with a direct call to the required string function. 但是,就个人而言 ,我会直接调用所需的字符串函数来替换SelectedComparsionType的任何使用。 Ie

query = query.Where(
Entity.StringProperty.StartsWith("SearchString")
)

As this will still map to a SQL 'LIKE' query. 因为它仍将映射到SQL'LIKE'查询。

This is exactly what I had in mind, thank you. 这正是我的想法,谢谢。 I had something similar already written, but it didn't translate to SQL. 我已经编写了类似的东西,但它没有转换为SQL。 For example, it worked if I did this directly: 例如,如果我直接执行此操作,它会起作用:

Entity.StringProperty.EndsWith("SearchString");

It didn't work if I used a dedicated method: 如果我使用专用方法,它不起作用:

CompMethod("BaseString","SearchString",SelectedComparsionType.EndsWith)

I think it probably has something to do with expression evaluation, i'm just not sure what. 我认为它可能与表达式评估有关,我只是不确定是什么。

您最好使用Regex来解决此问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM