我有一个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),以便它在数据库中完全运行并只返回一个或没有地址(使用单个数据库往返)?
答案 0 :(得分:5)
您的逻辑可以被解释为更喜欢具有给定ID的地址,如果没有匹配jut pick any。您的查询不会强制执行该案件的任何订单。
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
字段来强制执行订单。