效率低下的Linq声明

时间:2018-02-12 12:00:55

标签: c# performance linq

我有以下代码从sql数据库中提取客户地址作为纬度/经度点,并在winforms Map上显示点。但是,数据库非常大,这需要很长时间才能完成foreach。

有更好的方法吗?

public static List<MyAppMaps.MapPoints> GetAddresses()
{
    List<MyAppMaps.MapPoints> addresses = new List<MyAppMaps.MapPoints>();

    try
    {
        using (DataClassesDataContext dataClassesDataContext = new DataClassesDataContext(cDbConnection.GetConnectionString()))
        {
            var query = (from customer in dataClassesDataContext.Customers
                where (customer.Address.Latitude != ""
                       || customer.Address.Longitude != "")
                select customer);

            if (query.ToList().Count > 0)
            {
                foreach (Customer item in query)
                {
                    MyAppMaps.MapPoints address = new MyAppMaps.MapPoints();

                    address.Lat = item.Address.Latitude;
                    address.Long = item.Address.Longitude;
                    addresses.Add(address);
                }
            }
        }
    }
    catch (Exception)
    {

    }

    return addresses;
}

2 个答案:

答案 0 :(得分:2)

您可以在LINQ中进行投影,以避免提取您不需要的数据:

var addresses = dataClassesDataContext.Customers.
    .Select(c => c.Address)
    .Where(a => a.Latitude != "" || a.Longitude != "")
    .Select(c => new {
        a.Latitude
    ,   a.Longitude
    }).AsEnumerable() // From this point on LINQ is in memory
    .Select(p => new MyAppMaps.MapPoints {
        Lat = p.Latitude, Long = p.Longitude
    }).ToList();

此简化版本也可以使用:

var addresses = dataClassesDataContext.Customers.
    .Select(c => c.Address)
    .Where(a => a.Latitude != "" || a.Longitude != "")
    .Select(a => new MyAppMaps.MapPoints {
        Lat = a.Latitude, Long = a.Longitude
    }).ToList();

答案 1 :(得分:1)

问题是您访问Address属性,该属性可能指向另一个表,然后在foreach期间延迟加载(意味着每次迭代会导致另一个查询)。

在使用Entity Framework时,您需要确保避免这种情况。如果您想直接在响应中包含某些内容,请使用Include方法避免在引擎盖下进行其他查询。但是,通常最好使用Select投影来真正返回您实际需要的内容,这将使数据传输保持在最低速率。

您应该使用Select返回您需要的地址:

var query = (from customer in dataClassesDataContext.Customers
                where (customer.Address.Latitude != ""
                       || customer.Address.Longitude != "")
                select customer.Address);

您也可以致电ToList()检查是否有任何物品。这会不必要地执行查询。您可以立即拨打ToList(),然后使用创建的列表,或者只使用Any检查是否确实存在任何包含轻量级查询的项目。

总的来说,您可以改进代码如下:

using (DataClassesDataContext dataClassesDataContext = new DataClassesDataContext(cDbConnection.GetConnectionString()))
{
    var query = from customer in dataClassesDataContext.Customers
        where (customer.Address.Latitude != ""
               || customer.Address.Longitude != "")
        select new MyAppMaps.MapPoints() { Longitude = customer.Address.Longitude, Latitude = customer.Address.Latitude };
    addresses.AddRange( query );
}

更新

正如@Evk所建议的那样,只需返回两个必填字段而不是整个Address实例,即可进一步提高。我已更新代码以反映这一点。

更新2

根据@ Sivaprasath的建议进一步简化 - 通过LINQ查询将结果添加到地址列表中。