mirror of
https://github.com/DapperLib/Dapper.git
synced 2026-10-17 05:12:13 +08:00
Underscore handling (#1947)
* Adds MatchConstructorParametersWithUnderscores option * generalize to single MatchNamesWithUnderscores (pre-existing) * cite 2nd PR --------- Co-authored-by: Jonas Goronczy <goronczy.jonas@gmail.com>
This commit is contained in:
+60
-12
@@ -74,8 +74,16 @@ namespace Dapper
|
||||
int i = 0;
|
||||
for (; i < ctorParameters.Length; i++)
|
||||
{
|
||||
if (!string.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase))
|
||||
if (EqualsCI(ctorParameters[i].Name, names[i]))
|
||||
{ } // exact match
|
||||
else if (MatchNamesWithUnderscores && EqualsCIU(ctorParameters[i].Name, names[i]))
|
||||
{ } // match after applying underscores
|
||||
else
|
||||
{
|
||||
// not a name match
|
||||
break;
|
||||
}
|
||||
|
||||
if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary)
|
||||
continue;
|
||||
var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;
|
||||
@@ -119,9 +127,8 @@ namespace Dapper
|
||||
/// <returns>Mapping implementation</returns>
|
||||
public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
|
||||
{
|
||||
var parameters = constructor.GetParameters();
|
||||
|
||||
return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)));
|
||||
ParameterInfo param = MatchFirstOrDefault(constructor.GetParameters(), columnName, static p => p.Name);
|
||||
return new SimpleMemberMap(columnName, param);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -131,14 +138,7 @@ namespace Dapper
|
||||
/// <returns>Mapping implementation</returns>
|
||||
public SqlMapper.IMemberMap GetMember(string columnName)
|
||||
{
|
||||
var property = Properties.Find(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
|
||||
?? Properties.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (property == null && MatchNamesWithUnderscores)
|
||||
{
|
||||
property = Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
|
||||
?? Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
var property = MatchFirstOrDefault(Properties, columnName, static p => p.Name);
|
||||
|
||||
if (property != null)
|
||||
return new SimpleMemberMap(columnName, property);
|
||||
@@ -174,6 +174,54 @@ namespace Dapper
|
||||
/// </summary>
|
||||
public static bool MatchNamesWithUnderscores { get; set; }
|
||||
|
||||
static T MatchFirstOrDefault<T>(IList<T> members, string name, Func<T, string> selector) where T : class
|
||||
{
|
||||
if (members is { Count: > 0 })
|
||||
{
|
||||
// try exact first
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (string.Equals(name, selector(member), StringComparison.Ordinal))
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
// then exact ignoring case
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (string.Equals(name, selector(member), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
if (MatchNamesWithUnderscores)
|
||||
{
|
||||
// same again, minus underscore delta
|
||||
name = name?.Replace("_", "");
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (string.Equals(name, selector(member)?.Replace("_", ""), StringComparison.Ordinal))
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (string.Equals(name, selector(member)?.Replace("_", ""), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static bool EqualsCI(string x, string y)
|
||||
=> string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
|
||||
internal static bool EqualsCIU(string x, string y)
|
||||
=> string.Equals(x?.Replace("_", ""), y?.Replace("_", ""), StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// The settable properties for this typemap
|
||||
/// </summary>
|
||||
|
||||
@@ -22,6 +22,8 @@ Note: to get the latest pre-release build, add ` -Pre` to the end of the command
|
||||
|
||||
### unreleased
|
||||
|
||||
- add underscore handling with constructors (#1786 via @jo-goro, fixes #818; also #1947 via mgravell)
|
||||
|
||||
(note: new PRs will not be merged until they add release note wording here)
|
||||
|
||||
### 2.0.143
|
||||
|
||||
@@ -220,5 +220,45 @@ SELECT * FROM @ExplicitConstructors"
|
||||
var output = connection.Query<WithPrivateConstructor>("select 1 as Foo").First();
|
||||
Assert.Equal(1, output.Foo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CtorWithUnderscores()
|
||||
{
|
||||
var obj = connection.QueryFirst<Type_ParamsWithUnderscores>("select 'abc' as FIRST_NAME, 'def' as LAST_NAME");
|
||||
Assert.NotNull(obj);
|
||||
Assert.Equal("abc", obj.FirstName);
|
||||
Assert.Equal("def", obj.LastName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CtorWithoutUnderscores()
|
||||
{
|
||||
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
var obj = connection.QueryFirst<Type_ParamsWithoutUnderscores>("select 'abc' as FIRST_NAME, 'def' as LAST_NAME");
|
||||
Assert.NotNull(obj);
|
||||
Assert.Equal("abc", obj.FirstName);
|
||||
Assert.Equal("def", obj.LastName);
|
||||
}
|
||||
|
||||
class Type_ParamsWithUnderscores
|
||||
{
|
||||
public string FirstName { get; }
|
||||
public string LastName { get; }
|
||||
public Type_ParamsWithUnderscores(string first_name, string last_name)
|
||||
{
|
||||
FirstName = first_name;
|
||||
LastName = last_name;
|
||||
}
|
||||
}
|
||||
class Type_ParamsWithoutUnderscores
|
||||
{
|
||||
public string FirstName { get; }
|
||||
public string LastName { get; }
|
||||
public Type_ParamsWithoutUnderscores(string firstName, string lastName)
|
||||
{
|
||||
FirstName = firstName;
|
||||
LastName = lastName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,5 +1273,26 @@ insert TPTable (Value) values (2), (568)");
|
||||
public int Id { get; }
|
||||
public string Name { get; } = "abc";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestConstructorParametersWithUnderscoredColumns()
|
||||
{
|
||||
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
var obj = connection.QuerySingle<HazGetOnlyAndCtor>("select 42 as [id_property], 'def' as [name_property];");
|
||||
Assert.Equal(42, obj.IdProperty);
|
||||
Assert.Equal("def", obj.NameProperty);
|
||||
}
|
||||
|
||||
private class HazGetOnlyAndCtor
|
||||
{
|
||||
public int IdProperty { get; }
|
||||
public string NameProperty { get; }
|
||||
|
||||
public HazGetOnlyAndCtor(int idProperty, string nameProperty)
|
||||
{
|
||||
IdProperty = idProperty;
|
||||
NameProperty = nameProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user