diff --git a/Dapper.Tests/Dapper.Tests.xproj b/Dapper.Tests/Dapper.Tests.xproj
index e4eb69c8..80e0c21a 100644
--- a/Dapper.Tests/Dapper.Tests.xproj
+++ b/Dapper.Tests/Dapper.Tests.xproj
@@ -14,7 +14,9 @@
2.0
-
+
+ True
+
diff --git a/Dapper.Tests/Tests.cs b/Dapper.Tests/Tests.cs
index 0965e866..cbe834eb 100644
--- a/Dapper.Tests/Tests.cs
+++ b/Dapper.Tests/Tests.cs
@@ -3696,5 +3696,17 @@ insert TPTable (Value) values (2), (568)");
return connection;
}
#endif
- }
+ [Fact]
+ public void GetOnlyProperties()
+ {
+ var obj = connection.QuerySingle("select 42 as [Id], 'def' as [Name];");
+ obj.Id.IsEqualTo(42);
+ obj.Name.IsEqualTo("def");
+ }
+ class HazGetOnly
+ {
+ public int Id { get; }
+ public string Name { get; } = "abc";
+ }
+ }
}
diff --git a/Dapper/DefaultTypeMap.cs b/Dapper/DefaultTypeMap.cs
index 9d16349e..9fb04b8d 100644
--- a/Dapper/DefaultTypeMap.cs
+++ b/Dapper/DefaultTypeMap.cs
@@ -164,13 +164,25 @@ namespace Dapper
if (property != null)
return new SimpleMemberMap(columnName, property);
+ // roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
+ var backingFieldName = $"<{columnName}>k__BackingField";
+
+ // preference order is:
+ // exact match over underscre match, backing fields over regular fields, exact case over wrong case
var field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
- ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
if (field == null && MatchNamesWithUnderscores)
{
- field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
- ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
+ var effectiveColumnName = columnName.Replace("_", "");
+ backingFieldName = $"<{effectiveColumnName}>k__BackingField";
+
+ field = _fields.FirstOrDefault(p => string.Equals(p.Name, effectiveColumnName, StringComparison.Ordinal))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, effectiveColumnName, StringComparison.OrdinalIgnoreCase))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
}
if (field != null)
diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs
index 4e3dcd65..229fc22a 100644
--- a/Dapper/SqlMapper.cs
+++ b/Dapper/SqlMapper.cs
@@ -2798,7 +2798,7 @@ namespace Dapper
)
{
var returnType = type.IsValueType() ? typeof(object) : type;
- var dm = new DynamicMethod($"Deserialize{Guid.NewGuid()}", returnType, new[] { typeof(IDataReader) }, true);
+ var dm = new DynamicMethod($"Deserialize{Guid.NewGuid()}", returnType, new[] { typeof(IDataReader) }, type, true);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int));
il.DeclareLocal(type);