C# 如何在我的LINQ中实现对数据库的单次调用?

C# 如何在我的LINQ中实现对数据库的单次调用?,c#,.net,entity-framework,linq,C#,.net,Entity Framework,Linq,我正在做我的WebAPI项目 我在控制器类中有这个Linq To Entity行: SiteObject[] objects = await ObjectsRepository.Get().Where(x => x.SiteRegionId == regionId).ToArrayAsync(); return Ok(objects.Select(x => new ObjectBreif { IsServiceable = Context.Inspectio

我正在做我的WebAPI项目

我在控制器类中有这个Linq To Entity行:

SiteObject[] objects = await ObjectsRepository.Get().Where(x => x.SiteRegionId == regionId).ToArrayAsync();

return Ok(objects.Select(x => new ObjectBreif
    {
        IsServiceable = Context.InspectionReview.Any(i => i.ObjectId == x.Id && i.IsNormal == false) ? false : Context.InspectionReview.Any(i => i.ObjectId == x.Id) ? true : (bool?)null 
    })
我想创建比上面一行更有效的东西,而不是做两次数据库调用

如果InspectionReview表中存在至少一行满足此条件 条件:

ObjectId = 5 AND  IsNormal = false 
我需要将
IsServiceable
设置为
false
否则,我需要检查InspectionReview表中是否存在具有此条件的行:

 ObjectId = 5
如果是,
IsServiceable
必须获得
true
否则,
null

我通过使用上面的一行代码实现了这一点,但正如您所看到的,这是无效的,因为我访问了数据库两次


任何想法任何想法如何在一行中只调用一次数据库就能获得相同的结果我不想为此建议或额外行创建附加函数,因为我希望代码保持优雅

编辑:因为
ObjectId
不是唯一的:

var grouped = (from ir in Context.InspectionReview
group ir by ir.ObjectId into g
select new {
              ObjectId = g.Key, 
              FalsePresent = g.Any(gg=>gg.IsNormal == false)}).FirstOrDefault();

if(grouped == null) return (bool?)null;
return !grouped.FalsePresent;
试试这个:

var items = Context.InspectionReview.Where(i => i.ObjectID == x.id).ToList();
bool? IsServicable = (items.length == 0) ? 
                     (bool?)null : !(items.Any(i => i.IsNormal == false));

首先按
ObjectID
过滤,然后优先选择
为正常
为假的对象:

var first = Context.InspectionReview
    .Where(i => i.ObjectId == x.Id)
    .OrderBy(i => IsNormal)
    .FirstOrDefault();
bool? IsServiceable = (first == null) ? (bool?)null : first.IsNormal;

了解代码中的
对象
是很重要的,但不管怎样,这里是:

带子查询

var result =
    from x in objects
    let AnyNormal = db.InspectionReview.Where(r => r.ObjectId == x.Id)
        .DefaultIfEmpty().Min(r => r == null ? (int?)null : r.IsNormal ? 1 : 0)
    select new ObjectBreif
    {
        IsServiceable = AnyNormal == null ? (bool?)null : AnyNormal == 1
    };
或与左外连接

var result =
    from x in objects
    join r in db.InspectionReview on x.Id equals r.ObjectId into g
    let AnyNormal = g.DefaultIfEmpty().Min(r => r == null ? (int?)null : r.IsNormal ? 1 : 0)
    select new ObjectBreif
    {
        IsServiceable = AnyNormal == null ? (bool?)null : AnyNormal == 1
    };
更新::现在,当您更新代码段时,如果
ObjectsRepository.Get
从数据库返回
IQueryable
(不预先具体化),则最好将两个查询合并成一个这样的查询

var result =
    from x in ObjectsRepository.Get() where x.SiteRegionId == regionId
    join r in db.InspectionReview on x.Id equals r.ObjectId into g
    let AnyNormal = g.DefaultIfEmpty().Min(r => r == null ? (int?)null : r.IsNormal ? 1 : 0)
    select new ObjectBreif
    {
        IsServiceable = AnyNormal == null ? (bool?)null : AnyNormal == 1
    };

因为它将生成一个db查询,而所有其他查询都将对
objects
数组中的每个项执行

ObjectId不是primery键。知道如何在一行中执行它吗?不错,但为什么只需要一行就从数据库返回一个列表呢?如果有许多行具有相同的ObjectID,此解决方案将以未索引列表的形式获取所有行,然后对它们进行迭代。@mikeagg Valid point。这将取决于将有多少记录。如果相对较低,那么先对它们排序的性能权衡将是无关紧要的。当它返回false时?@Michael因为他正在检查first是否为null,如果由于OrderBy子句存在true记录,first.IsNormal将为true,否则为false。@Michael在您的代码段
中返回Ok(objects.Select…
是的,对不起。我有一个名为SiteObject的实体。这些对象是SiteObject类型的数组。我在示例中做了一些更改above@Michael好的,所以它不是一个db表包含相对较小的numebr项,然后使用
对象查询变量。contains
可能会执行得更好,因为它将允许在(…)子句中使用
生成单个db查询。或者,如果不需要数组来做其他事情,则将两个查询合并到一个查询中(如果
ObjectsRepository.Get
是可查询的。