简体   繁体   English

如何在LINQ语句中使用扩展方法?

[英]How to use Extension method inside a LINQ statement?

I have an extension method to convert Address into a oneline string: 我有一个扩展方法可以将Address转换为单行字符串:

public static class AddressExtensions
{
    public static string ToOneLine(this Address address)
    {
        var sb = new StringBuilder();
        sb.Append(address.Street);
        if (!string.IsNullOrWhiteSpace(address.City)) sb.Append(string.Format("{0}, ",address.City));
        if (!string.IsNullOrWhiteSpace(address.State)) sb.Append(string.Format("{0}, ", address.State));
        if (!string.IsNullOrWhiteSpace(address.Zip)) sb.Append(string.Format("{0}, ", address.Zip));
        return sb.ToString();
    }
}

Then I use it to transfer data from domain model into my DTO. 然后,我将其用于将数据从域模型传输到我的DTO中。 The code below uses foreach and works fine: 下面的代码使用foreach并正常工作:

var entity=_repository.GetAll();
var model = new List<SummaryViewModel>();
        foreach (var e in entity)
        {
            model.Add(new SummaryViewModel
            {
                Address = e.Address.ToOneLine(),
                Name = e.Name,
                Id = e.Id
            });
        }

But when using LINQ , 但是当使用LINQ

        var model = entity.Select(e => new SummaryViewModel
        {
            Address = e.Address.ToOneLine(), Id = e.Id
        }).ToList();

I got a System.NotSupportedException . 我有一个System.NotSupportedException

LINQ to Entities does not recognize the method 'System.String ToOneLine
(Domain.Models.Address)' method, and this method cannot be translated 
into a store expression.

What happened? 发生了什么? I thought both method should work. 我认为两种方法都可以。

What is the correct way to use my extension method inside a LINQ statement? 在LINQ语句中使用扩展方法的正确方法是什么?

Becasue EF can't translate that extension method to SQL. 因为EF无法将该扩展方法转换为SQL。 Simplest way to fix it is to shift to Linq-to-Objects using AsEnumerable() (which is effectively what foreach does): 解决此问题的最简单方法是使用AsEnumerable()转移到Linq-to-Objects(这实际上是foreach所做的):

var model = entity.AsEnumerable()
                  .Select(e => new SummaryViewModel
{
    Address = e.Address.ToOneLine(), Id = e.Id
}).ToList();

Unlike ToList , using AsEnumerable does not create an additional collection in memory, it just binds the Linq calls to Enumerable instead of Queryable , so the mapping is done in memory instead of in SQL. ToList不同,使用AsEnumerable不会在内存中创建其他集合,它只是将Linq调用绑定到Enumerable而不是Queryable ,因此映射是在内存中而不是在SQL中完成的。

Resolve your query, then map it. 解决您的查询,然后将其映射。

var model = entity.ToList().Select(e => new SummaryViewModel
    {
        Address = e.Address.ToOneLine(), Id = e.Id
    }).ToList();

As it stands right now, by using deferred execution, the database is trying to interpret your ToOneLine() extension method. 就目前而言,通过使用延迟执行,数据库正在尝试解释您的ToOneLine()扩展方法。 By resolving it immediately with ToList(), you can properly iterate over the collection on the server and map it to your model. 通过使用ToList()立即解决它,您可以正确地遍历服务器上的集合并将其映射到您的模型。

This line: 这行:

foreach (var e in entity)

implicitly does the same thing as .AsEnumerable() . 隐式地执行与.AsEnumerable()相同的.AsEnumerable() Which enumerates and pulls object out of your database, when using the LINQ-expression LINQ2Entities is trying to convert it to a database expression. 当使用LINQ表达式LINQ2Entities试图将对象转换为数据库表达式时,该对象会将对象枚举并将其从数据库中拉出。 Ie executing it outside of the model-scope. 即在模型范围之外执行它。

For a more in depth description of what foreach actually does, have a look at How do foreach loops work in C#? 有关foreach实际作用的更深入描述,请看一下foreach循环如何在C#中工作? , in reality entity doesn't have to be an IEnumerable<T> , it just has to implement a GetEnumerator() . ,实际上entity不必是IEnumerable<T> ,它只需要实现GetEnumerator() Cool, huh? 酷吧? :) :)

Ie in your solution, doing 即在您的解决方案中

var model = entity.AsEnumerable().Select(e => new SummaryViewModel
    {
        Address = e.Address.ToOneLine(), Id = e.Id
    }).ToList();

would solve the issue. 将解决问题。

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

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