Repository: phnx47/dapper-repositories
Pull request #456: Add and chain ConfigureAwait(false) on all awaits to allow blocking on async methods without deadlocking
Author: login: dhsu-atlas
Created: 2025-03-07T00:54:45Z | Merged: 2025-03-07T03:03:54Z

For general-purpose library code, adding ConfigureAwait(false) for every await allows .NET Framework applications that have a SynchronizationContext to call async code synchronously without deadlocking. The 'false' argument to ConfigureAwait allows the continuation to complete on any available thread in the pool and not just the original one. Since the context of the application is unknown to the library it can't use it anyway. To prevent future changes from reintroducing await's without a ConfigureAwait(false), the .editorconfig has been updated to treat these as errors and EnableNETAnalyzers added to the csproj. Tests are excluded from this rule as they are not expected to have a SynchronizationContext.

How this affects different applications:

* No impact on .NET Core apps. Async or synchronous calling is supported.
* No impact on .NET Framework apps that do not rely on a SynchronizationContext. Async or synchronous calling is supported.
* .NET Framework apps using SynchronizationContext (ASP.NET, WPF, Winforms) will no longer deadlock on synchronous calls as long as all await'ed async calls include ConfigureAwait(false).

Reference: https://devblogs.microsoft.com/dotnet/configureawait-faq/#when-should-i-use-configureawait(false)


Diff:
diff --git a/.editorconfig b/.editorconfig
index 46a83433..59cb2b23 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -76,3 +76,7 @@ dotnet_naming_rule.private_fields_with_underscore.severity = suggestion
 
 dotnet_naming_symbols.private_fields.applicable_kinds = field
 dotnet_naming_symbols.private_fields.applicable_accessibilities = private
+
+## Code Quality Analysis
+dotnet_diagnostic.CA2007.severity = error # Use ConfigureAwait
+configure_await_analysis_mode = library
diff --git a/src/DapperRepository.BulkInsert.cs b/src/DapperRepository.BulkInsert.cs
index 8b37af5e..f08ef6ee 100644
--- a/src/DapperRepository.BulkInsert.cs
+++ b/src/DapperRepository.BulkInsert.cs
@@ -111,7 +111,7 @@ public virtual async Task<int> BulkInsertAsync(IEnumerable<TEntity> instances, I
                     var items = instances.Skip(i * maxAllowedInstancesPerBatch).Take(maxAllowedInstancesPerBatch);
                     var msSqlQueryResult = SqlGenerator.GetBulkInsert(items);
                     count += await Connection.ExecuteAsync(new CommandDefinition(msSqlQueryResult.GetSql(), msSqlQueryResult.Param, transaction,
-                        cancellationToken: cancellationToken));
+                        cancellationToken: cancellationToken)).ConfigureAwait(false);
                 }
 
                 return count;
diff --git a/src/DapperRepository.BulkUpdate.cs b/src/DapperRepository.BulkUpdate.cs
index ce9d9a3b..17534faa 100644
--- a/src/DapperRepository.BulkUpdate.cs
+++ b/src/DapperRepository.BulkUpdate.cs
@@ -104,7 +104,7 @@ public virtual async Task<bool> BulkUpdateAsync(IEnumerable<TEntity> instances,
                     var items = instances.Skip(skips).Take(maxAllowedInstancesPerBatch);
                     var msSqlQueryResult = SqlGenerator.GetBulkUpdate(items);
                     count += await Connection.ExecuteAsync(new CommandDefinition(msSqlQueryResult.GetSql(), msSqlQueryResult.Param, transaction,
-                        cancellationToken: cancellationToken));
+                        cancellationToken: cancellationToken)).ConfigureAwait(false);
                 }
 
                 return count > 0;
@@ -112,7 +112,7 @@ public virtual async Task<bool> BulkUpdateAsync(IEnumerable<TEntity> instances,
         }
 
         var queryResult = SqlGenerator.GetBulkUpdate(instances);
-        var result = await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken)) > 0;
+        var result = await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
         return result;
     }
 }
diff --git a/src/DapperRepository.Delete.cs b/src/DapperRepository.Delete.cs
index f8b27b89..8de7b23f 100644
--- a/src/DapperRepository.Delete.cs
+++ b/src/DapperRepository.Delete.cs
@@ -80,7 +80,7 @@ public virtual async Task<bool> DeleteAsync(TEntity instance, IDbTransaction? tr
         if (timeout.HasValue)
             commandTimeout = timeout.Value.Seconds;
         var deleted = await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, commandTimeout,
-            cancellationToken: cancellationToken)) > 0;
+            cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
         return deleted;
     }
 
@@ -106,7 +106,7 @@ public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>>? pre
         if (timeout.HasValue)
             commandTimeout = timeout.Value.Seconds;
         var deleted = await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, commandTimeout,
-            cancellationToken: cancellationToken)) > 0;
+            cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
         return deleted;
     }
 }
diff --git a/src/DapperRepository.ExecuteJoinQuery.cs b/src/DapperRepository.ExecuteJoinQuery.cs
index 25b1ef57..2eff2c83 100644
--- a/src/DapperRepository.ExecuteJoinQuery.cs
+++ b/src/DapperRepository.ExecuteJoinQuery.cs
@@ -167,7 +167,7 @@ protected virtual async Task<IEnumerable<TEntity>> ExecuteJoinQueryAsync<TChild1
                 await Connection.QueryAsync<TEntity, TChild1, TEntity>(new CommandDefinition(sqlQuery.GetSql(),
                         sqlQuery.Param, transaction, flags: buffered, cancellationToken: cancellationToken), (entity, child1) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             case 2:
@@ -175,7 +175,7 @@ await Connection.QueryAsync<TEntity, TChild1, TChild2, TEntity>(new CommandDefin
                         sqlQuery.Param, transaction, flags: buffered, cancellationToken: cancellationToken), (entity, child1, child2) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1,
                             child2),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             case 3:
@@ -183,7 +183,7 @@ await Connection.QueryAsync<TEntity, TChild1, TChild2, TChild3, TEntity>(new Com
                         sqlQuery.Param, transaction, flags: buffered, cancellationToken: cancellationToken), (entity, child1, child2, child3) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1,
                             child2, child3),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             case 4:
@@ -191,7 +191,7 @@ await Connection.QueryAsync<TEntity, TChild1, TChild2, TChild3, TChild4, TEntity
                         sqlQuery.Param, transaction, flags: buffered, cancellationToken: cancellationToken), (entity, child1, child2, child3, child4) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1,
                             child2, child3, child4),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             case 5:
@@ -200,7 +200,7 @@ await Connection.QueryAsync<TEntity, TChild1, TChild2, TChild3, TChild4, TChild5
                     (entity, child1, child2, child3, child4, child5) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1,
                             child2, child3, child4, child5),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             case 6:
@@ -209,7 +209,7 @@ await Connection.QueryAsync<TEntity, TChild1, TChild2, TChild3, TChild4, TChild5
                     (entity, child1, child2, child3, child4, child5, child6) =>
                         EntityJoinMapping<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(lookup, keyProperties, childKeyProperties, childProperties, entity, child1,
                             child2, child3, child4, child5, child6),
-                    spiltOn);
+                    spiltOn).ConfigureAwait(false);
                 break;
 
             default:
diff --git a/src/DapperRepository.Insert.cs b/src/DapperRepository.Insert.cs
index 241ccb10..78a577c1 100644
--- a/src/DapperRepository.Insert.cs
+++ b/src/DapperRepository.Insert.cs
@@ -67,18 +67,18 @@ public virtual async Task<bool> InsertAsync(TEntity instance, IDbTransaction? tr
         {
             if (SqlGenerator.Provider == Repositories.SqlGenerator.SqlProvider.Oracle)
             {
-                await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken));
+                await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false);
                 int newId = ((DynamicParameters)(queryResult.Param!)).Get<int>(":newId");
                 return SetValue(newId, instance);
             }
             else
             {
-                var newId = (await Connection.QueryAsync<long>(queryResult.GetSql(), queryResult.Param, transaction)).FirstOrDefault();
+                var newId = (await Connection.QueryAsync<long>(queryResult.GetSql(), queryResult.Param, transaction).ConfigureAwait(false)).FirstOrDefault();
                 return SetValue(newId, instance);
             }
         }
 
-        return await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), instance, transaction, cancellationToken: cancellationToken)) > 0;
+        return await Connection.ExecuteAsync(new CommandDefinition(queryResult.GetSql(), instance, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
     }
 
     private bool SetValue(long newId, TEntity instance)
diff --git a/src/DapperRepository.Update.cs b/src/DapperRepository.Update.cs
index 2945a3c0..64ed42a3 100644
--- a/src/DapperRepository.Update.cs
+++ b/src/DapperRepository.Update.cs
@@ -50,7 +50,7 @@ public virtual async Task<bool> UpdateAsync(TEntity instance, IDbTransaction? tr
         params Expression<Func<TEntity, object>>[] includes)
     {
         var sqlQuery = SqlGenerator.GetUpdate(instance, includes);
-        var updated = await Connection.ExecuteAsync(new CommandDefinition(sqlQuery.GetSql(), sqlQuery.Param, transaction, cancellationToken: cancellationToken)) > 0;
+        var updated = await Connection.ExecuteAsync(new CommandDefinition(sqlQuery.GetSql(), sqlQuery.Param, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
         return updated;
     }
 
@@ -92,7 +92,7 @@ public virtual async Task<bool> UpdateAsync(Expression<Func<TEntity, bool>>? pre
         params Expression<Func<TEntity, object>>[] includes)
     {
         var sqlQuery = SqlGenerator.GetUpdate(predicate, instance, includes);
-        var updated = await Connection.ExecuteAsync(new CommandDefinition(sqlQuery.GetSql(), sqlQuery.Param, transaction, cancellationToken: cancellationToken)) > 0;
+        var updated = await Connection.ExecuteAsync(new CommandDefinition(sqlQuery.GetSql(), sqlQuery.Param, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false) > 0;
         return updated;
     }
 }
diff --git a/src/MicroOrm.Dapper.Repositories.csproj b/src/MicroOrm.Dapper.Repositories.csproj
index 57e85723..e5834fbb 100644
--- a/src/MicroOrm.Dapper.Repositories.csproj
+++ b/src/MicroOrm.Dapper.Repositories.csproj
@@ -20,6 +20,7 @@
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <PackageOutputPath>nupkgs</PackageOutputPath>
     <IsPackable>true</IsPackable>
+    <EnableNETAnalyzers>true</EnableNETAnalyzers>
   </PropertyGroup>
   <ItemGroup>
     <InternalsVisibleTo Include="SqlGenerator.Tests" />
diff --git a/src/ReadOnlyDapperRepository.Find.Join.cs b/src/ReadOnlyDapperRepository.Find.Join.cs
index 4dc15065..a53dc9dc 100644
--- a/src/ReadOnlyDapperRepository.Find.Join.cs
+++ b/src/ReadOnlyDapperRepository.Find.Join.cs
@@ -168,7 +168,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1);
-        return (await ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -205,7 +205,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1, tChild2);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -246,7 +246,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1, tChild2, tChild3);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -291,7 +291,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1, tChild2, tChild3, tChild4);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4))
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4).ConfigureAwait(false))
             .FirstOrDefault();
     }
 
@@ -341,7 +341,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1, tChild2, tChild3, tChild4, tChild5);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5))
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5).ConfigureAwait(false))
             .FirstOrDefault();
     }
 
@@ -396,6 +396,6 @@ public partial class ReadOnlyDapperRepository<TEntity>
     {
         var queryResult = SqlGenerator.GetSelectFirst(predicate, FilterData, tChild1, tChild2, tChild3, tChild4, tChild5, tChild6);
         return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5,
-            tChild6)).FirstOrDefault();
+            tChild6).ConfigureAwait(false)).FirstOrDefault();
     }
 }
diff --git a/src/ReadOnlyDapperRepository.FindAll.cs b/src/ReadOnlyDapperRepository.FindAll.cs
index 18a30571..3e86b604 100644
--- a/src/ReadOnlyDapperRepository.FindAll.cs
+++ b/src/ReadOnlyDapperRepository.FindAll.cs
@@ -92,6 +92,6 @@ public virtual Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity,
     public virtual async Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>>? predicate, IDbTransaction? transaction, CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectAll(predicate, FilterData);
-        return await Connection.QueryAsync<TEntity>(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken));
+        return await Connection.QueryAsync<TEntity>(new CommandDefinition(queryResult.GetSql(), queryResult.Param, transaction, cancellationToken: cancellationToken)).ConfigureAwait(false);
     }
 }
diff --git a/src/ReadOnlyDapperRepository.FindById.Join.cs b/src/ReadOnlyDapperRepository.FindById.Join.cs
index 79ba0d8e..7628ed73 100644
--- a/src/ReadOnlyDapperRepository.FindById.Join.cs
+++ b/src/ReadOnlyDapperRepository.FindById.Join.cs
@@ -172,7 +172,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1);
-        return (await ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -209,7 +209,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1, tChild2);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -250,7 +250,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1, tChild2, tChild3);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3)).FirstOrDefault();
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3).ConfigureAwait(false)).FirstOrDefault();
     }
 
 
@@ -295,7 +295,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1, tChild2, tChild3, tChild4);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4))
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4).ConfigureAwait(false))
             .FirstOrDefault();
     }
 
@@ -345,7 +345,7 @@ public partial class ReadOnlyDapperRepository<TEntity>
         CancellationToken cancellationToken)
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1, tChild2, tChild3, tChild4, tChild5);
-        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5))
+        return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5).ConfigureAwait(false))
             .FirstOrDefault();
     }
 
@@ -400,6 +400,6 @@ public partial class ReadOnlyDapperRepository<TEntity>
     {
         var queryResult = SqlGenerator.GetSelectById(id, FilterData, tChild1, tChild2, tChild3, tChild4, tChild5, tChild6);
         return (await ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, cancellationToken, tChild1, tChild2, tChild3, tChild4, tChild5,
-            tChild6)).FirstOrDefault();
+            tChild6).ConfigureAwait(false)).FirstOrDefault();
     }
 }


Reviews:
user: login: phnx47
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-07T02:41:24Z
user: login: dhsu-atlas
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T02:45:53Z
user: login: phnx47
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-07T02:51:29Z
user: login: dhsu-atlas
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T02:52:48Z

Review comments:
diff_hunk: @@ -0,0 +1,18 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2025 Atlassian US, Inc.
path: tests/.editorconfig
user: login: phnx47
body: Why is copyright needed in `tests/.editorconfig`?
created_at: 2025-03-07T02:41:24Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,18 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2025 Atlassian US, Inc.
path: tests/.editorconfig
user: login: dhsu-atlas
body: Legal requested that I include this text on new files I add. This file is a copy of what we use internally for exclusion. If this is a concern, I can revert back to them and see if I can remove it.
created_at: 2025-03-07T02:45:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,18 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2025 Atlassian US, Inc.
path: tests/.editorconfig
user: login: phnx47
body: Yes, please. You can also just remove this file, and I can add it myself to main branch.
created_at: 2025-03-07T02:51:28Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,18 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2025 Atlassian US, Inc.
path: tests/.editorconfig
user: login: dhsu-atlas
body: Ok I will push and update to remove the file.
created_at: 2025-03-07T02:52:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-07T02:47:00Z
body: ## [Codecov](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) Report
Attention: Patch coverage is `58.62069%` with `12 lines` in your changes missing coverage. Please review.
> Project coverage is 64.79%. Comparing base [(`33da63b`)](https://app.codecov.io/gh/phnx47/dapper-repositories/commit/33da63beefbdd7efa19971d192e824c3f6cdce59?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) to head [(`14f0892`)](https://app.codecov.io/gh/phnx47/dapper-repositories/commit/14f08927ba451ff29b9cdf6b29814152effc36c6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) | Patch % | Lines |
|---|---|---|
| [src/ReadOnlyDapperRepository.Find.Join.cs](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&filepath=src%2FReadOnlyDapperRepository.Find.Join.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K#diff-c3JjL1JlYWRPbmx5RGFwcGVyUmVwb3NpdG9yeS5GaW5kLkpvaW4uY3M=) | 16.66% | [5 Missing :warning: ](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) |
| [src/ReadOnlyDapperRepository.FindById.Join.cs](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&filepath=src%2FReadOnlyDapperRepository.FindById.Join.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K#diff-c3JjL1JlYWRPbmx5RGFwcGVyUmVwb3NpdG9yeS5GaW5kQnlJZC5Kb2luLmNz) | 33.33% | [4 Missing :warning: ](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) |
| [src/DapperRepository.ExecuteJoinQuery.cs](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&filepath=src%2FDapperRepository.ExecuteJoinQuery.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K#diff-c3JjL0RhcHBlclJlcG9zaXRvcnkuRXhlY3V0ZUpvaW5RdWVyeS5jcw==) | 50.00% | [3 Missing :warning: ](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #456   +/-   ##
=======================================
  Coverage   64.79%   64.79%           
=======================================
  Files          59       59           
  Lines        1909     1909           
  Branches      376      376           
=======================================
  Hits         1237     1237           
  Misses        567      567           
  Partials      105      105           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/phnx47/dapper-repositories/pull/456?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Serge+K).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: phnx47
created_at: 2025-03-07T03:04:59Z
body: Thank you!
author_association: OWNER
user: login: phnx47
created_at: 2025-03-07T07:29:56Z
body: Published [v1.28.0](https://www.nuget.org/packages/MicroOrm.Dapper.Repositories/1.28.0)
author_association: OWNER