Расширяемое реляционное деление в LINQ
В этом примере класса IcdPatient
представляет отношение "многие ко многим" между таблицей пациента (не показанной в этом примере) и таблицей поиска Icd
,
public class IcdPatient
{
public int PatientId { get; set; }
public int ConditionCode { get; set; }
public static List<IcdPatient> GetIcdPatientList()
{
return new List<IcdPatient>()
{
new IcdPatient { PatientId = 100, ConditionCode = 111 },
new IcdPatient { PatientId = 100, ConditionCode = 222 },
new IcdPatient { PatientId = 200, ConditionCode = 111 },
new IcdPatient { PatientId = 200, ConditionCode = 222 },
new IcdPatient { PatientId = 3, ConditionCode = 222 },
};
}
}
public class Icd
{
public int ConditionCode { get; set; }
public string ConditionName { get; set; }
public static List<Icd> GetIcdList()
{
return new List<Icd>()
{
new Icd() { ConditionCode =111, ConditionName ="Condition 1"},
new Icd() { ConditionCode =222, ConditionName ="Condition 2"},
};
}
}
Я хотел бы, чтобы пользователь мог ввести столько условий, сколько он хочет, и получить обратно объект LINQ, который сообщает им, сколько PatientId
удовлетворить этот запрос. Я придумал:
List<string> stringFilteredList = new List<string> { "Condition 1", "Condition 2" };
List<int> filteringList = new List<int> { 111,222 };
var manyToMany = IcdPatient.GetIcdPatientList();
var icdList = Icd.GetIcdList();
/*Working method without joining on the lookup table*/
var grouped = from m in manyToMany
group m by m.PatientId into g
where g.Count() == filteringList.Distinct().Count()
select new
{
PatientId = g.Key,
Count = g.Count()
};
/*End*/
foreach (var item in grouped)
{
Console.WriteLine(item.PatientId);
}
Скажем так IcdPatient
имеет составной первичный ключ в обоих полях, поэтому мы знаем, что каждая строка уникальна. Если мы найдем различное количество записей в filteringList
и рассчитывать на количество раз PatientId
появляется, это означает, что мы нашли всех людей, у которых есть все условия. Поскольку коды могут быть эзотерическими, я хотел бы сделать что-то вроде позволить пользовательской таблице в ConditionName
введите Icd и выполните ту же операцию. Я не использовал LINQ таким образом, и я собрал:
List<int> filteringList = new List<int> { 111,222 };
List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };
filteringList.Distinct();
var manyToMany = IcdPatient.GetIcdPatientList();
var icdList = Icd.GetIcdList();
/*Working method without joining on the lookup table*/
var grouped = from m in manyToMany
join i in icdList on
m.ConditionCode equals i.ConditionCode
//group m by m.PatientId into g
group new {m,i} by new { m.ConditionCode }into g
where g.Count() == filteringList.Distinct().Count()
select new
{
Condition = g.Key.ConditionCode
};
/*End*/
но ничего не могу заставить работать. По сути, это соединение поверх моего первого запроса, но я не получаю то, что мне нужно для группировки.
2 ответа
Вам не нужно ничего группировать в этом случае, просто используйте соединение и содержит:
List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };
var patients =
from icd in Icd.GetIcdList()
join patient in IcdPatient.GetIcdPatientList() on icd.ConditionCode equals patient.ConditionCode
where stringFilteredList.Contains(icd.ConditionName)
select patient.PatientId;
Допустим, у IcdPatient есть составной первичный ключ в обоих полях, поэтому мы знаем, что каждая строка уникальна. Если мы найдем различное количество записей в filteringList и посчитаем количество раз, которое показывает PatientId, это означает, что мы нашли всех людей, у которых есть все условия. Поскольку коды могут быть эзотерическими, я хотел бы сделать что-то вроде позволить пользовательской таблице в ConditionName типа Icd и выполнить ту же операцию.
Я полагаю, вы спрашиваете:
Приведен список
ConditionCode
s, вернуть списокPatientId
где каждый пациент имеет все условия в списке.
В этом случае проще всего сгруппировать таблицу IcdPatients по Id, чтобы мы могли определить каждое состояние пациента, просмотрев один раз. Затем мы проверяем, что каждый ConditionCode
мы ищем в группе. В коде это выглядит так:
var result = IcdPatient.GetIcdPatientList()
// group up all the objects with the same PatientId
.GroupBy(patient => patient.PatientId)
// gather the information we care about into a single object of type {int, List<int>}
.Select(patients => new {Id = patients.Key,
Conditions = patients.Select(p => p.ConditionCode)})
// get rid of the patients without every condition
.Where(conditionsByPatient =>
conditionsByPatient.Conditions.All(condition => filteringList.Contains(condition)))
.Select(conditionsByPatient => conditionsByPatient.Id);
В формате запроса это выглядит так:
var groupedInfo = from patient in IcdPatient.GetIcdPatientList()
group patient by patient.PatientId
into patients
select new { Id = patients.Key,
Conditions = patients.Select(patient => patient.ConditionCode) };
var resultAlt = from g in groupedInfo
where g.Conditions.All(condition => filteringList.Contains(condition))
select g.Id;
Изменить: Если вы также хотите, чтобы ваш пользователь указал ConditionName
а не ConditionId
затем просто конвертировать из одного в другой, сохраняя результат в filteringList
, вот так:
var conditionNames = // some list of names from the user
var filteringList = Icd.GetIcdList().Where(icd => conditionNames.Contains(icd.ConditionName))
.Select(icd => icd.ConditionCode);