diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 173531df..ba2aa93c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,8 +46,8 @@ jobs: run: dotnet test tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj -c Release --logger GitHubActions /p:CI=true env: MySqlConnectionString: Server=localhost;Port=${{ job.services.mysql.ports[3306] }};Uid=root;Pwd=root;Database=test;Allow User Variables=true - OLEDBConnectionString: Provider=SQLOLEDB;Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.; + OLEDBConnectionString: Provider=SQLOLEDB;Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;TrustServerCertificate=True; PostgesConnectionString: Server=localhost;Port=${{ job.services.postgres.ports[5432] }};Database=test;User Id=postgres;Password=postgres; - SqlServerConnectionString: Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.; + SqlServerConnectionString: Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;TrustServerCertificate=True; - name: .NET Lib Pack run: dotnet pack Build.csproj --no-build -c Release /p:PackageOutputPath=%CD%\.nupkgs /p:CI=true diff --git a/Dapper.sln b/Dapper.sln index e993c7a4..19eed964 100644 --- a/Dapper.sln +++ b/Dapper.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28917.182 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35312.102 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A34907DF-958A-4E4C-8491-84CF303FD13E}" ProjectSection(SolutionItems) = preProject @@ -11,12 +11,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props docs\index.md = docs\index.md License.txt = License.txt + .github\workflows\main.yml = .github\workflows\main.yml nuget.config = nuget.config Readme.md = Readme.md version.json = version.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Contrib", "Dapper.Contrib\Dapper.Contrib.csproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Contrib", "src\Dapper.Contrib\Dapper.Contrib.csproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Tests.Contrib", "tests\Dapper.Tests.Contrib\Dapper.Tests.Contrib.csproj", "{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}" EndProject diff --git a/Readme.md b/Readme.md index 9a1fd892..9acdd59d 100644 --- a/Readme.md +++ b/Readme.md @@ -133,10 +133,10 @@ Special Attributes ---------- Dapper.Contrib makes use of some optional attributes: -* `[Table("Tablename")]` - use another table name instead of the (by default pluralized) name of the class +* `[Table("Tablename", Schema = "SchemaName")]` - use another table name instead of the (by default pluralized) name of the class, Schema can be optionally passed as well. ```csharp - [Table ("emps")] + [Table ("emps", Schema = "emp")] public class Employee { public int Id { get; set; } diff --git a/appveyor.yml b/appveyor.yml index d91b9d19..53a05626 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -image: Visual Studio 2019 +image: Visual Studio 2022 skip_branch_with_pr: true skip_tags: true @@ -9,14 +9,14 @@ skip_commits: environment: Appveyor: true # Postgres - POSTGRES_PATH: C:\Program Files\PostgreSQL\9.6 + POSTGRES_PATH: C:\Program Files\PostgreSQL\13 PGUSER: postgres PGPASSWORD: Password12! POSTGRES_ENV_POSTGRES_USER: postgres POSTGRES_ENV_POSTGRES_PASSWORD: Password12! POSTGRES_ENV_POSTGRES_DB: test # MySQL - MYSQL_PATH: C:\Program Files\MySql\MySQL Server 5.7 + MYSQL_PATH: C:\Program Files\MySQL\MySQL Server 8.0 MYSQL_PWD: Password12! MYSQL_ENV_MYSQL_USER: root MYSQL_ENV_MYSQL_PASSWORD: Password12! @@ -25,16 +25,17 @@ environment: MySqlConnectionString: Server=localhost;Database=test;Uid=root;Pwd=Password12! OLEDBConnectionString: Provider=SQLOLEDB;Data Source=(local)\SQL2019;Initial Catalog=tempdb;User Id=sa;Password=Password12! PostgesConnectionString: Server=localhost;Port=5432;User Id=postgres;Password=Password12!;Database=test - SqlServerConnectionString: Server=(local)\SQL2019;Database=tempdb;User ID=sa;Password=Password12! + SqlServerConnectionString: Server=(local)\SQL2019;Database=tempdb;User ID=sa;Password=Password12!;TrustServerCertificate=True -services: - - mysql - - postgresql +services: + - postgresql13 init: + - ps: Start-Service MySQL80 - git config --global core.autocrlf input - SET PATH=%POSTGRES_PATH%\bin;%MYSQL_PATH%\bin;%PATH% - net start MSSQL$SQL2019 + nuget: disable_publish_on_pr: true diff --git a/src/Dapper.Contrib/Dapper.Contrib.csproj b/src/Dapper.Contrib/Dapper.Contrib.csproj index 86a0cbb2..1ec842c9 100644 --- a/src/Dapper.Contrib/Dapper.Contrib.csproj +++ b/src/Dapper.Contrib/Dapper.Contrib.csproj @@ -5,12 +5,12 @@ orm;sql;micro-orm;dapper The official collection of get, insert, update and delete helpers for Dapper.net. Also handles lists of entities and optional "dirty" tracking of interface-based entities. Sam Saffron;Johan Danforth - net461;netstandard2.0;net5.0 + net6.0;net7.0;net8.0 false $(NoWarn);CA1050 - + @@ -19,4 +19,12 @@ + + + all + runtime; build; native; contentfiles; analyzers + + + + \ No newline at end of file diff --git a/src/Dapper.Contrib/SqlMapperExtensions.cs b/src/Dapper.Contrib/SqlMapperExtensions.cs index 9a30e805..a6be48d4 100644 --- a/src/Dapper.Contrib/SqlMapperExtensions.cs +++ b/src/Dapper.Contrib/SqlMapperExtensions.cs @@ -1,17 +1,16 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; -using System.Text; -using System.Collections.Concurrent; using System.Reflection.Emit; -using System.Threading; - +using System.Text; using Dapper; namespace Dapper.Contrib.Extensions { + /// /// The Dapper.Contrib extensions for Dapper /// @@ -187,7 +186,7 @@ public static T Get(this IDbConnection connection, dynamic id, IDbTransaction if (type.IsInterface) { - if (!(connection.Query(sql, dynParams).FirstOrDefault() is IDictionary res)) + if (connection.Query(sql, dynParams).FirstOrDefault() is not IDictionary res) { return null; } @@ -292,6 +291,13 @@ private static string GetTableName(Type type) type.GetCustomAttribute(false)?.Name ?? (type.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic)?.Name; + var tableAttrSchema = type.GetCustomAttribute(false)?.Schema; + + if (!string.IsNullOrEmpty(tableAttrSchema)) + { + tableAttrName = $"{tableAttrSchema}.{tableAttrName}"; + } + if (tableAttrName != null) { name = tableAttrName; @@ -299,7 +305,7 @@ private static string GetTableName(Type type) else { name = type.Name + "s"; - if (type.IsInterface && name.StartsWith("I")) + if (type.IsInterface && name.StartsWith('I')) name = name.Substring(1); } } @@ -601,7 +607,7 @@ public static T GetInterfaceProxy() return (T)Activator.CreateInstance(generatedType); } - private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder) + private static MethodBuilder CreateIsDirtyProperty(TypeBuilder typeBuilder) { var propType = typeof(bool); var field = typeBuilder.DefineField("_" + nameof(IProxy.IsDirty), propType, FieldAttributes.Private); @@ -642,7 +648,7 @@ private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder) return currSetPropMthdBldr; } - private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propType, MethodInfo setIsDirtyMethod, bool isIdentity) + private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propType, MethodBuilder setIsDirtyMethod, bool isIdentity) { //Define the field and the property var field = typeBuilder.DefineField("_" + propertyName, propType, FieldAttributes.Private); @@ -710,15 +716,22 @@ public class TableAttribute : Attribute /// Creates a table mapping to a specific name for Dapper.Contrib commands /// /// The name of this table in the database. - public TableAttribute(string tableName) + /// The schema name of this table in the database + public TableAttribute(string tableName, string schema = "") { Name = tableName; + Schema = schema; } /// /// The name of the table in the database /// public string Name { get; set; } + + /// + /// The schema of the table in the database + /// + public string Schema { get; set; } } /// diff --git a/tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj b/tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj index d217f0ce..3d317311 100644 --- a/tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj +++ b/tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj @@ -2,7 +2,7 @@ Dapper.Tests.Contrib Dapper Contrib Test Suite - netcoreapp3.1;net462;net5.0 + net6.0;net7.0;net8.0 $(NoWarn);CA1816;IDE0063;xUnit1004 @@ -13,11 +13,29 @@ - - - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/tests/Dapper.Tests.Contrib/TestSuite.Async.cs b/tests/Dapper.Tests.Contrib/TestSuite.Async.cs index fcd11802..849537c5 100644 --- a/tests/Dapper.Tests.Contrib/TestSuite.Async.cs +++ b/tests/Dapper.Tests.Contrib/TestSuite.Async.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Dapper.Contrib.Extensions; -using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute; using Xunit; namespace Dapper.Tests.Contrib @@ -125,7 +124,7 @@ public async Task InsertGetUpdateDeleteWithExplicitKeyAsync() var list2 = (await connection.QueryAsync("select * from ObjectY").ConfigureAwait(false)).ToList(); Assert.Equal(list2.Count, originalyCount + 1); o2 = await connection.GetAsync(id).ConfigureAwait(false); - Assert.Equal(o2.ObjectYId, id); + Assert.Equal(id, o2.ObjectYId); o2.Name = "Bar"; await connection.UpdateAsync(o2).ConfigureAwait(false); o2 = await connection.GetAsync(id).ConfigureAwait(false); @@ -305,9 +304,9 @@ private async Task InsertHelperAsync(Func, T> helper) await connection.DeleteAllAsync().ConfigureAwait(false); var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); } } @@ -343,9 +342,9 @@ private async Task UpdateHelperAsync(Func, T> helper) await connection.DeleteAllAsync().ConfigureAwait(false); var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); foreach (var user in users) { user.Name += " updated"; @@ -377,7 +376,7 @@ public async Task DeleteListAsync() private async Task DeleteHelperAsync(Func, T> helper) where T : class { - const int numberOfEntities = 10; + int numberOfEntities = 10; var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) @@ -388,14 +387,14 @@ private async Task DeleteHelperAsync(Func, T> helper) await connection.DeleteAllAsync().ConfigureAwait(false); var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); var usersToDelete = users.Take(10).ToList(); await connection.DeleteAsync(helper(usersToDelete)).ConfigureAwait(false); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities - 10); + Assert.Equal(numberOfEntities - 10, users.Count); } } @@ -413,11 +412,11 @@ public async Task GetAllAsync() await connection.DeleteAllAsync().ConfigureAwait(false); var total = await connection.InsertAsync(users).ConfigureAwait(false); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = (List)await connection.GetAllAsync().ConfigureAwait(false); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); var iusers = await connection.GetAllAsync().ConfigureAwait(false); - Assert.Equal(iusers.ToList().Count, numberOfEntities); + Assert.Equal(numberOfEntities, iusers.ToList().Count); } } @@ -436,12 +435,12 @@ public async void GetAsyncAndGetAllAsyncWithNullableValues() Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value); var value2 = await connection.GetAsync(id2).ConfigureAwait(false); - Assert.True(value2.DateValue == null); + Assert.Null(value2.DateValue); var value3 = await connection.GetAllAsync().ConfigureAwait(false); var valuesList = value3.ToList(); Assert.Equal(new DateTime(2011, 07, 14), valuesList[0].DateValue.Value); - Assert.True(valuesList[1].DateValue == null); + Assert.Null(valuesList[1].DateValue); } } diff --git a/tests/Dapper.Tests.Contrib/TestSuite.cs b/tests/Dapper.Tests.Contrib/TestSuite.cs index 71dbd8ad..8f690de9 100644 --- a/tests/Dapper.Tests.Contrib/TestSuite.cs +++ b/tests/Dapper.Tests.Contrib/TestSuite.cs @@ -5,8 +5,6 @@ using Dapper.Contrib.Extensions; using Xunit; -using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute; - namespace Dapper.Tests.Contrib { [Table("ObjectX")] @@ -247,7 +245,7 @@ public void InsertGetUpdateDeleteWithExplicitKey() var list2 = connection.Query("select * from ObjectY").ToList(); Assert.Equal(list2.Count, originalyCount + 1); o2 = connection.Get(id); - Assert.Equal(o2.ObjectYId, id); + Assert.Equal(id, o2.ObjectYId); o2.Name = "Bar"; connection.Update(o2); o2 = connection.Get(id); @@ -284,7 +282,7 @@ public void InsertGetUpdateDeleteWithExplicitKeyNamedId() var list2 = connection.Query("select * from ObjectZ").ToList(); Assert.Single(list2); o2 = connection.Get(id); - Assert.Equal(o2.Id, id); + Assert.Equal(id, o2.Id); } } @@ -298,7 +296,7 @@ public void ShortIdentity() Assert.True(id > 0); // 1-n are valid here, due to parallel tests var item = connection.Get(id); Assert.Equal(item.TheId, (short)id); - Assert.Equal(item.Name, name); + Assert.Equal(name, item.Name); } } @@ -389,9 +387,9 @@ private void InsertHelper(Func, T> helper) connection.DeleteAll(); var total = connection.Insert(helper(users)); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); } } @@ -427,9 +425,9 @@ private void UpdateHelper(Func, T> helper) connection.DeleteAll(); var total = connection.Insert(helper(users)); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); foreach (var user in users) { user.Name += " updated"; @@ -461,7 +459,7 @@ public void DeleteList() private void DeleteHelper(Func, T> helper) where T : class { - const int numberOfEntities = 10; + int numberOfEntities = 10; var users = new List(numberOfEntities); for (var i = 0; i < numberOfEntities; i++) @@ -472,14 +470,14 @@ private void DeleteHelper(Func, T> helper) connection.DeleteAll(); var total = connection.Insert(helper(users)); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); var usersToDelete = users.Take(10).ToList(); connection.Delete(helper(usersToDelete)); users = connection.Query("select * from Users").ToList(); - Assert.Equal(users.Count, numberOfEntities - 10); + Assert.Equal(numberOfEntities - 10, users.Count); } } @@ -569,7 +567,7 @@ public void InsertWithCustomTableNameMapper() return tableattr.Name; var name = type.Name + "s"; - if (type.IsInterface && name.StartsWith("I")) + if (type.IsInterface && name.StartsWith('I')) return name.Substring(1); return name; } @@ -597,13 +595,13 @@ public void GetAll() connection.DeleteAll(); var total = connection.Insert(users); - Assert.Equal(total, numberOfEntities); + Assert.Equal(numberOfEntities, total); users = connection.GetAll().ToList(); - Assert.Equal(users.Count, numberOfEntities); + Assert.Equal(numberOfEntities, users.Count); var iusers = connection.GetAll().ToList(); - Assert.Equal(iusers.Count, numberOfEntities); + Assert.Equal(numberOfEntities, iusers.Count); for (var i = 0; i < numberOfEntities; i++) - Assert.Equal(iusers[i].Age, i); + Assert.Equal(i, iusers[i].Age); } } @@ -622,11 +620,11 @@ public void GetAndGetAllWithNullableValues() Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value); var value2 = connection.Get(id2); - Assert.True(value2.DateValue == null); + Assert.Null(value2.DateValue); var value3 = connection.GetAll().ToList(); Assert.Equal(new DateTime(2011, 07, 14), value3[0].DateValue.Value); - Assert.True(value3[1].DateValue == null); + Assert.Null(value3[1].DateValue); } } diff --git a/tests/Dapper.Tests.Contrib/TestSuites.cs b/tests/Dapper.Tests.Contrib/TestSuites.cs index 55d4ef5c..5a23d629 100644 --- a/tests/Dapper.Tests.Contrib/TestSuites.cs +++ b/tests/Dapper.Tests.Contrib/TestSuites.cs @@ -1,8 +1,8 @@ -using Microsoft.Data.Sqlite; +using Microsoft.Data.SqlClient; +using Microsoft.Data.Sqlite; using MySqlConnector; using System; using System.Data; -using System.Data.SqlClient; using System.IO; using Xunit; using Xunit.Sdk;