From bebe80bcaff983433ea8043f3e432fd191915035 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 26 Feb 2016 08:51:04 +0000 Subject: [PATCH] Support get-only C# 6 properties, i.e. `public int Id {get;}` --- Dapper.Tests/Dapper.Tests.xproj | 4 +++- Dapper.Tests/Tests.cs | 14 +++++++++++++- Dapper/DefaultTypeMap.cs | 18 +++++++++++++++--- Dapper/SqlMapper.cs | 2 +- 4 files changed, 32 insertions(+), 6 deletions(-) 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);