Skip to content
This repository has been archived by the owner on Jul 7, 2022. It is now read-only.

Commit

Permalink
[#1] Add Dapper + implement operation context handler
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Mashenkin <xromash@vxdesign.store>
  • Loading branch information
GUSAR1T0 committed Nov 2, 2019
1 parent a0196d5 commit 8e037a6
Show file tree
Hide file tree
Showing 39 changed files with 569 additions and 10 deletions.
14 changes: 14 additions & 0 deletions Server/DataStorage/DataStorage.csproj
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>
11 changes: 11 additions & 0 deletions Server/DataStorage/Entities/ExampleEntity.cs
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; }
}
}
68 changes: 68 additions & 0 deletions Server/DataStorage/Operation/IOperation.cs
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

}
}
230 changes: 230 additions & 0 deletions Server/DataStorage/Operation/Operation.cs
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();
}
}
}
}
18 changes: 18 additions & 0 deletions Server/DataStorage/Stores/Implementations/ExampleStore.cs
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]");
}
}
}
14 changes: 14 additions & 0 deletions Server/DataStorage/Stores/Interfaces/IExampleStore.cs
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);
}
}
14 changes: 13 additions & 1 deletion Server/Server.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server.csproj", "{73CA6A97-54C3-46AE-9E13-4803D4D1964F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAPI", "WebAPI\WebAPI.csproj", "{73CA6A97-54C3-46AE-9E13-4803D4D1964F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStorage", "DataStorage\DataStorage.csproj", "{3CDDE94C-9B3B-4ECB-ADFE-4941433DAB4D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{CB3D6514-9FBF-4317-8C67-DCBAF79A2834}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -12,5 +16,13 @@ Global
{73CA6A97-54C3-46AE-9E13-4803D4D1964F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73CA6A97-54C3-46AE-9E13-4803D4D1964F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73CA6A97-54C3-46AE-9E13-4803D4D1964F}.Release|Any CPU.Build.0 = Release|Any CPU
{3CDDE94C-9B3B-4ECB-ADFE-4941433DAB4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CDDE94C-9B3B-4ECB-ADFE-4941433DAB4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CDDE94C-9B3B-4ECB-ADFE-4941433DAB4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CDDE94C-9B3B-4ECB-ADFE-4941433DAB4D}.Release|Any CPU.Build.0 = Release|Any CPU
{CB3D6514-9FBF-4317-8C67-DCBAF79A2834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB3D6514-9FBF-4317-8C67-DCBAF79A2834}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB3D6514-9FBF-4317-8C67-DCBAF79A2834}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB3D6514-9FBF-4317-8C67-DCBAF79A2834}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Loading

0 comments on commit 8e037a6

Please sign in to comment.