Использовал оператор 'new' в запросе LINQ to SQL, но в каждом результате указывается один и тот же экземпляр - PullRequest
3 голосов
/ 10 марта 2012

Может кто-нибудь объяснить поведение, которое я вижу в приведенном ниже примере минимального кода?Похоже, что для данного поля или свойства одни и те же два экземпляра класса Entry используются повторно в каждой итерации запроса LINQ to SQL, хотя я использую оператор new.Та же проблема не проявляется для запросов LINQ к объектам.Я создал проект консольного приложения C #, используя .NET Framework 4 и подключаясь к базе данных SQL Server 2005 Enterprise.

public class Set
{
    public Entry Field;
    public Entry Property { get; set; }
}

public class Entry
{
    public int ID;
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var dc = new DataClasses1DataContext(); // just create a simple dbml with some table from some database
        var resultQuery = (
            from x in dc.SomeTable
            select new Set()
            {
                Field = new Entry(),
                Property = new Entry()
            }
        );
        var sets = resultQuery.ToArray();
        Test(sets);

        var source = Enumerable.Range(0, 10);
        var sourceQuery = (
            from x in source
            select new Set()
            {
                Field = new Entry(),
                Property = new Entry()
            }
        );
        var sets2 = sourceQuery.ToArray();
        Test(sets2);
    }

    static void Test(Set[] sets)
    {
        var f = sets[0].Field;
        Console.WriteLine(sets.All(x => object.Equals(x.Field, f)));
        var p = sets[0].Property;
        Console.WriteLine(sets.All(x => object.Equals(x.Property, p)));
        Console.Writeline(sets.Length);
        Console.WriteLine(object.Equals(f, p));
        Console.WriteLine();
    }
}

Для * LINQ to SQL вывод Test()

True
True
1362
False

, означающий, что для всех созданных объектов Set все элементы Field указывают на один и тот же экземпляр Entry, а все элементы Property указывают на один и тот же экземпляр Entry.То есть один и тот же экземпляр повторно используется для соответствующего члена на каждой итерации запроса.

Выходные данные Test() для запроса LINQ to objects -

False
False
10
False

, что указывает наВ каждой итерации запроса создается новый отдельный экземпляр.

Ожидается ли поведение LINQ to SQL или ошибка?Кто-нибудь знает, происходит ли это с Entity Framework?

1 Ответ

1 голос
/ 10 марта 2012

Я не знаю, является ли это ошибкой, и если и почему это ожидается в LINQ to SQL.Я могу ответить на ваш последний вопрос только в том случае, если это также произойдет с Entity Framework.

Ответ: Нет.

С EF вы должны использовать синтаксис инициализатора объекта, хотя при создании экземпляра Entryобъекты.Использование конструктора по умолчанию приводит к исключению:

var resultQuery = (
    from x in dc.SomeTable
    select new Set
    {
        Field = new Entry { Name = "X" },
        Property = new Entry { Name = "X" }
    }
);

Не имеет значения, как вы инициализируете.Используя приведенный выше код (и с 4 строками в небольшой тестовой таблице), я получаю вывод с вашей тестовой программой:

False
False
4
False

False
False
10
False

Похоже, что существует большая разница между LINQ to SQL и Entity Framework в отношении объектаматериализация во время проекций.

(я тестировал с EF 4.1 / DbContext.)

Edit

Если я возьму измененный запрос в моем кодеприведенный выше фрагмент также для вашего запроса LINQ to SQL и просмотра сгенерированного SQL. Я получаю следующее:

SELECT NULL AS [EMPTY]
FROM [dbo].[SomeTable] AS [t0]

Тогда как то же самое с LINQ to Entites создает этот запрос:

SELECT 
1 AS [C1], 
N'X' AS [C2], 
N'X' AS [C3]
FROM [dbo].[SomeTable] AS [Extent1]

Моя интерпретациязаключается в том, что LINQ to SQL анализирует код проекции и запрашивает только столбцы для значений свойств, которые зависят от «переменной строки» x.Все остальные свойства заполняются на клиенте, когда объекты материализуются.Если объект вообще не зависит от значения столбца, LINQ to SQL создает один постоянный объект и повторно использует его во всей коллекции результатов.

В отличие от этого Entity Framework также отправляет постоянные значения (независимо от x) к серверу базы данных.Значения отправляются обратно клиенту, и EF обрабатывает эти значения, как если бы они были значениями столбцов, и обновляет свойства объектов в проекции.

Это также приводит к большой разнице в том, что нечто подобное ...

Random random = new Random();
var resultQuery = (
    from x in dc.SomeTable
    select new Set
    {
        Field = new Entry { ID = random.Next() },
        Property = new Entry { Name = "X" }
    }
);

... работает в LINQ to SQL, потому что, по-видимому, случайное значение функции (которое не зависит от x) вычисляется на клиенте и затем присваивается свойству.Но EF хочет перевести правую часть присваивания свойства в SQL и отправить его в виде фрагмента SQL на сервер базы данных, что приводит к ошибке и приводит к печально известному « ... не может быть преобразовано в выражение хранилища ... "исключение.

Редактировать 2

Кстати: последний фрагмент кода выше все еще создает только один экземпляр Field во всей коллекции: random.Next() оценивается толькоOnce (а также конструктор Entry вызывается только один раз для объекта Field).Теперь это действительно сбивает с толку, поскольку при написании такого кода можно ожидать, что вы хотите иметь случайное значение для каждой строки, возвращаемой из базы данных.Это не тот случай.

...