A "new" statement is used in a LINQ to SQL query, but the same instance references each result

Can anyone explain the behavior I am seeing in the minimal code example below? It seems that for a given field or property, the same two instances of the class are Entry

reused on each iteration of the LINQ to SQL query, although I am using the new

. The same issue does not occur for LINQ queries to objects. I created a C # Console Application project using .NET Framework 4 and a SQL Server 2005 Enterprise database connection.

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();
    }
}

      

Output Test()

for LINQ to SQL Query

True
True
1362
False

      

which specifies that for all created objects, Set

all members Field

point to the same single instance Entry

and all members Property

point to the same single instance Entry

. Ie, the same instance is reused for the corresponding member on each iteration of the request.

The output Test()

for a LINQ to objects query is

False
False
10
False

      

which indicates that a new, distinct instance is created on each iteration of the request.

Is LINQ to SQL expected behavior or error? Does anyone know if this happens with the Entity Framework?

+3


source to share


1 answer


I don't know if this is a bug or if and why this is expected in LINQ to SQL. I can only answer your last question if this also happens with Entity Framework.

Answer: No.

With EF, you need to use the object initializer syntax, though when instantiating objects Entry

. Using the default constructor throws an exception:

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

      

It doesn't matter how you initialize. Using the above code (and with 4 lines in a small test table) I get this result with a test program:

False
False
4
False

False
False
10
False

      

There seems to be a big difference between LINQ to SQL and Entity Framework regarding object materialization during predictions.

(I tested with EF 4.1 / DbContext.)

Edit

If I make a modified query in my code snippet above also for your LINQ to SQL query and see the generated SQL, I get this:



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

      

Whereas the same with LINQ to Entites creates this query:

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

      

My interpretation is that LINQ to SQL parses the projection code and only queries columns for property values ​​that depend on the "string variable" x. All other properties are populated on the client when objects materialize. If the object does not depend on the column value at all, LINQ to SQL creates a single persistent object and reuses it throughout the entire result collection.

Accordingly, Entity Framework also sends constant values ​​(independent of x) to the database server. The values ​​are returned to the client, and EF treats these values ​​as if they were column values ​​and updates the properties of the objects in the projection.

It also makes a big difference that something like this ...

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" }
    }
);

      

... works in LINQ to SQL because apparently the value of the random function (which does not depend on x) is evaluated on the client and then assigned to the property. But EF wants to translate the right side of the property assignment into SQL and send it as a SQL chunk to the database server - which fails and results in the infamous "... can't translate to store expression ..." exception.

Edit 2

BTW: The last piece of code above still only creates one instance Field

in the entire collection: random.Next()

only evaluated once (and also the constructor Entry

is only called once for the object Field

). This is really confusing, because to write code like this, you might expect that you want a random value for each row returned from the database. This is not true.

+1


source







All Articles