我有一个Address模型(简体)...

public class Address
{
    public int AddressId { get; set; }
    public string City { get; set; }
}


...以及一个包含一组DbContextAddresses派生类:

public DbSet<Address> Addresses { get; set; }


然后我有这个查询,它应该检索一个或不检索address_context是我的数据库上下文类的一个实例):

public Address GetAddress(string city, int addressId)
{
    Address address = null;

    // this is a database query
    var addresses = _context.Addresses.Where(a => a.City == city).ToList();

    // the rest queries in memory
    if (addresses.Count <= 1)
        address = addresses.FirstOrDefault();
    else
    {
        address = addresses.FirstOrDefault(a => a.AddressId == addressId);
        if (address == null)
            address = addresses.FirstOrDefault();
    }

    return address;
}


查询有点奇怪。逻辑很简单:


如果数据库表中只有一个(或没有)地址,并且请求的city以该地址为结果。
如果请求的city有多个地址,则首选具有给定addressId的地址。如果没有结果地址具有此addressId,则仅取第一个。


令人不安的是,.ToList()调用可能会将许多地址加载到我不感兴趣的内存中。最后,我仅过滤内存中已加载的地址之一作为最终结果。

有没有办法重写此查询(使用LINQ-to-Entities),使其完全在数据库中运行,并且仅返回一个地址或不返回地址(使用单个数据库往返)?

最佳答案

您的逻辑可以解释为偏爱具有给定ID的地址,如果没有匹配项,则完全不选任何一个。您的查询不会针对这种情况执行任何命令。

var addressesInCity = _context.Addresses.Where(a => a.City == city);
var addrByID = addressesInCity.Where(a => a.AddressId == addressId);
var anyAddr = addressesInCity.Take(1);


您可以使用两个查询来编写此代码:

addrByID.FirstOrDefault() ?? anyAddr.FirstOrDefault();


您可以将它们合并为一个查询:

addrByID.Select(a => new { Priority = 1, a })
.Concat(anyAddr.Select(a => new { Priority = 2, a }))
.OrderBy(x => x.Priority)
.Take(1)
.Select(x => x.a)
.FirstOrDefault();


这样可以节省往返时间,并且SQL Server可以按常量理解排序。它将高效运行。不一定比第一种形式更有效,但也没有明显恶化。

注意,UNION (ALL)返回的结果顺序是不确定的。我们需要通过引入Priority字段来强制执行顺序。

关于c# - 改进LINQ查询,使其在数据库中完全执行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24100141/

10-12 12:44
查看更多