SqlMapper ignores culture when using parameterless default constructor
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
I have a simple POCO to feed comboboxes:
internal class IdDatumKm
{
public int Id { get; set; }
public string Datum { get; set; }
public string KM { get; set; }
internal IdDatumKm() { } // parameterless default constructor
internal IdDatumKm(int id, DateTime datum, double km) // constructor with matching signature for SELECT
{
Id = id;
Datum = datum.ToString();
KM = km.ToString("F0");
}
}
Please note that Datum will contain the culture-specific text representation of a DateTime.
When using cboDatasource = connection.Query<IdDatumKm>(sql...).AsList(); the DefaultTypeMap.FindConstructor-method will find the parameterless default constructor because the results are ordered by the number of parameters:
foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))
So SqlMapper generates code that maps the columns from the query to the POCO fields automatically, but it ignores the culture and causes the Datum to contain "10/07/2022 14:30:22" instead of "07.10.2022 14:30:22" as given by the current culture which is set by:
var ci = new CultureInfo("DE-de");
CultureInfo.DefaultThreadCurrentCulture = ci;
CultureInfo.DefaultThreadCurrentUICulture = ci;
My current (and somewhat dirty) workaround is to set access to "public" instead of "internal" to the constructor with matching signature, so the OrderBy clause in FindConstructor will give it a higher rating. With this constructor the DateTime is formatted with the correct culture.
I looked through the SqlMapper code but was not able to find the spot where the real conversion from DateTime to string happens. This inline code generation in SqlMapper is really cool stuff, but difficult to read. I cannot see why it ignores the current culture (or maybe use invariant?) with the parameterless ctor but with the matching ctor it works fine.
BTW: why is FindConstructor giving precedence to the parameterless ctor? Shouldn't it use the matching ctor if there is one? For example when it has to set other fields with defaults or it validates the incoming values and sets error flags or whatever.
Thanks a lot!
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in SqlMapper and trace the generated mapping path used after DefaultTypeMap.FindConstructor selects the parameterless constructor. Compare that path with the matching-constructor path described in the issue, then inspect existing mapping tests if available. Done means the intended culture behavior and constructor-selection behavior are covered consistently without relying on constructor visibility.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100