Reducing the number of queries created with Decimal data types
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
Note: The goal of this issue is to determine if I should submit a pull-request that I think will make the development experience better when facing one particular problem. My understanding of best-practice is to open an issue first to discuss before simply submitting a PR. I have searched StackExchange and this repository for anything that might already have been done relating to this issue and was unable to find anything. If this has already been addressed, or if I have misunderstood something, please let me know.
As I understand it today, the default behavior for decimal (numeric) data types is for Dapper to submit queries using the smallest possible data type that the data will fit in. Thus, if the value is 123.45, it will be submitted in the query as decimal(3,2) assuming the type has not been explicitly specified. This is a very reasonable default, but does result in what is ultimately different queries being submitted to the DB depending on the values of the parameters.
These different queries are not usually a problem, but can make analysis of those queries more difficult, can cause some very small performance impact since more query plans need to be generated, and in those rare situations where the query plan needs to be pinned, can make that task much more difficult since there can be hundreds of queries depending on the variability of the data.
This problem is solvable by explicitly giving Dapper a data type that matches the column's data type in the database. This way, the query will always be submitted using that type, and only 1 query will be created regardless of how many times the query is executed and the variability of the data. Often, that looks something like this:
var parameters = new DynamicParameters();
parameters.Add("decimalParameter1", valueForParameter1, DbType.Decimal, ParameterDirection.Input, 5, 9, 2);
conn.Execute(query, parameters);
The 5 that preceeds the precision and scale values (9,2) in the parameters.Add(...) method represents the size of the parameter value, and can be calculated using this code block:
// Values from Transact-SQL Reference > Numeric > decimal & numeric
// https://docs.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver15
int result = 5;
if (precision > 28) result = 17;
else if (precision > 19) result = 13;
else if (precision > 9) result = 9;
The resulting syntax is not horrible by any stretch of the imagination, but it certainly could be cleaner, especially when there are a number of decimal data types in the same query. Also, the functionality in the code block above that calculates the size should probably be encapsulated somewhere so it doesn't have to proliferate. It makes sense to me that this code should live in the Dapper project if possible.
Looking through the project, I see that there is a DbString object that encapsulates some of the complexities of using Dapper with string data types. Following this pattern, I created a similar DbDecimal object that also implements SqlMapper.ICustomQueryParameter and allows the developer to specify the precision and scale values on constructions. This creates queries that are strongly typed, while allowing the query syntax to look something like this:
var parameters = new DynamicParameters();
parameters.Add("decimalParameter1", new DbDecimal(9, 2, valueForParameter1));
conn.Execute(query, parameters);
Is this DbDecimal object something that people believe belongs in the core project? Is there a better way to simplify this story for developers? I look forward to your input.
FWIW: If we decide to go forward with this and do a PR, we'll need to decide what defaults (if any) there should be for the precision and scale values.
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
Review the existing DbString pattern and the SqlMapper.ICustomQueryParameter and DynamicParameters usage first. Decide whether a DbDecimal belongs in the core project, including precision, scale, and size defaults; done means a settled API and design suitable for a pull request.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend-api-design, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100