DapperLib / DapperLib/Dapper

Reducing the number of queries created with Decimal data types

Open
#1,536 5 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.