-
Notifications
You must be signed in to change notification settings - Fork 0
/
DapperProviderBase.cs
184 lines (153 loc) · 6.49 KB
/
DapperProviderBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq.Mapping;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace DapperUtils
{
public class DapperProviderBase
{
private readonly string _connectionString = String.Empty;
/// <summary>
/// Creates a new instance of the base provider
/// </summary>
/// <param name="connectionString">Connectoin string to use</param>
public DapperProviderBase(string connectionString)
{
_connectionString = connectionString;
}
/// <summary>
/// Connects async to the database
/// </summary>
/// <typeparam name="R">The return type of the method to execute</typeparam>
/// <param name="f">Method to execute</param>
/// <returns></returns>
public async Task<R> ConnectAsync<R>(Func<IDbConnection, Task<R>> f)
{
ConfigureDapperColumnMapping<R>();
using (IDbConnection connection = await GetOpenConnectionAsync())
{
return await f(connection);
}
}
/// <summary>
/// Convert IEnumerable<T> to DataTable
/// (useful when calling storeds that have table-valued parameters)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <returns></returns>
public DataTable CreateTable<T>(IEnumerable<T> collection)
{
// Fetch the type of List contained in the ParamValue
var tableType = typeof(T);
// Create DataTable which will contain data from List<T>
var dataTable = new DataTable();
// Fetch the Type fields count
int columnCount = tableType.GetProperties().Count();
var columnNameMappingDictionary = new Dictionary<string, string>();
// Create DataTable Columns using table type field name and their types
// Traversing through Column Collection
for (int counter = 0; counter < columnCount; counter++)
{
var propertyInfo = tableType.GetProperties()[counter];
string columnName = GetColumnAttributeValue(propertyInfo) ?? propertyInfo.Name;
Type columnType = Nullable.GetUnderlyingType(tableType.GetProperties()[counter].PropertyType) ??
tableType.GetProperties()[counter].PropertyType;
columnNameMappingDictionary.Add(propertyInfo.Name, columnName);
dataTable.Columns.Add(columnName, columnType);
}
// Return parameter with null value
if (collection == null)
return dataTable;
// Traverse through number of entries / rows in the List
foreach (var item in collection)
{
// Create a new DataRow
DataRow dataRow = dataTable.NewRow();
// Traverse through type fields or column names
for (int counter = 0; counter < columnCount; counter++)
{
// Fetch Column Name
string columnName = columnNameMappingDictionary[tableType.GetProperties()[counter].Name];
//Fetch Value for each column for each element in the List<T>
dataRow[columnName] = item
.GetType().GetProperties()[counter]
.GetValue(item) ?? DBNull.Value;
}
// Add Row to Table
dataTable.Rows.Add(dataRow);
}
return (dataTable);
}
/// <summary>
/// Executes work transactionally
/// </summary>
/// <param name="connection">The connection to use</param>
/// <param name="work">The work to be executed</param>
/// <param name="isolationLevel">The IsolationLevel to use. Default set to ReadCommitted.</param>
/// <returns></returns>
public async Task<T> Transactionally<T>(IDbConnection connection, Func<IDbTransaction, Task<T>> work,
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
{
using (IDbTransaction transaction = connection.BeginTransaction(isolationLevel))
{
try
{
return await work(transaction);
}
catch
{
transaction.Rollback();
throw;
}
}
}
#region Private Methods
private async Task<IDbConnection> GetOpenConnectionAsync()
{
var dbConnection = new SqlConnection(_connectionString);
await dbConnection.OpenAsync();
return dbConnection;
}
private string GetColumnAttributeValue(PropertyInfo propertyInfo)
{
var columnAttribute = propertyInfo.GetCustomAttributes(false).OfType<ColumnAttribute>().FirstOrDefault();
return columnAttribute?.Name;
}
private void ConfigureDapperColumnMapping<T>()
{
ConfigureDapperColumnMapping(typeof(T));
}
private void ConfigureDapperColumnMapping(Type propertyType)
{
Type[] t = { propertyType };
if (propertyType.IsGenericType)
{
t = propertyType.GetGenericArguments();
}
Func<Type, string, PropertyInfo> mapping = (type, columnName) =>
type.GetProperties().FirstOrDefault(prop => GetColumnAttributeValue(prop) == columnName);
for (int i = 0; i < t.Length; i++)
{
SqlMapper.SetTypeMap(t[i], new CustomPropertyTypeMap(t[i], mapping));
//map properties that are not value types
foreach (var property in t[i].GetProperties(BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public))
{
if (property.PropertyType.IsValueType == false)
{
//WARNING: Infinite recursion is possible if you have circular references
ConfigureDapperColumnMapping(property.PropertyType);
}
}
}
}
#endregion
}
}