Подтвердить что ты не робот

Как вы покинули объединение в Linq, если в объединении есть несколько полей?

Я задал вопрос ранее о почему левые соединения в 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

(FYI, если это помогает понять, что я пытаюсь сделать: я пытаюсь определить, есть ли записи TreatmentPlanDetail для этого Patient, где для авторизации Payer требуется рецепт для этого ServiceType, но нет либо записи ServicePerscription, либо истек.)

Теперь, как выглядит мой код на С#:

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

Упс, не компилируется! По какой-то причине (я бы хотел объяснить) я не могу использовать этот буквальный логический элемент внутри equijoin! Хорошо, я оставлю это и отфильтровать материал "RequiresPrescription" позже...

...
join pstr in LinqUtils.GetTable<PayerServiceTypeRules>().DefaultIfEmpty()
on new { auth.PayerID, tpd.ServiceTypeID } equals new { pstr.PayerID, pstr.ServiceTypeID }
...

... и теперь он компилируется - но когда я запустил, в этой строке я получаю исключение "Object reference not set". DUH! Конечно, там есть нуль! Как еще вы должны выполнить сравнение с левым соединением, если вам не разрешено ссылаться на объект с правой стороны, который потенциально может быть пустым?

Итак, как вы должны делать левое соединение, используя несколько полей?

4b9b3361

Ответ 1

Я думаю, вам нужно использовать ключевое слово 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 будет выполнен перед его выбором, что не является желаемым поведением. Вы хотите совпадающие строки или null, если не существует соответствующих строк.


Для булевой проблемы это проблема именования (ничего не соответствует "RxReq" с правой стороны, и ничто не соответствует "RequiresPrescription" с левой стороны). Попробуйте присвоить имя true "RequiresPrescription", как указано выше (или назовите правую сторону pstr.RequiresPrescription "RxReq" ).