ชอบ Operator ใน Entity Framework หรือไม่?


93

เรากำลังพยายามใช้ตัวดำเนินการ "LIKE" ใน Entity Framework สำหรับเอนทิตีของเราที่มีฟิลด์สตริง แต่ดูเหมือนว่าจะไม่ได้รับการสนับสนุน มีใครลองทำอะไรแบบนี้บ้าง?

นี้โพสต์บล็อกสรุปปัญหาที่เรากำลังมี เราสามารถใช้มี แต่ตรงกับกรณีที่ไม่สำคัญที่สุดสำหรับ LIKE เท่านั้น การรวมประกอบด้วย, เริ่มต้นด้วย, สิ้นสุดด้วยและดัชนีของเราทำให้เราไปที่นั่นได้ แต่ต้องมีการแปลระหว่างอักขระตัวแทนมาตรฐานและรหัส Linq เป็นเอนทิตี


1
ไปที่คำตอบนี้ หากคุณใช้ EF 6.2.x อยู่แล้ว สำหรับคำตอบนี้หากคุณกำลังใช้ EF Core 2.x
CodeNotFound

คำตอบ:


36

นี่เป็นโพสต์เก่าตอนนี้ แต่สำหรับใครก็ตามที่กำลังมองหาคำตอบลิงก์นี้จะช่วยได้ ไปที่คำตอบนี้หากคุณใช้ EF 6.2.x อยู่แล้ว หากต้องการคำตอบนี้หากคุณใช้ EF Core 2.x

เวอร์ชั่นสั้น:

วิธีSqlFunctions.PatIndex - ส่งกลับตำแหน่งเริ่มต้นของการเกิดขึ้นครั้งแรกของรูปแบบในนิพจน์ที่ระบุหรือศูนย์หากไม่พบรูปแบบบนข้อความและชนิดข้อมูลอักขระที่ถูกต้องทั้งหมด

Namespace: System.Data.Objects.SqlClient Assembly: System.Data.Entity (ใน System.Data.Entity.dll)

นอกจากนี้ยังมีคำอธิบายเล็กน้อยในหัวข้อฟอรัมนี้


59
คำตอบที่ได้รับการยอมรับคือคำตอบที่เชื่อมโยงไปยังฟอรัม MSDN ที่เชื่อมโยงกลับไปยังคำถามนี้เพื่อหาคำตอบด้านล่างได้อย่างไร
Eonasdan

คำตอบคือใช้วิธี SqlFunctions.PatIndex เธรดฟอรัมที่เชื่อมโยงคือการให้ข้อมูล "พื้นหลัง" เพิ่มเติมเล็กน้อย
Yann Duran

คำตอบด้านล่างเหมาะสำหรับรูปแบบง่ายๆ แต่ถ้าฉันต้องการพูดว่า "WHERE Name LIKE" abc [0-9]% "" หรือรูปแบบอื่น ๆ ที่ซับซ้อนกว่านั้นเพียงแค่ใช้ประกอบด้วย () ก็ไม่ได้ตัดออกไปเสียทีเดียว
HotN

1
คำตอบที่เก่ากว่านี้สำหรับคำถามนี้ (ไม่ใช่ในส่วนแรก แต่เป็นทางเลือกอื่น)
Frédéric

154

ฉันไม่รู้อะไรเกี่ยวกับ EF เลย แต่ใน LINQ ถึง SQL คุณมักจะแสดงประโยค LIKE โดยใช้ String ประกอบด้วย:

where entity.Name.Contains("xyz")

แปลเป็น

WHERE Name LIKE '%xyz%'

(ใช้StartsWithและEndsWithสำหรับพฤติกรรมอื่น ๆ )

ฉันไม่แน่ใจทั้งหมดว่าจะมีประโยชน์หรือไม่เพราะฉันไม่เข้าใจว่าคุณหมายถึงอะไรเมื่อคุณบอกว่าคุณกำลังพยายามใช้ LIKE หากฉันเข้าใจผิดอย่างสมบูรณ์โปรดแจ้งให้เราทราบแล้วฉันจะลบคำตอบนี้ :)


4
โปรดทราบว่า "WHERE Name LIKE '% xyz%'" จะไม่สามารถใช้ดัชนีได้ดังนั้นหากตารางมีขนาดใหญ่ก็อาจทำได้ไม่ดีนัก ...
Mitch Wheat

1
เราอยากจะจับคู่กับblah * blah foo bar foo? bar? foo bar? และรูปแบบที่ซับซ้อนอื่น ๆ แนวทางปัจจุบันของเราคล้ายกับที่คุณพูดถึงเราจะแปลงการสืบค้นเหล่านั้นเป็นการดำเนินการโดยใช้ประกอบด้วย, indexof, เริ่มต้นด้วย, สิ้นสุดด้วย ฯลฯ ฉันแค่หวังว่าจะมีโซลูชันที่ใช้งานได้ทั่วไปมากกว่านี้
brien

2
ไม่ใช่ว่าฉันรู้ - ฉันสงสัยว่ารูปแบบที่ซับซ้อนลงเอยด้วยการเจาะจงฐานข้อมูลมากกว่าและยากที่จะแสดงออกโดยทั่วไป
Jon Skeet

4
@ Jon Skeet: เพื่อความรู้ที่ดีที่สุดของฉันฟังก์ชัน LIKE อยู่ในมาตรฐาน ANSI และค่อนข้างเหมือนกันใน SQL Server, Oracle และ DB2
AK

2
สิ่งหนึ่งที่ฉันเคยเห็นจากการใช้โอเปอเรเตอร์เหล่านี้และ MS SQL ก็คือ EF เพิ่มเป็นพารามิเตอร์ที่หลีกเลี่ยง "ชื่อ LIKE @ p__linq__1 ESCAPE N '' ~ ''" ซึ่งในกรณีการใช้งานที่ จำกัด มากของฉันจะทำงานช้าลงมากหากสตริงการค้นหา อยู่ในแบบสอบถาม "ชื่อเช่น '% xyz%' สำหรับสถานการณ์ที่ฉันมีฉันยังคงใช้ StartsWith และมีอยู่ แต่ฉันทำผ่าน dynamic linq เพราะมันจะฉีดพารามิเตอร์ลงในคำสั่ง SQL ซึ่งในสถานการณ์ของฉันกำลังสร้าง การสืบค้นที่มีประสิทธิภาพมากขึ้นไม่แน่ใจว่านี่คือสิ่งที่ EF 4.0 หรือไม่คุณยังสามารถใช้ ObjectQueryParameters เพื่อให้ได้สิ่งเดียวกัน ...
Shane Neuville

35

ผมมีปัญหาเหมือนกัน.

สำหรับตอนนี้ฉันได้ตัดสินด้วยการกรอง Wildcard / Regex ฝั่งไคลเอ็นต์ตามhttp://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - มันง่ายและทำงานเป็น คาดว่า.

ฉันพบการสนทนาอื่นในหัวข้อนี้: http://forums.asp.net/t/1654093.aspx/2/10
โพสต์นี้ดูดีถ้าคุณใช้ Entity Framework> = 4.0:

ใช้ SqlFunctions.PatIndex:

http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

แบบนี้:

var q = EFContext.Products.Where(x =>
SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);

หมายเหตุ: โซลูชันนี้ใช้สำหรับ SQL-Server เท่านั้นเนื่องจากใช้ฟังก์ชัน PATINDEX ที่ไม่ได้มาตรฐาน


ในขณะที่ PatIndex "ใช้งานได้" มันจะกลับมากัดคุณ PatIndex ในส่วนคำสั่งที่ไม่ใช้ดัชนีในคอลัมน์ที่คุณต้องการกรอง
BlackICE

@BlackICE นี้คาดว่า เมื่อคุณค้นหาข้อความภายใน (% CD% BLUE%) เซิร์ฟเวอร์จะไม่สามารถใช้ดัชนีได้ เมื่อใดก็ตามที่เป็นไปได้การค้นหาข้อความจากจุดเริ่มต้น (CD% BLUE%) จะมีประสิทธิภาพมากขึ้น
เล่น

@surfen patindex นั้นแย่กว่านั้นคือจะไม่ใช้ดัชนีแม้ว่าจะไม่มี% อยู่ข้างหน้าก็ตามการค้นหา (BLUE CD%) ด้วย patindex จะไม่ใช้ดัชนีคอลัมน์
BlackICE

23

อัปเดต: ใน EF 6.2 มีตัวดำเนินการที่เหมือนกัน

Where(obj => DbFunctions.Like(obj.Column , "%expression%")

นี่จะเป็นตัวอย่างที่ชัดเจนกว่านี้ไม่ได้: Where(obj => DbFunctions.Like(obj.Column , "%expression%")?
DCD

แน่ใจ. เปลี่ยนมัน
Lode Vlaeminck

20

มีLIKEการเพิ่มตัวดำเนินการในEntity Framework Core 2.0:

var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;

การเปรียบเทียบกับ... where e.Title.Contains("developer") ...มันถูกแปลSQL LIKEเป็นมากกว่าที่CHARINDEXเราเห็นสำหรับContainsวิธีการ


5

มีการกล่าวถึงเป็นพิเศษในเอกสารประกอบซึ่งเป็นส่วนหนึ่งของ Entity SQL คุณได้รับข้อความแสดงข้อผิดพลาดหรือไม่?

// LIKE and ESCAPE
// If an AdventureWorksEntities.Product contained a Name 
// with the value 'Down_Tube', the following query would find that 
// value.
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name LIKE 'DownA_%' ESCAPE 'A'

// LIKE
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name like 'BB%'

http://msdn.microsoft.com/en-us/library/bb399359.aspx


1
ฉันจะถูกล่อลวงให้อยู่ห่างจาก Entity SQL ในกรณีที่คุณต้องการย้ายออกจาก EF ในอนาคต เล่นอย่างปลอดภัยและใช้ตัวเลือกประกอบด้วย (), StartsWith () และ EndsWith () ในการตอบกลับเดิมแทน
Stephen Newman

1
คอมไพล์ได้ดี แต่ล้มเหลวในขณะรันไทม์
brien

รหัสที่ฉันโพสต์ล้มเหลวในขณะรันไทม์? มาจากลิงค์ของ Microsoft
Robert Harvey

ฉันแก้ไขคำถามโดยมีลิงก์ไปยังบล็อกโพสต์ที่อธิบายปัญหาเดียวกันกับที่เราพบ
brien

ดูเหมือนว่ามี () เป็นตั๋วของคุณ แต่อย่างที่ Jon Skeet ชี้ให้เห็นคุณอาจต้องเลื่อนลงไปที่ SQL จริงที่จัดการฐานข้อมูลโดยตรงหากมีไม่ตรงกับความต้องการของคุณ
Robert Harvey

2

หากคุณใช้ MS Sql ฉันได้เขียนวิธีการขยาย 2 วิธีเพื่อรองรับอักขระ% สำหรับการค้นหาสัญลักษณ์แทน (จำเป็นต้องมี LinqKit)

public static class ExpressionExtension
{
    public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> expr, string likeValue)
    {
        var paramExpr = expr.Parameters.First();
        var memExpr = expr.Body;

        if (likeValue == null || likeValue.Contains('%') != true)
        {
            Expression<Func<string>> valExpr = () => likeValue;
            var eqExpr = Expression.Equal(memExpr, valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
        }

        if (likeValue.Replace("%", string.Empty).Length == 0)
        {
            return PredicateBuilder.True<T>();
        }

        likeValue = Regex.Replace(likeValue, "%+", "%");

        if (likeValue.Length > 2 && likeValue.Substring(1, likeValue.Length - 2).Contains('%'))
        {
            likeValue = likeValue.Replace("[", "[[]").Replace("_", "[_]");
            Expression<Func<string>> valExpr = () => likeValue;
            var patExpr = Expression.Call(typeof(SqlFunctions).GetMethod("PatIndex",
                new[] { typeof(string), typeof(string) }), valExpr.Body, memExpr);
            var neExpr = Expression.NotEqual(patExpr, Expression.Convert(Expression.Constant(0), typeof(int?)));
            return Expression.Lambda<Func<T, bool>>(neExpr, paramExpr);
        }

        if (likeValue.StartsWith("%"))
        {
            if (likeValue.EndsWith("%") == true)
            {
                likeValue = likeValue.Substring(1, likeValue.Length - 2);
                Expression<Func<string>> valExpr = () => likeValue;
                var containsExpr = Expression.Call(memExpr, typeof(String).GetMethod("Contains",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
            }
            else
            {
                likeValue = likeValue.Substring(1);
                Expression<Func<string>> valExpr = () => likeValue;
                var endsExpr = Expression.Call(memExpr, typeof(String).GetMethod("EndsWith",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(endsExpr, paramExpr);
            }
        }
        else
        {
            likeValue = likeValue.Remove(likeValue.Length - 1);
            Expression<Func<string>> valExpr = () => likeValue;
            var startsExpr = Expression.Call(memExpr, typeof(String).GetMethod("StartsWith",
                new[] { typeof(string) }), valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(startsExpr, paramExpr);
        }
    }

    public static Expression<Func<T, bool>> AndLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var andPredicate = Like(expr, likeValue);
        if (andPredicate != null)
        {
            predicate = predicate.And(andPredicate.Expand());
        }
        return predicate;
    }

    public static Expression<Func<T, bool>> OrLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var orPredicate = Like(expr, likeValue);
        if (orPredicate != null)
        {
            predicate = predicate.Or(orPredicate.Expand());
        }
        return predicate;
    }
}

การใช้งาน

var orPredicate = PredicateBuilder.False<People>();
orPredicate = orPredicate.OrLike(per => per.Name, "He%llo%");
orPredicate = orPredicate.OrLike(per => per.Name, "%Hi%");

var predicate = PredicateBuilder.True<People>();
predicate = predicate.And(orPredicate.Expand());
predicate = predicate.AndLike(per => per.Status, "%Active");

var list = dbContext.Set<People>().Where(predicate.Expand()).ToList();    

ใน ef6 และควรแปลเป็นไฟล์

....
from People per
where (
    patindex(@p__linq__0, per.Name) <> 0
    or per.Name like @p__linq__1 escape '~'
) and per.Status like @p__linq__2 escape '~'

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Hi% ', @ p__linq_2 ='% Active '


ขอบคุณสำหรับความคิดเห็นของคุณ Ronel มีอะไรให้ฉันช่วยไหม ข้อความแสดงข้อผิดพลาดคืออะไร?
Steven Chong

2

สำหรับ EfCore นี่คือตัวอย่างในการสร้างนิพจน์ LIKE

protected override Expression<Func<YourEntiry, bool>> BuildLikeExpression(string searchText)
    {
        var likeSearch = $"%{searchText}%";

        return t => EF.Functions.Like(t.Code, likeSearch)
                    || EF.Functions.Like(t.FirstName, likeSearch)
                    || EF.Functions.Like(t.LastName, likeSearch);
    }

//Calling method

var query = dbContext.Set<YourEntity>().Where(BuildLikeExpression("Text"));

0

คุณสามารถใช้เหมือนจริงในลิงก์ไปยังเอนทิตีได้อย่างง่ายดาย

เพิ่ม

    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>

ไปยัง EDMX ของคุณในแท็กนี้:

edmx: Edmx / edmx: Runtime / edmx: ConceptualModels / Schema

จำเนมสเปซใน<schema namespace="" />แอตทริบิวต์ด้วย

จากนั้นเพิ่มคลาสส่วนขยายในเนมสเปซด้านบน:

public static class Extensions
{
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")]
    public static Boolean Like(this String searchingIn, String lookingFor)
    {
        throw new Exception("Not implemented");
    }
}

วิธีการขยายนี้จะแมปกับฟังก์ชัน EDMX

ข้อมูลเพิ่มเติมที่นี่: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.