将子查询限制为第一行的Lambda语法是什么?

时间:2018-08-08 23:04:22

标签: c# sql linq

我有一个包含Item,Tax,TaxItem和ItemPrice表的数据库。除ItemPrice表外,这些关系都非常简单。在ItemPrice表中,给定ItemId可以有多行,因为Locations可以覆盖Region级别指定的价格。每条记录必须包含RegionId或LocationId,但不能同时包含两者,并且都不能为null。

ItemPrice
ID
ItemId
LocationId(可为空)
价格
RegionId(可为空)

项目
ID
名称

税收
ID
名称
类型

TaxItem
ItemId
TaxId

我正在使用的ABP框架利用了存储库模式。我当前拥有的代码如下:

var taxItems = _TaxItemRepository
.GetAllIncluding(ti => ti.Item, ti => ti.Tax, ti => ti.Item.ItemPrice)
.Where(ti =>
    (ti.ItemId == ItemId)
    &&
    (
        (ti.Item.ItemPrice.LocationId.HasValue && ti.Item.ItemPrice.LocationId == LocationId)
        ||
        (!ti.Item.ItemPrice.LocationId.HasValue && ti.Item.ItemPrice.RegionId == locationCacheItem.RegionId)
    )
);

不幸的是,它不等同于以下提供正确结果的SQL:

declare @ItemId bigint, @LocationId bigint, @RegionId bigint
set @ItemId = 1
set @LocationId = 1
set @RegionId = 1


        select ti.TaxId as TaxId, ti.ApplyTax as ApplyTax, ip.Price, tx.Value as TaxValue, tx.SpecifyItems, tx.TaxType, tx.UseNetPrice
        from TaxItem ti 
        inner join Tax tx on tx.id = ti.TaxId
        inner join Item on ti.ItemId = Item.Id
        inner join 
        (   
            select top 1 locationId, regionId, ItemId, Price 
            from ItemPrice 
            where ItemId = @ItemId and 
            (
                (LocationId is not null and LocationId = @locationId) OR 
                (LocationId is null and RegionId = @RegionId) 
            )
        ) ip on ip.ItemId = Item.Id

我在ItemPrice表中有两个ID为1的记录。

   
LocationId  Price   RegionId
1           1.25    NULL
NULL        1.50    1

如何获取Lambda表达式以将子查询限制为第一行?

3 个答案:

答案 0 :(得分:0)

那么您无需使用GetAllInclusion方法,因为它看起来包括整个集合...

答案 1 :(得分:0)

我认为Abion47的评论是关于金钱的,但是我通过升级到EF Core 2.1.1并使用Query .FromSQL()方法执行SQL来解决了该问题。我认为这是查询无法使用Lambda表示法轻松表达的情况之一,并且使用SQL更有效地执行。HTTP

var taxInfo = Context.Query<TaxItemInfo>().FromSql
  (
    $@"select ti.TaxId, ti.ApplyTax, ip.Price, tx.Value as TaxValue,   tx.SpecifyItems, tx.TaxType, tx.UseNetPrice
       from TaxItem ti 
       inner join Tax tx on tx.id = ti.TaxId 
       inner join Item on ti.ItemId = Item.Id 
       inner join 
       (
         select top 1 locationId,regionId,ItemId,Price from ItemPrice where  ItemId = {ItemId} and 
           ( 
             (LocationId is not null and LocationId = {LocationId}) OR 
             (LocationId is null and RegionId = {RegionId}) 
            ) 
        ) ip on ip.ItemId = Item.Id"
  );

答案 2 :(得分:0)

我不是ABP框架,这是普通的Linq:

var taxItems = from ti in context.TaxItem
               let ip = ti.Item.ItemPrice
                   .FirstOrDefault(x => x.LocationId == LocationId ||
                                  x.RegionId == locationCacheItem.RegionId)
               select new {
                  TaxId = ti.TaxId, 
                  ApplyTax = ti.ApplyTax, 
                  Price = ip.Price, 
                  TaxValue = ti.Tax.Value, 
                  SpecifyItems = ti.Tax.SpecifyItems, 
                  TaxType = ti.Tax.TaxType, 
                  UseNetPrice = ti.Tax.UseNetPrice
               };