Skip to content

Anonymous record fields names are sorted #6422

@enricosada

Description

@enricosada

When i create an anonymous record, the fields get sorted by name.

This cause issues when libraries expect the same order of declaration.
An example in dapper

// dotnet add package Dapper

open Dapper

// docker pull mcr.microsoft.com/mssql/server:2017-latest-ubuntu
// docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=<YourStrong!Passw0rd>" -p 1433:1433 --name sql1 -d mcr.microsoft.com/mssql/server:2017-latest-ubuntu
let connection = @"Data Source=localhost,1433;Database=master;User=sa;Password=<YourStrong!Passw0rd>;";

use db = new SqlConnection(connection)
db.Open()

// OK, same order
let res = db.QuerySingle<{|Name: string; Tid: int; X:string; Y: int|}>("select Name='fdsf', Tid=20, X='hfds', Y=15") 

// fails but understandable, different order
let res = db.QuerySingle<{|Name: string; Tid: int; X:string; Y: int|}>("select Tid=20, Name='fdsf', X='hfds', Y=15")

// fails and unexpected, same order
let res = db.QuerySingle<{|Tid: int; Name: string; X:string; Y: int|}>("select Tid=20, Name='fdsf', X='hfds', Y=15")  

the error is cryptic too, but explain it doesnt find the constructor with right shape

Unhandled Exception: System.InvalidOperationException: A parameterless default constructor or one matching signature (System.Int32 Tid, System.String Name, System.String X, System.Int32 Y) is required for <>f__AnonymousType1611086028`4'[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]] materialization

Repro steps

Use the snippet above.

We can also compare C# and F#

// F#
let x = {| Tid = 20; Name = "fdsf"; X = "hfds"; Y = 15 |}

// IL
IL_00ea: ldstr        "fdsf"
IL_00ef: ldc.i4.s     20 // 0x14
IL_00f1: ldstr        "hfds"
IL_00f6: ldc.i4.s     15 // 0x0f
IL_00f8: newobj       instance void class '<>f__AnonymousType1611086028`4\''<string, int32, string, int32>::.ctor(!0/*string*/, !1/*int32*/, !2/*string*/, !3/*int32*/)

while C#

// C#
var x = new { Tid = 20, Name = "fdsf", X = "hfds", Y = 15 };

// IL
IL_0001: ldc.i4.s     20 // 0x14
IL_0003: ldstr        "fdsf"
IL_0008: ldstr        "hfds"
IL_000d: ldc.i4.s     15 // 0x0f
IL_000f: newobj       instance void class '<>f__AnonymousType0`4'<int32, string, string, int32>::.ctor(!0/*int32*/, !1/*string*/, !2/*string*/, !3/*int32*/)```

Expected behavior

The generated IL respect order of declaration

Actual behavior

The generated IL sort the field names

Known workarounds

None

Related information

> dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.0.100-preview3-010431
 Commit:    d72abce213

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64

Host (useful for support):
  Version: 3.0.0-preview3-27503-5
  Commit:  3844df9537

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions