Add support for order-agnostic constructor parameter SqlMapper.ITypeMap implementations
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
Background
I love Dapper because it adds great support for hydrating immutable objects from a database. For example, it allows us to hydrate objects of the following type like so:
public class Example {
public Guid Id { get; }
public String Name { get; }
public DateTime Created { get; }
public Guid CreatedBy { get; }
public DateTime Updated { get; }
public Guid UpdatedBy { get; }
public Example(
Guid id,
String name,
DateTime created,
Guid createdBy,
DateTime updated,
Guid updatedBy) {
this.Id = id;
this.Name = name;
this.Created = created;
this.CreatedBy = createdBy;
this.Updated = updated;
this.UpdatedBy = updatedBy;
}
}
public IEnumerable<Example> GetExamples() {
var columns = String.Join(
","
typeof(Example)
.GetTypeInfo()
.GetProperties()
.Select(property => $"`{property.Name}`")
);
return this.connection.Query<Example>($"SELECT {columns} FROM ExampleTable");
}
Problem
Unfortunately, the columns provided to the query parameters must be in the same order that the constructor parameters are in. So, given the above example, if I changed the Example to look like this...
public class Example {
public Guid Id { get; }
public String Name { get; }
/* [ ... the rest removed for emphasis ... ] */
public Example(
String name,
Guid id,
/* [ ... the rest removed for emphasis ... ] */) {
}
}
... it will fail. Here is where I'm not 100% because I have a hard time reading the IL, but my understanding is that first it will fail because the DefaultTypeMap won't find the constructor because the names are in a different order. You can get around this by making your own SqlMapper.ITypeMap implementation that does not care about the order of the SQL query values matching the order of the constructor. However, this just makes the code fail (via, at least in .NET core, with weird NullReferenceExceptions) later because the SqlMapper code enumerates on the constructor parameters with the assumption they are in the same order as the SQL query.
Suggestion
Update the SqlMapper logic here such that it does not enumerable over the ConstructorParameter objects assuming they are in the same order from which they were returned from the query.
Workaround
The workaround for this is to fetch the SqlMapper.ITypeMap implementation Dapper has plugged in for the type you're about to query via this method. From there you can fetch the ConstructorInfo being used, if one happens to be used. This has the following negative consequences:
- You have to reproduce the ConstructorInfo processing code which involves Reflection, and you have to call it every time in case the
SqlMapper.ITypeMapimplementation for the objects you are hydrating changed (and even then there's a race condition). - You have to add branching to your code if you have a mix of immutable and mutable objects that you hydrate to account for objects which have property setters instead of constructor arguments.
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
Read constructor matching in Dapper/DefaultTypeMap.cs and the materialization logic around SqlMapper.cs line 3007, then inspect the type-map lookup around line 2798. Verify how a custom SqlMapper.ITypeMap supplies constructor parameters and define completion as supporting differing SQL-column and constructor-parameter orders without the reported later failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend, database
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100