Я читал DDD Evans, и я экспериментирую с дизайном совокупного корневого репозитория с использованием C # и Entity Framework 4.1 + LINQ.
Однако меня беспокоят фактические запросы, которые отправляются в БД. Я использую SQL 2008 R2 и запускаю SQL Profiler, чтобы проверить, что делает БД в ответ на код LINQ.
Рассмотрим простой дизайн из двух сущностей с Person и EmailAddress. Один человек может иметь от нуля до нескольких адресов электронной почты, а адрес электронной почты должен иметь ровно одно лицо. Person - это совокупный корень, поэтому не должно быть репозитория для адресов электронной почты. Адреса электронной почты следует выбирать из репозитория Person (согласно DDD Evans).
Для сравнения, у меня есть временный репозиторий для адресов электронной почты. Следующая строка кода:
var emailString = "someone@somewhere.com";
var emailEntity = _tempEmailRepository.All.SingleOrDefault(e =>
e.Value.Equals(emailString, StringComparison.OrdinalIgnoreCase));
... выполняет красивый чистый SQL-запрос в соответствии с профилировщиком:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Value] AS [Value],
[Extent1].[IsDefault] AS [IsDefault],
[Extent1].[IsConfirmed] AS [IsConfirmed],
FROM [dbo].[EmailAddress] AS [Extent1]
Я могу выбрать электронную почту из репозитория людей с помощью следующего кода:
var emailEntity = _personRepository.All.SelectMany(p => p.Emails)
.SingleOrDefault(e => e.Value.Equals(emailString,
StringComparison.OrdinalIgnoreCase))
Это дает мне то же самое во время выполнения, но с разными командами, отображаемыми в SQL Profiler:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
FROM [dbo].[Person] AS [Extent1]
В дополнение к вышеупомянутому запросу, который выбирает из Person, существует ряд событий «RPC: Completed», по одному для каждой строки EmailAddress в базе данных:
exec sp_executesql N'SELECT
[Extent1].[Id] AS [Id],
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Value] AS [Value],
[Extent1].[IsDefault] AS [IsDefault],
[Extent1].[IsConfirmed] AS [IsConfirmed],
FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] =
@EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
exec sp_executesql N'SELECT
[Extent1].[Id] AS [Id],
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Value] AS [Value],
[Extent1].[IsDefault] AS [IsDefault],
[Extent1].[IsConfirmed] AS [IsConfirmed],
FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] =
@EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2
Моя тестовая база данных имеет 14 строк в dbo.EmailAddress, и есть 14 различных вызовов RPC: Completed, каждый с другим значением @ EntityKeyValue1.
Я предполагаю, что это плохо для производительности SQL, так как dbo Таблица .EmailAddress получает больше строк, больше этих RPC будет вызываться в базе данных. Есть ли другой лучший подход к использованию совокупных корневых репозиториев DDD с EF 4.1 + LINQ?
Обновление: Решено
Проблема заключалась в том, что свойство All возвращало IEnumerable
. После того, как это было изменено на IQueryable
, LINQ включился и выбрал все лицо + электронные письма за один раз. Однако мне пришлось связать .Include (p => p.Emails), прежде чем возвращать IQueryable из All.