Как выйти из объединения в Linq, если в объединении более одного поля?
-
11-09-2019 - |
Вопрос
Ранее я задавал вопрос о почему левые соединения в Linq не могут использовать определенные отношения;на сегодняшний день я не получил удовлетворительного ответа.
Теперь, параллельно, я признал, что мне нужно использовать join
ключевое слово, как если бы между моими объектами не была определена связь, и я пытаюсь придумать, как выразить свой запрос в Linq.Проблема в том, что это скопление левых соединений между несколькими таблицами, в которых участвует несколько полей.Упростить это невозможно, поэтому вот SQL во всей его неприкрытой красе:
select *
from TreatmentPlan tp
join TreatmentPlanDetail tpd on tpd.TreatmentPlanID = tp.ID
join TreatmentAuthorization auth on auth.TreatmentPlanDetailID = tpd.ID
left join PatientServicePrescription rx on tpd.ServiceTypeID = rx.ServiceTypeID
left join PayerServiceTypeRules pstr on auth.PayerID = pstr.PayerID and tpd.ServiceTypeID = pstr.ServiceTypeID and pstr.RequiresPrescription = 1
where tp.PatientID = @PatientID
(К вашему сведению, если это поможет понять, что я пытаюсь сделать:Я пытаюсь определить, есть ли они TreatmentPlanDetail
записи для этого Patient
где разрешение Payer
для этого нужен рецепт ServiceType
, но либо нет ServicePerscription
запись, или срок ее действия истек.)
Вот как выглядит мой код на C#:
var q = from tp in TreatmentPlans
from tpd in tp.Details
from auth in tpd.Authorizations
join rx in ServicePrescriptions.DefaultIfEmpty() on tpd.ServiceTypeID equals rx.ServiceTypeID
// from pstr in auth.Payer.ServiceTypeRules.DefaultIfEmpty() -- very frustrating that this doesn't work!!
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
on new { auth.PayerID, tpd.ServiceTypeID, RxReq = (bool)true } equals new { pstr.PayerID, pstr.ServiceTypeID, pstr.RequiresPrescription }
select new { Payer = auth.Payer, Prescription = rx, TreatmentPlanDetail = tpd, Rules = pstr };
Упс, не компилируется!По какой-то причине (мне бы хотелось объяснений) я не могу использовать это буквальное логическое значение внутри эквивалентного соединения!Хорошо, я оставлю это и отфильтрую «RequiresPrescription» позже...
...
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
on new { auth.PayerID, tpd.ServiceTypeID } equals new { pstr.PayerID, pstr.ServiceTypeID }
...
...и теперь он компилируется - но когда я запускаю, я получаю исключение «Ссылка на объект не установлена» в этой строке. ДА! Конечно, там ноль!Как еще вы должны выполнять сравнение с левым соединением, если вам не разрешено ссылаться на объект справа, который потенциально может быть нулевым?
Итак, как же выполнить левое соединение, используя несколько полей?
Решение
Я думаю, вам нужно использовать into
ключевое слово и разрешить отсутствующие дочерние элементы DefaultIfEmpty(). после соединение, не раньше:
...
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>()
on new { auth.PayerID, tpd.ServiceTypeID, bool RequiresPrescription = true }
equals new { pstr.PayerID, pstr.ServiceTypeID, pstr.RequiresPrescription }
into pstrs
from PSTR in pstrs.DefaultIfEmpty()
select new {
Payer = auth.Payer,
Prescription = rx,
TreatmentPlanDetail = tpd,
Rules = PSTR
};
LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
вероятно, оказывается нулевым, потому что Возвращенный DataTable не содержит строк, тем самым вызывая ваше исключение.Обратите внимание на все утверждение после in
будет выполнен перед выбором в нем, что не является вашим желаемым поведением.Вам нужны совпадающие строки или ноль, если соответствующих строк не существует.
Что касается логической проблемы, это проблема именования (ничто не соответствует «RxReq» справа и ничего не соответствует «RequiresPrescription» слева).Попробуйте назвать true
«RequiresPrescription», как указано выше (или назовите правую сторону). pstr.RequiresPrescription
«RxReq»).