2015-11-03 62 views
2

我的工作我的网络API项目。如何在LINQ中实现对数据库的单一调用?

我有这个Linq到实体行控制器类中:

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 

我需要设置IsServiceablefalse否则,我需要检查,如果在InspectionReview表中存在的行与此条件:

ObjectId = 5 

如果是,IsServiceable必须得到true否则,null

我用一行代码above.But,你可以看到它不是有效的,因为我访问两次数据库实现这一点。

任何想法,任何想法,我怎么能达到同样的效果只在一排一个调用数据库,我不希望创建附加功能为这个提议或额外行,因为我想要的代码保持优雅。

+0

什么'objects'? –

回答

1

知道你的鳕鱼中有什么objects是很重要的e是,但无论如何,在这里你去:

随着子查询

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<SiteObject>(W/O提前去实现它),它可能是更好的两个查询合并成一个单一的一个像这样的

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 
    }; 

,因为它会产生一个单一的数据库查询,而所有其他人将执行(OK,单一,不两倍原)的objects阵列查询每每个项目

+0

@Michael在你的代码片段中'return Ok(objects.Select ...' –

+0

是的,对不起。我有实体叫做SiteObject对象是类型为SiteObject的数组。 – Michael

+0

我在上面的示例中做了一些更改 – Michael

1

编辑:既然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; 
+0

如果objectid是唯一的,可能取决于。 –

+0

的ObjectId不primery关键 – Michael

+0

任何想法,我怎样才能使它在一排? – Michael

4

试试这个:

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

不错,但是当只需要一行时,为什么从数据库中返回一个列表?如果有多个具有相同ObjectID的行,则此解决方案会将其全部作为无索引列表提取,然后对它们进行迭代。 – mikeagg

+0

@mikeagg有效的点。这将取决于将有多少记录。如果相对较低,那么首先订购它们的性能折衷将是微不足道的。 –

1

过滤通过ObjectID第一,然后优先选择那些IsNormal是假的:

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

当它返回false? – Michael

+1

@Michael由于他检查第一个不是null,所以如果由于OrderBy子句而存在真实记录,则first.IsNormal将为true,否则为false。 –

相关问题