This repository has been archived by the owner on Jul 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#1] Add Dapper + implement operation context handler
Signed-off-by: Roman Mashenkin <xromash@vxdesign.store>
- Loading branch information
Showing
39 changed files
with
569 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.0</TargetFramework> | ||
<AssemblyName>VXDesign.Store.CarWashSystem.Server.DataStorage</AssemblyName> | ||
<RootNamespace>VXDesign.Store.CarWashSystem.Server.DataStorage</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Dapper" Version="2.0.30" /> | ||
<PackageReference Include="System.Data.SqlClient" Version="4.3.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using System; | ||
|
||
namespace VXDesign.Store.CarWashSystem.Server.DataStorage.Entities | ||
{ | ||
[Obsolete("It's just an example")] | ||
public class ExampleEntity | ||
{ | ||
public int X { get; set; } | ||
public int Y { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data; | ||
using System.Threading.Tasks; | ||
using Dapper; | ||
|
||
namespace VXDesign.Store.CarWashSystem.Server.DataStorage.Operation | ||
{ | ||
public interface IOperation : IAsyncDisposable | ||
{ | ||
#region ExecuteAsync | ||
|
||
Task<int> ExecuteAsync(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<int> ExecuteAsync(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<int> ExecuteAsync(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QueryAsync | ||
|
||
Task<IEnumerable<T>> QueryAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<IEnumerable<T>> QueryAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<IEnumerable<T>> QueryAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QueryFirstAsync | ||
|
||
Task<T> QueryFirstAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QueryFirstAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QueryFirstAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QueryFirstOrDefaultAsync | ||
|
||
Task<T> QueryFirstOrDefaultAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QueryFirstOrDefaultAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QueryFirstOrDefaultAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QuerySingleAsync | ||
|
||
Task<T> QuerySingleAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QuerySingleAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QuerySingleAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QuerySingleOrDefaultAsync | ||
|
||
Task<T> QuerySingleOrDefaultAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QuerySingleOrDefaultAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<T> QuerySingleOrDefaultAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
#region QueryMultipleAsync | ||
|
||
Task<SqlMapper.GridReader> QueryMultipleAsync(string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<SqlMapper.GridReader> QueryMultipleAsync(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
Task<SqlMapper.GridReader> QueryMultipleAsync(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null); | ||
|
||
#endregion | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data; | ||
using System.Data.SqlClient; | ||
using System.Threading.Tasks; | ||
using Dapper; | ||
|
||
namespace VXDesign.Store.CarWashSystem.Server.DataStorage.Operation | ||
{ | ||
public class Operation : IOperation | ||
{ | ||
private readonly SqlConnection connection; | ||
private SqlTransaction transaction; | ||
|
||
private Operation(string dataStoreConnectionString) | ||
{ | ||
connection = new SqlConnection(dataStoreConnectionString); | ||
connection.Open(); | ||
} | ||
|
||
private SqlTransaction BeginTransaction() | ||
{ | ||
if (transaction != null) | ||
{ | ||
throw new Exception("Transaction has already started"); | ||
} | ||
|
||
return transaction = connection.BeginTransaction(IsolationLevel.Snapshot); | ||
} | ||
|
||
private void EndTransaction() | ||
{ | ||
transaction?.Dispose(); | ||
transaction = null; | ||
} | ||
|
||
private async Task<T> Execute<T>(Func<string, DynamicParameters, SqlTransaction, int?, CommandType?, Task<T>> function, DynamicParameters parameters, string command, | ||
CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await function(command, parameters, transaction, commandTimeout, commandType); | ||
} | ||
|
||
#region ExecuteAsync | ||
|
||
public async Task<int> ExecuteAsync(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.ExecuteAsync, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<int> ExecuteAsync(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await ExecuteAsync(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<int> ExecuteAsync(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await ExecuteAsync(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QueryAsync | ||
|
||
public async Task<IEnumerable<T>> QueryAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QueryAsync<T>, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<IEnumerable<T>> QueryAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryAsync<T>(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<IEnumerable<T>> QueryAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryAsync<T>(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QueryFirstAsync | ||
|
||
public async Task<T> QueryFirstAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QueryFirstAsync<T>, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QueryFirstAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryFirstAsync<T>(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QueryFirstAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryFirstAsync<T>(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QueryFirstOrDefaultAsync | ||
|
||
public async Task<T> QueryFirstOrDefaultAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QueryFirstOrDefaultAsync<T>, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QueryFirstOrDefaultAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryFirstOrDefaultAsync<T>(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QueryFirstOrDefaultAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryFirstOrDefaultAsync<T>(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QuerySingleAsync | ||
|
||
public async Task<T> QuerySingleAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QuerySingleAsync<T>, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QuerySingleAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QuerySingleAsync<T>(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QuerySingleAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QuerySingleAsync<T>(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QuerySingleOrDefaultAsync | ||
|
||
public async Task<T> QuerySingleOrDefaultAsync<T>(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QuerySingleOrDefaultAsync<T>, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QuerySingleOrDefaultAsync<T>(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QuerySingleOrDefaultAsync<T>(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<T> QuerySingleOrDefaultAsync<T>(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QuerySingleOrDefaultAsync<T>(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
#region QueryMultipleAsync | ||
|
||
public async Task<SqlMapper.GridReader> QueryMultipleAsync(DynamicParameters parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await Execute(connection.QueryMultipleAsync, parameters, command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<SqlMapper.GridReader> QueryMultipleAsync(string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryMultipleAsync(new DynamicParameters(), command, commandType, commandTimeout); | ||
} | ||
|
||
public async Task<SqlMapper.GridReader> QueryMultipleAsync(object parameters, string command, CommandType? commandType = null, int? commandTimeout = null) | ||
{ | ||
return await QueryMultipleAsync(new DynamicParameters(parameters), command, commandType, commandTimeout); | ||
} | ||
|
||
#endregion | ||
|
||
public ValueTask DisposeAsync() | ||
{ | ||
connection?.DisposeAsync(); | ||
transaction?.DisposeAsync(); | ||
return new ValueTask(); | ||
} | ||
|
||
public static async Task MakeAction(string dataStoreConnectionString, Func<IOperation, Task> action) | ||
{ | ||
await MakeAction(dataStoreConnectionString, async operation => | ||
{ | ||
await action(operation); | ||
return true; | ||
}); | ||
} | ||
|
||
public static async Task<T> MakeAction<T>(string dataStoreConnectionString, Func<IOperation, Task<T>> action) | ||
{ | ||
await using var operation = new Operation(dataStoreConnectionString); | ||
try | ||
{ | ||
await using var transaction = operation.BeginTransaction(); | ||
try | ||
{ | ||
var result = await action(operation); | ||
transaction.Commit(); | ||
return result; | ||
} | ||
catch | ||
{ | ||
var retries = 5; | ||
while (retries > 0) | ||
{ | ||
try | ||
{ | ||
transaction.Rollback(); | ||
break; | ||
} | ||
catch | ||
{ | ||
retries--; | ||
await Task.Delay(TimeSpan.FromSeconds(1)); | ||
} | ||
} | ||
|
||
throw; | ||
} | ||
} | ||
finally | ||
{ | ||
operation.EndTransaction(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using VXDesign.Store.CarWashSystem.Server.DataStorage.Entities; | ||
using VXDesign.Store.CarWashSystem.Server.DataStorage.Operation; | ||
using VXDesign.Store.CarWashSystem.Server.DataStorage.Stores.Interfaces; | ||
|
||
namespace VXDesign.Store.CarWashSystem.Server.DataStorage.Stores.Implementations | ||
{ | ||
[Obsolete("It's just an example")] | ||
public class ExampleStore : IExampleStore | ||
{ | ||
public async Task<IEnumerable<ExampleEntity>> GetExample1(IOperation operation) | ||
{ | ||
return await operation.QueryAsync<ExampleEntity>(@"SELECT * FROM [example].[Example1]"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using VXDesign.Store.CarWashSystem.Server.DataStorage.Entities; | ||
using VXDesign.Store.CarWashSystem.Server.DataStorage.Operation; | ||
|
||
namespace VXDesign.Store.CarWashSystem.Server.DataStorage.Stores.Interfaces | ||
{ | ||
[Obsolete("It's just an example")] | ||
public interface IExampleStore | ||
{ | ||
Task<IEnumerable<ExampleEntity>> GetExample1(IOperation operation); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.