C# SQL-запрос для ретранслятора asp.
Я работаю над отчетом, который будет принимать все посещения, которые произошли в любой месяц, и отображать некоторую основную информацию. У меня есть все части, за исключением FirstName и LastName людей, которые посетили этот визит. Может быть от 0 до бесконечного количества посетителей за посещение.
Для этого есть 3 таблицы SQL. dbo.CaseNotes, dbo.VisitAttendance и dbo.Persons
Они связаны с dbo.CaseNotes[CaseNoteID] в dbo.VisitAttendance[CaseNoteID] dbo.VisitAttendance[CasePersonID] в dbo.Persons[PersonID]
От dbo.Persons я могу получить FirstName и LastName из PersonID.
Ошибка исходит от "Те, присутствующих". любая помощь будет принята с благодарностью.
Вот мой текущий код:
private void Services_BindGrid(DateTime begin, DateTime end)
{
using (var db = new FormsDataContext())
{
var caseID = Convert.ToInt32(Request.QueryString["CaseID"]);
var _visitAttend = db.VisitAttendances.Where(v => v.CaseID == caseID);
var query = db.CaseNotes.Where(c => c.CaseID == caseID && c.VisitDate >= begin && c.VisitDate <= end)
.Select(c=>new
{
VisitDate = c.VisitDate,
StartTime = c.StartTime,
EndTime = c.EndTime,
Duration = c.Duration,
TypeOfContact = db.DropDowns.SingleOrDefault(d => d.DropDownID == c.TypeOfContact).DisplayText,
LocationOfVisit = db.DropDowns.SingleOrDefault(d => d.DropDownID == c.LocationOfVisit).DisplayText,
VisitPunctuality = c.VisitPuncuality,
ThosePresent = db.VisitAttendances.SingleOrDefault(v => v.CaseNoteID == c.CaseNoteID).CasePersonID.ToString()
});
rptContacts.DataSource = query.ToList();
rptContacts.DataBind();
}
}
1 ответ
Я предполагаю, что вы используете Linq2Sql из-за суффикса DataContext
, Обратите внимание, что SingleOrDefault
вернет ноль, если записи не найдены, и сгенерирует исключение, если будет найдено более одной записи, поэтому доступ к нему невозможен. CasePersonID
без предварительной проверки на ноль.
Хотя вы могли бы сделать что-то громоздкое, как:
ThosePresent = db.VisitAttendances.SingleOrDefault(v => v.CaseNoteID == c.CaseNoteID) != null
? db.VisitAttendances.SingleOrDefault(v => v.CaseNoteID == c.CaseNoteID).CasePersonID.ToString()
: "No One"
производительность будет еще хуже, чем сейчас. Обратите внимание, что в настоящее время звонки db.DropDowns.SingleOrDefault
а также db.VisitAttendances.SingleOrDefault
будет выполняться один раз для каждого анонимного объекта, спроецированного по вашему запросу.
Могу ли я порекомендовать вместо этого использовать комбинацию активной загрузки и навигации по внешним ключам, чтобы заставить Sql выполнить объединение в базе данных. Сделайте это, добавив это сразу после создания контекста:
var options = new DataLoadOptions();
options.LoadWith<CaseNotes>(x => x.VisitAttendances);
db.LoadOptions = options;
И тогда вы можете сделать так:
ThosePresent = c.VisitAttendances
.SingleOrDefault(v => v.CaseNoteID == c.CaseNoteID)
(хотя, опять же, перед разыменованием вам нужно будет проверить нулевой результат). Тогда реальное решение состоит в том, чтобы использовать промежуточную проекцию, материализоваться (после фильтрации), а затем сделать окончательную проекцию в промежуточной проекции:
.Select(c => new
{
...
tmpThosePresent = c.VisitAttendances
.SingleOrDefault(v => v.CaseNoteID == c.CaseNoteID)
...
}
.ToList()
.Select(c => new
{
...
ThosePresent = c.tmpThosePresent != null
? c.tmpThosePresent.CasePersonID.ToString()
: "No One"
...
});
Точно так же для выпадающих меню, если они имеют навигационные внешние ключи от объектов домена, вы также можете загрузить с LoadsWith
или, в качестве альтернативы, если они constant
данные, загрузить их в статический Dictionary
кеш с ключом DropDownID
,