如何修复Linq中的FirstOrDefault返回Null

时间:2019-04-25 11:17:08

标签: entity-framework linq entity-framework-6

我的Linq查询不断在FirstOrDefault上返回空错误

The cast to value type 'System.Int32' failed because the materialized value is null

因为它无法在ClinicalReading Table的ClinicalAssetID上找到任何匹配的记录,所以很公平!

但是我希望我的详细信息页面中的字段仅在表没有匹配条目的情况下显示为空白。

但是在使用order by函数时如何处理null问题?

当前代码:

var ClinicalASSPATINCVM = (from s in db.ClinicalAssets
                           join cp in db.ClinicalPATs on s.ClinicalAssetID equals cp.ClinicalAssetID into AP
                           from subASSPAT in AP.DefaultIfEmpty()
                           join ci in db.ClinicalINSs on s.ClinicalAssetID equals ci.ClinicalAssetID into AI
                           from subASSINC in AI.DefaultIfEmpty()
                           join co in db.ClinicalReadings on s.ClinicalAssetID equals co.ClinicalAssetID into AR
                           let subASSRED = AR.OrderByDescending(subASSRED => subASSRED.MeterReadingDone).FirstOrDefault()


                           select new ClinicalASSPATINCVM
                           {
                                ClinicalAssetID = s.ClinicalAssetID,
                                AssetTypeName = s.AssetTypeName,
                                ProductName = s.ProductName,
                                ModelName = s.ModelName,
                                SupplierName = s.SupplierName,
                                ManufacturerName = s.ManufacturerName,
                                SerialNo = s.SerialNo,
                                PurchaseDate = s.PurchaseDate,
                                PoNo = s.PoNo,
                                Costing = s.Costing,
                                TeamName = s.TeamName,
                                StaffName = s.StaffName,
                                WarrantyEndDate = subASSPAT.WarrantyEndDate,
                                InspectionDate = subASSPAT.InspectionDate,
                                InspectionOutcomeResult = subASSPAT.InspectionOutcomeResult,
                                InspectionDocumnets = subASSPAT.InspectionDocumnets,
                                LastTypeofInspection = subASSINC.LastTypeofInspection,
                                NextInspectionDate = subASSINC.NextInspectionDate,
                                NextInspectionType = subASSINC.NextInspectionType,
                                MeterReadingDone = subASSRED.MeterReadingDone,
                                MeterReadingDue = subASSRED.MeterReadingDue,
                                MeterReading = subASSRED.MeterReading,
                                MeterUnitsUsed = subASSRED.MeterUnitsUsed,
                                FilterReplaced = subASSRED.FilterReplaced


                                }).FirstOrDefault(x => x.ClinicalAssetID == id);

尝试过此方法但不起作用


.DefaultIfEmpty(new ClinicalASSPATINCVM())
                .FirstOrDefault()

错误为:

CS1929  'IOrderedEnumerable<ClinicalReading>' does not contain a definition for 'DefaultIfEmpty' and the best extension method overload 'Queryable.DefaultIfEmpty<ClinicalASSPATINCVM>(IQueryable<ClinicalASSPATINCVM>, ClinicalASSPATINCVM)' requires a receiver of type 'IQueryable<ClinicalASSPATINCVM>' 

对此感到有点距离,但仍然会出错

 let subASSRED = AR.OrderByDescending(subASSRED => (subASSRED.MeterReadingDone != null) ? subASSRED.MeterReadingDone : String.Empty).FirstOrDefault()

错误:

CS0173  Type of conditional expression cannot be determined because there is no implicit conversion between 'System.DateTime?' and 'string'

2 个答案:

答案 0 :(得分:1)

原始错误表示ClinicalASSPATINCVM类的以下某些属性-MeterReadingDoneMeterReadingDueMeterReadingMeterUnitsUsed或{{1 }}的类型为FilterReplaced

请记住int在这里

subASSRED

可能是let subASSRED = AR.OrderByDescending(subASSRED => subASSRED.MeterReadingDone).FirstOrDefault() (没有相应的记录)。

现在看一下投影的这一部分:

null

如果这是对象的LINQ,则所有这些将在运行时生成NRE(空引用异常)。在LINQ to Entities中,它被转换并作为SQL执行。 SQL不会像MeterReadingDone = subASSRED.MeterReadingDone, MeterReadingDue = subASSRED.MeterReadingDue, MeterReading = subASSRED.MeterReading, MeterUnitsUsed = subASSRED.MeterUnitsUsed, FilterReplaced = subASSRED.FilterReplaced 这样的表达式出现问题,因为即使subASSRED.SomeProperty通常不允许NULL,SQL也自然支持SomeProperty。因此,SQL查询可以正常执行,但是现在EF必须将结果具体化为对象,并且C#对象属性不能为 为空,因此出现了问题。

要解决此问题,请找到NULL属性,并在查询中使用以下模式:

int

或将接收对象属性类型更改为SomeIntProperty = (int?)subASSRED.SomeIntProperty ?? 0 // or other meaningful default ,并保持原始查询不变。

对所有不可为null的type属性执行相同的操作,例如int?DateTimedoubledecimal等。

答案 1 :(得分:0)

您的问题是因为您的DefaultIfEmpty是按AsQueryable执行的。执行[HttpPost("x")] public async Task<IActionResult> MigrateHistoricalData() { int filesCount = HttpContext.Request.Form.Files.Count; if (filesCount > 0) { var file = HttpContext.Request.Form.Files[0]; if (file != null && file.Length > 0) { // other logic } } else { return new BadRequestObjectResult("No files found in the request."); } return null; } ,它将起作用:

AsEnumerable

这不会导致性能下降!

数据库管理系统针对选择数据进行了优化。数据库查询的最慢部分之一是将所选数据传输到本地进程。因此,明智的做法是让DBMS进行大多数选择,并且只有在您知道只有真正打算使用的数据之后,才将数据移至本地进程。

在您的情况下,您的DBMS中最多需要一个元素,如果没有任何元素,则要改用默认对象。

// create the default element only once! static readonly ClinicalAssPatInVcm defaultElement = new ClinicalAssPatInVcm (); var result = <my big linq query> .Where(x => x.ClinicalAssetID == id) .AsEnumerable() .DefaultIfEmpty(defaultElement) .FirstOrDefault(); 将以智能的方式(可能是所选数据的“页面”)将所选数据移动到本地进程。

页面大小是一个很好的折衷方案:不要太小,因此您不必太频繁地请求下一页;不会太大,这样您传输的物品不会超过实际使用的数量。

此外,由于使用AsQueryable语句,您仍然希望最多有一个元素。这样就可以提取完整的“页面”,该页面将仅包含一个元素。

提取页面后,Where检查页面是否为空,如果为空,则返回包含DefaultIfEmpty的序列。如果不是,则返回完整页面。

在DefaultIfEmpty之后,您只需要获取第一个元素。