From 5472d04cca27140e9deb964bd9100a0217814309 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Sat, 11 Nov 2023 08:37:35 +0000 Subject: [PATCH] fix #1993 - when matching properties, prefer non-normalized property name --- Dapper/DefaultTypeMap.cs | 18 +++++++ tests/Dapper.Tests/ConstructorTests.cs | 67 +++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/Dapper/DefaultTypeMap.cs b/Dapper/DefaultTypeMap.cs index 38c3cbd79..98029741a 100644 --- a/Dapper/DefaultTypeMap.cs +++ b/Dapper/DefaultTypeMap.cs @@ -206,6 +206,24 @@ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, { // same again, minus underscore delta name = name?.Replace("_", ""); + + // match normalized column name vs actual property name + foreach (var member in members) + { + if (string.Equals(name, selector(member), StringComparison.Ordinal)) + { + return member; + } + } + foreach (var member in members) + { + if (string.Equals(name, selector(member), StringComparison.OrdinalIgnoreCase)) + { + return member; + } + } + + // match normalized column name vs normalized property name foreach (var member in members) { if (string.Equals(name, selector(member)?.Replace("_", ""), StringComparison.Ordinal)) diff --git a/tests/Dapper.Tests/ConstructorTests.cs b/tests/Dapper.Tests/ConstructorTests.cs index 07e7c638a..927c030a4 100644 --- a/tests/Dapper.Tests/ConstructorTests.cs +++ b/tests/Dapper.Tests/ConstructorTests.cs @@ -1,5 +1,4 @@ using System; -using System.Data; using System.Linq; using Xunit; @@ -241,6 +240,72 @@ public void CtorWithoutUnderscores() Assert.Equal("def", obj.LastName); } + [Fact] + public void Issue1993_PreferPropertyOverField() // https://github.com/DapperLib/Dapper/issues/1993 + { + var oldValue = DefaultTypeMap.MatchNamesWithUnderscores; + try + { + DefaultTypeMap.MatchNamesWithUnderscores = true; + + var map = new DefaultTypeMap(typeof(ShowIssue1993)); + var first = map.GetMember("field_first"); + Assert.NotNull(first); + Assert.Null(first.Field); + Assert.Equal(nameof(ShowIssue1993.FieldFirst), first.Property?.Name); + + var last = map.GetMember("field_last"); + Assert.NotNull(last); + Assert.Null(last.Field); + Assert.Equal(nameof(ShowIssue1993.FieldLast), last.Property?.Name); + } + finally + { + DefaultTypeMap.MatchNamesWithUnderscores = oldValue; + } + } + + [Fact] + public void Issue1993_Query() + { + var oldValue = DefaultTypeMap.MatchNamesWithUnderscores; + try + { + DefaultTypeMap.MatchNamesWithUnderscores = true; + + var obj = connection.QueryFirst("select 'abc' as field_first, 'def' as field_last"); + Assert.Equal("abc", obj.FieldFirst); + Assert.Equal("def", obj.FieldLast); + + Assert.Equal("abc", obj.AltFieldFirst); + Assert.Equal("def", obj.AltFieldLast); + } + finally + { + DefaultTypeMap.MatchNamesWithUnderscores = oldValue; + } + } + + public class ShowIssue1993 + { + private string _fieldFirst { get; set; } = null!; // not actually a field + public string FieldFirst + { + get => _fieldFirst; + set => _fieldFirst = AltFieldFirst = value; + } + + public string FieldLast + { + get => _fieldLast; + set => _fieldLast = AltFieldLast = value; + } + private string _fieldLast { get; set; } = null!;// not actually a field + + public string AltFieldFirst { get; set; } = null!; + public string AltFieldLast { get; set; } = null!; + } + class Type_ParamsWithUnderscores { public string FirstName { get; }