我有一个Address
模型(简体)...
public class Address
{
public int AddressId { get; set; }
public string City { get; set; }
}
...以及一个包含一组
DbContext
的Addresses
派生类: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/