Как исправить FirstOrDefault, возвращающий Null в Linq - PullRequest
0 голосов
/ 25 апреля 2019

Мой запрос Linq продолжает возвращать нулевую ошибку в FirstOrDefault

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

потому что он не может найти никаких записей для соответствия в ClinicalAssetID из таблицы ClinicalReading, достаточно справедливо!

Но я хочу, чтобы поля на странице сведений отображались пустыми, если в таблице нет соответствующей записи.

Но как я могу обработать нулевую проблему при использовании порядка по функции?

Текущий код:

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 ]

1 голос
/ 25 апреля 2019

Исходная ошибка означает, что некоторые из следующих свойств класса ClinicalASSPATINCVM - MeterReadingDone, MeterReadingDue, MeterReading, MeterUnitsUsed или FilterReplaced имеют тип int.

Помните, что subASSRED здесь

let subASSRED = AR.OrderByDescending(subASSRED => subASSRED.MeterReadingDone).FirstOrDefault()

может быть null (нет соответствующей записи).

Теперь посмотрите на эту часть проекции:

MeterReadingDone = subASSRED.MeterReadingDone,
MeterReadingDue = subASSRED.MeterReadingDue,
MeterReading = subASSRED.MeterReading,
MeterUnitsUsed = subASSRED.MeterUnitsUsed,
FilterReplaced = subASSRED.FilterReplaced

Если бы это было LINQ to Objects, все это генерировало бы NRE (исключение нулевой ссылки) во время выполнения.В LINQ to Entities это преобразуется и выполняется как SQL.В SQL нет проблем с выражением типа subASSRED.SomeProperty, потому что SQL, естественно, поддерживает NULL, даже если SomeProperty обычно не разрешает NULL.Таким образом, SQL-запрос выполняется нормально, но теперь EF должен материализовать результат в объекты, а свойство объекта C # имеет значение , а не , допускающее значение Nullable, следовательно, возникает вопрос об ошибке.

Чтобы решить его, найдитеint свойство (я) и используйте следующий шаблон внутри запроса:

SomeIntProperty = (int?)subASSRED.SomeIntProperty ?? 0 // or other meaningful default

или измените тип свойства объекта-получателя на int? и оставьте исходный запрос как есть.

ВыполнитеТо же самое для любого свойства типа Nullable, например DateTime, double, decimal, Guid и т. д.

0 голосов
/ 25 апреля 2019

Ваша проблема в том, что ваш DefaultIfEmpty выполняется как AsQueryable.Выполните его AsEnumerable, и оно будет работать:

// 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 проверяет, пуста ли страница, и, если да, возвращает последовательностьсодержащий defaultElement.Если нет, возвращается полная страница.

После DefaultIfEmpty вы берете только первый элемент, который вам нужен.

...