【问题标题】:Entity Framework - "All" method实体框架 - “全部”方法
【发布时间】:2009-05-18 21:52:43
【问题描述】:

All 方法应该针对列表中的所有元素评估参数。它在常规 Linq 中可以正常工作,但是当我尝试将它与 EF 一起使用时,它会引发错误(“无法创建类型为“闭包类型”的常量值。只有原始类型(例如 Int32、String 和 Guid)是在这种情况下支持。 ")

例子:

var myList = from person in entities.People
             where searchList.All(arg => arg == arg).ToList();

(arg == arg 这里只是为了说明我的问题)

在我的场景中,searchList 是一个包含搜索项的列表,例如“John”、“Accounting”、“75”。在我的 EF 查询中,我想检索人中 John、Accounting 和 75 出现在某些指定的可搜索字段中的所有记录。一个更现实的例子是这样的:

where SearchList.All((person.FirstName + " " + person.LastName + " " + person.DepartmentName + " " + person.Phone).Contains)

第二个例子也适用于 Linq,在内存中,但 EF 不喜欢它。

请帮忙!我该怎么做才能让它发挥作用?

这是一个来自我的another question 的更具体的问题。

示例代码:

IEnumerable<string> searchList = ParseSearchText(searchText); //search text is broken into search tokens - each token is an element in searchList. For instance "John", "Sales", "654"

var peopleQuery = from person in entities.vSearchPeople
where upperSearchList.All((person.FirstName + " " + person.Lastname + " " + person.Phone).ToUpperInvariant().Contains)
select person;

【问题讨论】:

    标签: linq entity-framework linq-to-entities


    【解决方案1】:

    实体框架不支持所有查询。如果您想到以下内容,这将变得很明显

    dataContext.Persons.Where(person => MyMethod(person));
    

    MyMethod() 返回一个布尔值。该方法可能会做所有事情,但您无法将所有内容都转换为 SQL。解决方案是使用ToList() 将所有实体放入本地内存,然后使用 LINQ to Object。

    dataContext.Persons.ToList().Where(person => MyMethod(person));
    

    是否可以重写取决于您的实际查询,以便它可以通过实体框架转换为 SQL,或者您是否必须使用 LINQ to Object 在本地内存中进行查询。

    您提到的异常听起来像您正在尝试以下内容。

    Company company = datacontext.Companies.Where(company.Name == "ACME").Single();
    
    dataContext.Employees.Where(employee => employee.Company == company);
    

    LINQ to Entity 不支持包含实体的表达式,因此Company 实体的比较无效。在这种情况下,您可以将其重写如下。

    dataContext.Employees.Where(employee => employee.Company.Id == company.Id);
    

    这仅比较 id - 一种原始类型,如整数或 GUID - 并且可以转换为 SQL。

    逐字搜索示例(另见 cmets)

    IQueryable<People> result = entities.People;
    
    foreach (String item in searchList)
    {
        // This copy is important in order not to modify the closure.
        String itemCopy = item;
    
        result = result.Where(p =>
            p.FirstName.ToUpper().Contains(itemCopy) ||
            p.LastName.ToUpper().Contains(itemCopy) ||
            p.Phone.ToUpper().Contains(itemCopy));
    }
    

    这将逐字构造查询。注意到实体框架可以识别ToUpper()ToLower()Contains()(以及更多)——所以当我说实体框架不识别方法调用时,我很严格。确实如此,但不是很多,也不是ToUpperInvariant()ToLowerInvariant()。此外,此查询使用列的排序规则转换为 CHARINDEX() 函数调用,因此搜索可以不区分大小写而无需显式 ToUpper()ToLower() 调用。

    【讨论】:

    • 谢谢丹尼尔。我很感激你的回答。由于 All 方法而引发错误。即使我有这样的东西,它也会出错:来自 entity.vSearchPeople 中的人,其中 searchList.All(f => true)。我不知道如何重写它,因为我必须将“searchList”中的所有项目与评估函数匹配。我想我需要在存储过程中这样做,而不是......你同意吗?
    • 该错误是由于 Where() 中的 searchlist.All()。您可以将其重写为entities.People.ToList().Where(people => searchList.All(arg => whatever(arg)));
    • 如果你能提供整个代码——搜索列表和查询的类型和构造——我可以看看它是否可以重写以在数据库中执行。
    • ToUpperInvariant() 导致问题 - 实体框架无法将此方法调用转换为 SQL。它应该怎么做?它必须知道该方法的作用。可能还有一些问题,但我这里没有 IDE 来测试它。如果需要方法调用,只能先调用ToList(),再使用LINQ to Object。 var peopleQuery = entity.vSearchPeople.ToList().Where(person => upperSearchList.All(item => (person.FirstName + " " + person.Lastname + " " + person.Phone).ToUpperInvariant().Contains(item )))
    • 进一步我建议进行以下修改(我保留 ToUpper() 调用)。 entity.vSearchPeople.ToList().Where(person => upperSearchList.All(item => person.FirstName.Contains(item) ||person.Lastname.Contains(item) || person.Phone.Contains(item)))这似乎比您使用字符串连接的版本更自然。
    【解决方案2】:

    这个很好的例子在 All() 方法中有什么好处。

    model.ReferenceList = db.JournalCardReference.OrderBy(a => a.orderF)
     .Include(x => x.JournalCardField)
      .Where(x => x.JournalCardField
        .All(f => f.deleted == null || f.deleted != true)).ToList();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-10
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      相关资源
      最近更新 更多