-
Notifications
You must be signed in to change notification settings - Fork 303
/
BulkUpdater.cs
98 lines (81 loc) · 4.16 KB
/
BulkUpdater.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Simple.Data.Ado
{
using System.Data;
using Schema;
class BulkUpdater : IBulkUpdater
{
public int Update(AdoAdapter adapter, string tableName, IList<IDictionary<string, object>> data, IDbTransaction transaction)
{
return Update(adapter, tableName, data, adapter.GetKeyFieldNames(tableName).ToList(), transaction);
}
public int Update(AdoAdapter adapter, string tableName, IList<IDictionary<string, object>> data, IEnumerable<string> criteriaFieldNames, IDbTransaction transaction)
{
int count = 0;
if (data == null) throw new ArgumentNullException("data");
if (data.Count < 2) throw new ArgumentException("UpdateMany requires more than one record.");
var criteriaFieldNameList = criteriaFieldNames.ToList();
if (criteriaFieldNameList.Count == 0) throw new NotSupportedException("Adapter does not support key-based update for this object.");
if (!AllRowsHaveSameKeys(data)) throw new SimpleDataException("Records have different structures. Bulk updates are only valid on consistent records.");
var table = adapter.GetSchema().FindTable(tableName);
var exampleRow = new Dictionary<string, object>(data.First(), HomogenizedEqualityComparer.DefaultInstance);
var commandBuilder = new UpdateHelper(adapter.GetSchema()).GetUpdateCommand(tableName, exampleRow,
ExpressionHelper.CriteriaDictionaryToExpression(
tableName, GetCriteria(criteriaFieldNameList, exampleRow)));
using (var connectionScope = ConnectionScope.Create(transaction, adapter.CreateConnection))
using (var command = commandBuilder.GetRepeatableCommand(connectionScope.Connection))
{
var propertyToParameterMap = CreatePropertyToParameterMap(data, table, command);
foreach (var row in data)
{
foreach (var kvp in row)
{
propertyToParameterMap[kvp.Key].Value = kvp.Value ?? DBNull.Value;
}
count += command.ExecuteNonQuery();
}
}
return count;
}
private static Dictionary<string, IDbDataParameter> CreatePropertyToParameterMap(IEnumerable<IDictionary<string, object>> data, Table table, IDbCommand command)
{
return data.First().Select(kvp => new
{
kvp.Key,
Value = GetDbDataParameter(table, command, kvp)
})
.Where(t => t.Value != null)
.ToDictionary(t => t.Key, t => t.Value);
}
private static IDbDataParameter GetDbDataParameter(Table table, IDbCommand command, KeyValuePair<string, object> kvp)
{
return command.Parameters.Cast<IDbDataParameter>().
FirstOrDefault
(p =>
p.SourceColumn ==
table.FindColumn(kvp.Key).ActualName);
}
private static bool AllRowsHaveSameKeys(IList<IDictionary<string, object>> data)
{
var exemplar = new HashSet<string>(data.First().Keys);
return data.Skip(1).All(d => exemplar.SetEquals(d.Keys));
}
private static Dictionary<string, object> GetCriteria(IEnumerable<string> keyFieldNames, IDictionary<string, object> record)
{
var criteria = new Dictionary<string, object>();
foreach (var keyFieldName in keyFieldNames)
{
if (!record.ContainsKey(keyFieldName))
{
throw new InvalidOperationException("Key field value not set.");
}
criteria.Add(keyFieldName, record[keyFieldName]);
record.Remove(keyFieldName);
}
return criteria;
}
}
}