diff --git a/Dapper.Rainbow/Database.Async.cs b/Dapper.Rainbow/Database.Async.cs index cdbf7e41f..e1a78c47b 100644 --- a/Dapper.Rainbow/Database.Async.cs +++ b/Dapper.Rainbow/Database.Async.cs @@ -88,7 +88,7 @@ public Task> AllAsync() => /// The parameters to use. /// The number of rows affected. public Task ExecuteAsync(string sql, dynamic param = null) => - _connection.ExecuteAsync(sql, param as object, _transaction, _commandTimeout); + _connection.ExecuteAsync(sql, param as object, Transaction, _commandTimeout); /// /// Asynchronously queries the current database. @@ -98,7 +98,7 @@ public Task ExecuteAsync(string sql, dynamic param = null) => /// The parameters to use. /// An enumerable of for the rows fetched. public Task> QueryAsync(string sql, dynamic param = null) => - _connection.QueryAsync(sql, param as object, _transaction, _commandTimeout); + _connection.QueryAsync(sql, param as object, Transaction, _commandTimeout); /// /// Asynchronously queries the current database for a single record. @@ -108,7 +108,7 @@ public Task> QueryAsync(string sql, dynamic param = null) => /// The parameters to use. /// An enumerable of for the rows fetched. public Task QueryFirstOrDefaultAsync(string sql, dynamic param = null) => - _connection.QueryFirstOrDefaultAsync(sql, param as object, _transaction, _commandTimeout); + _connection.QueryFirstOrDefaultAsync(sql, param as object, Transaction, _commandTimeout); /// /// Perform an asynchronous multi-mapping query with 2 input types. @@ -195,7 +195,7 @@ public Task> QueryAsyncThe parameters to use. /// Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object> public Task> QueryAsync(string sql, dynamic param = null) => - _connection.QueryAsync(sql, param as object, _transaction); + _connection.QueryAsync(sql, param as object, Transaction); /// /// Execute a command that returns multiple result sets, and access each in turn. diff --git a/Dapper.Rainbow/Database.cs b/Dapper.Rainbow/Database.cs index 670f22631..74ab9ba2f 100644 --- a/Dapper.Rainbow/Database.cs +++ b/Dapper.Rainbow/Database.cs @@ -175,7 +175,11 @@ public Table(Database database, string likelyTableName) private DbConnection _connection; private int _commandTimeout; - private DbTransaction _transaction; + + /// + /// Get access to the underlying transaction + /// + public DbTransaction Transaction { get; private set; } /// /// Get underlying database connection. @@ -215,9 +219,11 @@ internal virtual Action CreateTableConstructorForTable() /// Begins a transaction in this database. /// /// The isolation level to use. - public void BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted) + /// The transaction created + public DbTransaction BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommitted) { - _transaction = _connection.BeginTransaction(isolation); + Transaction = _connection.BeginTransaction(isolation); + return Transaction; } /// @@ -225,8 +231,8 @@ public void BeginTransaction(IsolationLevel isolation = IsolationLevel.ReadCommi /// public void CommitTransaction() { - _transaction.Commit(); - _transaction = null; + Transaction.Commit(); + Transaction = null; } /// @@ -234,8 +240,8 @@ public void CommitTransaction() /// public void RollbackTransaction() { - _transaction.Rollback(); - _transaction = null; + Transaction.Rollback(); + Transaction = null; } /// @@ -336,7 +342,7 @@ private bool TableExists(string name) if (!string.IsNullOrEmpty(schemaName)) builder.Append("TABLE_SCHEMA = @schemaName AND "); builder.Append("TABLE_NAME = @name"); - return _connection.Query(builder.ToString(), new { schemaName, name }, _transaction).Count() == 1; + return _connection.Query(builder.ToString(), new { schemaName, name }, Transaction).Count() == 1; } /// @@ -346,7 +352,7 @@ private bool TableExists(string name) /// The parameters to use. /// The number of rows affected. public int Execute(string sql, dynamic param = null) => - _connection.Execute(sql, param as object, _transaction, _commandTimeout); + _connection.Execute(sql, param as object, Transaction, _commandTimeout); /// /// Queries the current database. @@ -357,7 +363,7 @@ public int Execute(string sql, dynamic param = null) => /// Whether to buffer the results. /// An enumerable of for the rows fetched. public IEnumerable Query(string sql, dynamic param = null, bool buffered = true) => - _connection.Query(sql, param as object, _transaction, buffered, _commandTimeout); + _connection.Query(sql, param as object, Transaction, buffered, _commandTimeout); /// /// Queries the current database for a single record. @@ -367,7 +373,7 @@ public IEnumerable Query(string sql, dynamic param = null, bool buffered = /// The parameters to use. /// An enumerable of for the rows fetched. public T QueryFirstOrDefault(string sql, dynamic param = null) => - _connection.QueryFirstOrDefault(sql, param as object, _transaction, _commandTimeout); + _connection.QueryFirstOrDefault(sql, param as object, Transaction, _commandTimeout); /// /// Perform a multi-mapping query with 2 input types. @@ -455,7 +461,7 @@ public IEnumerable QueryWhether the results should be buffered in memory. /// Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object> public IEnumerable Query(string sql, dynamic param = null, bool buffered = true) => - _connection.Query(sql, param as object, _transaction, buffered); + _connection.Query(sql, param as object, Transaction, buffered); /// /// Execute a command that returns multiple result sets, and access each in turn. @@ -477,7 +483,7 @@ public virtual void Dispose() if (connection.State != ConnectionState.Closed) { _connection = null; - _transaction = null; + Transaction = null; connection?.Close(); } GC.SuppressFinalize(this); diff --git a/Dapper.Rainbow/readme.md b/Dapper.Rainbow/readme.md new file mode 100644 index 000000000..c1f34b67a --- /dev/null +++ b/Dapper.Rainbow/readme.md @@ -0,0 +1,118 @@ +# Using Dapper.Rainbow in C# for CRUD Operations + +This guide outlines how to use `Dapper.Rainbow` in C# for CRUD operations. + +## 1. Setting Up + +Add Dapper and Dapper.Rainbow to your project via NuGet: + +```powershell +Install-Package Dapper -Version x.x.x +Install-Package Dapper.Rainbow -Version x.x.x +``` + +*Replace `x.x.x` with the latest version numbers.* + +## 2. Database Setup and Requirements + +For `Dapper.Rainbow` to function correctly, ensure each table has a primary key column named `Id`. + +Example `Users` table schema: + +```sql +CREATE TABLE Users ( + Id INT IDENTITY(1,1) PRIMARY KEY, + Name VARCHAR(100), + Email VARCHAR(100) +); +``` + +## 3. Establishing Database Connection + +Open a connection to your database: + +```csharp +using System.Data.SqlClient; + +var connectionString = "your_connection_string_here"; +using var connection = new SqlConnection(connectionString); +connection.Open(); // Open the connection +``` + +## 4. Defining Your Database Context + +Define a class for your database context: + +```csharp +using Dapper; +using System.Data; + +public class MyDatabase : Database +{ + public Table Users { get; set; } +} + +public class User +{ + public int Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } +} +``` + +## 5. Performing CRUD Operations + +### Insert + +```csharp +var db = new MyDatabase { Connection = connection }; +var newUser = new User { Name = "John Doe", Email = "john.doe@example.com" }; +var insertedUser = db.Users.Insert(newUser); +``` + +### Select + +Fetch users by ID or all users: + +```csharp +var user = db.Users.Get(id); // Single user by ID +var users = connection.Query("SELECT * FROM Users"); // All users +``` + +### Update + +```csharp +var userToUpdate = db.Users.Get(id); +userToUpdate.Email = "new.email@example.com"; +db.Users.Update(userToUpdate); +``` + +### Delete + +```csharp +db.Users.Delete(id); +``` + +## 6. Working with Foreign Keys + +Example schema for a `Posts` table with a foreign key to `Users`: + +```sql +CREATE TABLE Posts ( + Id INT IDENTITY(1,1) PRIMARY KEY, + UserId INT, + Content VARCHAR(255), + FOREIGN KEY (UserId) REFERENCES Users(Id) +); +``` + +Inserting a parent (`User`) and a child (`Post`) row: + +```csharp +var newUser = new User { Name = "Jane Doe", Email = "jane.doe@example.com" }; +var userId = db.Users.Insert(newUser); + +var newPost = new Post { UserId = userId, Content = "Hello, World!" }; +db.Connection.Insert(newPost); // Using Dapper for the child table +``` + diff --git a/Dapper/CompiledRegex.cs b/Dapper/CompiledRegex.cs index 5deef70f1..faffb7c9a 100644 --- a/Dapper/CompiledRegex.cs +++ b/Dapper/CompiledRegex.cs @@ -9,7 +9,7 @@ internal static partial class CompiledRegex [StringSyntax("Regex")] #endif private const string - WhitespaceOrReservedPattern = @"[\s;/\-+*]|^vacuum$|^commit$|^rollback$", + WhitespaceOrReservedPattern = @"[\s;/\-+*]|^vacuum$|^commit$|^rollback$|^revert$", LegacyParameterPattern = @"(? - - + + - + - - - - + + + + - + - + - + - + - + \ No newline at end of file diff --git a/Readme.md b/Readme.md index 5991d7109..8c8be6bd7 100644 --- a/Readme.md +++ b/Readme.md @@ -28,7 +28,7 @@ Package Purposes: * Dapper.EntityFramework.StrongName * Extension handlers for EntityFramework * Dapper.Rainbow - * Micro-ORM implemented on Dapper, provides CRUD helpers + * Micro-ORM implemented on Dapper, provides CRUD helpers ([readme](Dapper.Rainbow/readme.md)) * Dapper.SqlBuilder * Component for building SQL queries dynamically and composably diff --git a/appveyor.yml b/appveyor.yml index e766f8a28..08c5eb159 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,8 +6,8 @@ skip_commits: files: - '**/*.md' -install: - - choco install dotnet-sdk --version 8.0.100 +# install: +# - choco install dotnet-sdk --version 8.0.100 environment: Appveyor: true diff --git a/tests/Dapper.Tests/MiscTests.cs b/tests/Dapper.Tests/MiscTests.cs index cc762bf89..9028919ea 100644 --- a/tests/Dapper.Tests/MiscTests.cs +++ b/tests/Dapper.Tests/MiscTests.cs @@ -1309,6 +1309,43 @@ public HazGetOnlyAndCtor(int idProperty, string nameProperty) IdProperty = idProperty; NameProperty = nameProperty; } + } + + [Fact] + public void Issue1164_OverflowExceptionForByte() + { + const string sql = "select cast(200 as smallint) as [value]"; // 200 more than sbyte.MaxValue but less than byte.MaxValue + Issue1164Object obj = connection.QuerySingle>(sql); + Assert.StrictEqual(200, obj.Value); + } + + [Fact] + public void Issue1164_OverflowExceptionForUInt16() + { + const string sql = "select cast(40000 as bigint) as [value]"; // 40000 more than short.MaxValue but less than ushort.MaxValue + Issue1164Object obj = connection.QuerySingle>(sql); + Assert.StrictEqual(40000, obj.Value); + } + + [Fact] + public void Issue1164_OverflowExceptionForUInt32() + { + const string sql = "select cast(4000000000 as bigint) as [value]"; // 4000000000 more than int.MaxValue but less than uint.MaxValue + Issue1164Object obj = connection.QuerySingle>(sql); + Assert.StrictEqual(4000000000, obj.Value); + } + + [Fact] + public void Issue1164_OverflowExceptionForUInt64() + { + const string sql = "select cast(10000000000000000000.0 as float) as [value]"; // 10000000000000000000 more than long.MaxValue but less than ulong.MaxValue + Issue1164Object obj = connection.QuerySingle>(sql); + Assert.StrictEqual(10000000000000000000, obj.Value); + } + + private class Issue1164Object + { + public T Value; } internal record struct One(int OID); diff --git a/tests/Dapper.Tests/ProcedureTests.cs b/tests/Dapper.Tests/ProcedureTests.cs index b0371174b..4c15bd7bb 100644 --- a/tests/Dapper.Tests/ProcedureTests.cs +++ b/tests/Dapper.Tests/ProcedureTests.cs @@ -333,6 +333,7 @@ public async Task Issue1986_AutoProc_Whitespace(string space) [InlineData("VACUUM;", CommandType.Text)] [InlineData("cOmmiT", CommandType.Text)] [InlineData("rOllbAck", CommandType.Text)] + [InlineData("reVErt", CommandType.Text)] // comments imply text [InlineData("foo--bar", CommandType.Text)]