Почему linq-2-sql создает лишние ненужные объекты?

У меня есть простая таблица родительского ребенка в такой базе данных

CREATE TABLE [Parent](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](256) NOT NULL)    
ALTER TABLE [Parent] ADD CONSTRAINT [PK_Parent_Id] PRIMARY KEY ([Id])    

CREATE TABLE [Child](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NOT NULL,
    [Name] [nvarchar](256) NOT NULL)    
ALTER TABLE [Child] ADD CONSTRAINT [PK_Child_Id] PRIMARY KEY ([Id])
ALTER TABLE [Child] ADD CONSTRAINT [FK_Child_Parent_ID] 
    FOREIGN KEY([ParentId]) REFERENCES [Parent] ([Id])

Данные, которые у меня есть,

Родительская таблица

Id  Name
1   John

Таблица детей

Id ParentId  Name
1     1    Mike
2     1    Jake
3     1    Sue
4     1    Liz

Эти таблицы сопоставляются с объектами Parent и Child С# с использованием конструктора Linq-2-SQL в Visual Studio без стандартных параметров.

Я сделал простую тестовую программу для запроса всего ребенка с родителями.

public partial class Parent
{
    static int counter = 0;
    //default OnCreated created by the linq to sql designer
    partial void OnCreated()
    {
        Console.WriteLine(string.Format("CreatedParent {0} hashcode={1}",
            ++counter , GetHashCode()));
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var db = new SimpleDbDataContext())
        {
            DataLoadOptions opts = new DataLoadOptions();
            opts.LoadWith<Child>(c => c.Parent);
            db.LoadOptions = opts;
            var allChildren = db.Childs.ToArray();
            foreach (var child in allChildren)
            {
                Console.WriteLine(string.Format("Parent name={0} hashcode={1}",
                    child.Parent.Name, child.Parent.GetHashCode()));

            }
        }
    }
}

Вывод вышеуказанной программы

CreatedParent 1 hashcode=53937671
CreatedParent 2 hashcode=9874138
CreatedParent 3 hashcode=2186493
CreatedParent 4 hashcode=22537358
Parent name=John hashcode=53937671
Parent name=John hashcode=53937671
Parent name=John hashcode=53937671
Parent name=John hashcode=53937671

Как вы можете видеть, объект Parent был создан для каждого Child в базе данных, который должен быть отброшен в конце концов.

Вопросы:

  • Почему Linq-2-Sql создает эти лишние дополнительные объекты Parent?
  • Есть ли какие-либо варианты, чтобы избежать создания дополнительных объектов Parent?

Ответы

Ответ 1

Это побочный эффект, реализуемый LoadWith. LINQ to SQL конвертирует ваш запрос внутри:

from c in children
select { Child = c, Parent = c.Parent }

Как вы можете видеть, мы загружаем родителя один раз для каждого ребенка (внутреннее соединение). Этот эффект обычно не виден из-за карты идентичности. ORM убедитесь, что объекты сущности никогда не дублируются (таблица, первичный ключ). Это пригодится при обновлении.

LINQ to SQL считывает результирующий набор, возвращенный с сервера (он содержит тот же родительский N раз!) и материализует его в объекты. Только после того, как материализация выполнена, карта идентичности выполняет свою работу и отбрасывает дубликаты родительских экземпляров.

Тот же эффект работает для всех запросов, возвращающих один и тот же объект несколько раз.