-
Notifications
You must be signed in to change notification settings - Fork 45
/
ActiveRecord.cs
353 lines (305 loc) · 13.1 KB
/
ActiveRecord.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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
* SubSonic - http://subsonicproject.com
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*/
using System;
using System.Data;
namespace SubSonic
{
/// <summary>
/// Summary for the ActiveRecord<T> class
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public abstract class ActiveRecord<T> : ReadOnlyRecord<T> where T : ReadOnlyRecord<T>, new()
{
/// <summary>
/// Initializes a new instance of the <see cref="ActiveRecord<T>"/> class.
/// </summary>
protected ActiveRecord()
{
MarkNew();
}
#region CommandMethods
/// <summary>
/// Gets the provider specific (SQL Server, Oracle, etc.) SQL parameter prefix.
/// </summary>
/// <value>The parameter prefix.</value>
protected static string ParameterPrefix
{
get { return BaseSchema.Provider.GetParameterPrefix(); }
}
/// <summary>
/// Returns a INSERT QueryCommand object used to generate SQL.
/// </summary>
/// <param name="userName">An optional username to be used if audit fields are present</param>
/// <returns></returns>
public QueryCommand GetInsertCommand(string userName)
{
return ActiveHelper<T>.GetInsertCommand(this, userName);
}
/// <summary>
/// Returns a UPDATE QueryCommand object used to generate SQL.
/// </summary>
/// <param name="userName">An optional username to be used if audit fields are present</param>
/// <returns></returns>
public QueryCommand GetUpdateCommand(string userName)
{
return ActiveHelper<T>.GetUpdateCommand(this, userName);
}
/// <summary>
/// Returns a DELETE QueryCommand object to delete the record with
/// the primary key value matching the passed value
/// </summary>
/// <param name="keyID">The primary key record value to match for the delete</param>
/// <returns></returns>
public static QueryCommand GetDeleteCommand(object keyID)
{
return ActiveHelper<T>.GetDeleteCommand(keyID);
}
/// <summary>
/// Returns a DELETE QueryCommand object to delete the records with
/// matching passed column/value criteria
/// </summary>
/// <param name="columnName">Name of the column to match</param>
/// <param name="oValue">Value of column to match</param>
/// <returns></returns>
public static QueryCommand GetDeleteCommand(string columnName, object oValue)
{
return ActiveHelper<T>.GetDeleteCommand(columnName, oValue);
}
#endregion
#region Persistence
/// <summary>
/// Executes before any operations occur within ActiveRecord Save()
/// </summary>
protected virtual void BeforeValidate() {}
/// <summary>
/// Executes on existing records after validation and before the update command has been generated.
/// </summary>
protected virtual void BeforeUpdate() {}
/// <summary>
/// Executes on existing records after validation and before the insert command has been generated.
/// </summary>
protected virtual void BeforeInsert() {}
/// <summary>
/// Executes after all steps have been performed for a successful ActiveRecord Save()
/// </summary>
protected virtual void AfterCommit() {}
[Obsolete("Deprecated: Use BeforeInsert() and/or BeforeUpdate() instead.")]
protected virtual void PreUpdate() {}
[Obsolete("Deprecated: Use AfterCommit() instead.")]
protected virtual void PostUpdate() {}
/// <summary>
/// Saves this object's state to the selected Database.
/// </summary>
public void Save()
{
Save(String.Empty);
}
/// <summary>
/// Saves this object's state to the selected Database.
/// </summary>
/// <param name="userID">The user ID.</param>
public void Save(int userID)
{
Save(userID.ToString());
}
/// <summary>
/// Saves this object's state to the selected Database.
/// </summary>
/// <param name="userID">The user ID.</param>
public void Save(Guid userID)
{
string sUserID = userID.ToString();
Save(sUserID);
}
/// <summary>
/// Validates this instance.
/// </summary>
/// <returns></returns>
public virtual bool Validate()
{
ValidateColumnSettings();
return Errors.Count == 0;
}
/// <summary>
/// Saves this object's state to the selected Database.
/// </summary>
/// <param name="userName">Name of the user.</param>
public void Save(string userName)
{
bool isValid = true;
if(ValidateWhenSaving)
{
BeforeValidate();
isValid = Validate();
}
if(isValid)
{
if(IsNew)
BeforeInsert();
else if(IsDirty)
BeforeUpdate();
QueryCommand cmd = GetSaveCommand(userName);
if(cmd == null)
return;
// reset the Primary Key with the id passed back by the operation
object pkVal = DataService.ExecuteScalar(cmd);
// clear out the DirtyColumns
DirtyColumns.Clear();
if(pkVal != null)
{
if(pkVal.GetType() == typeof(decimal))
pkVal = Convert.ToInt32(pkVal);
// set the primaryKey, only if an auto-increment
if(BaseSchema.PrimaryKey.AutoIncrement || BaseSchema.PrimaryKey.DataType == DbType.Guid)
{
try
{
pkVal = Convert.ChangeType(pkVal, BaseSchema.PrimaryKey.GetPropertyType());
SetPrimaryKey(pkVal);
}
catch
{
// this will happen if there is no PK defined on a table. Catch this and notify
throw new Exception("No Primary Key is defined for this table. A primary key is required to use SubSonic");
}
}
}
// set this object as old
MarkOld();
MarkClean();
AfterCommit();
}
else
{
// throw an Exception
string notification = String.Empty;
foreach(string message in Errors)
notification += message + Environment.NewLine;
throw new Exception("Can't save: " + notification);
}
}
/// <summary>
/// Returns a QueryCommand object used to persist the instance to the underlying database.
/// </summary>
/// <returns></returns>
public QueryCommand GetSaveCommand()
{
return GetSaveCommand(String.Empty);
}
/// <summary>
/// Returns a QueryCommand object used to persist the instance to the underlying database.
/// </summary>
/// <param name="userName">An optional username to be used if audit fields are present</param>
/// <returns></returns>
public QueryCommand GetSaveCommand(string userName)
{
if(IsNew)
return GetInsertCommand(userName);
if(IsDirty)
return GetUpdateCommand(userName);
return null;
}
/// <summary>
/// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy()
/// </summary>
/// <param name="keyID">The key ID.</param>
/// <returns>Number of rows affected by the operation</returns>
public static int Delete(object keyID)
{
return DeleteByParameter(BaseSchema.PrimaryKey.ColumnName, keyID, null);
}
/// <summary>
/// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy()
/// </summary>
/// <param name="columnName">The name of the column that whose value will be evaluated for deletion</param>
/// <param name="oValue">The value that will be compared against columnName to determine deletion</param>
/// <returns>Number of rows affected by the operation</returns>
public static int Delete(string columnName, object oValue)
{
return DeleteByParameter(columnName, oValue, null);
}
/// <summary>
/// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy()
/// </summary>
/// <param name="columnName">The name of the column that whose value will be evaluated for deletion</param>
/// <param name="oValue">The value that will be compared against columnName to determine deletion</param>
/// <param name="userName">The userName that the record will be updated with. Only relevant if the record contains Deleted or IsDeleted properties</param>
/// <returns>Number of rows affected by the operation</returns>
public static int Delete(string columnName, object oValue, string userName)
{
return DeleteByParameter(columnName, oValue, userName);
}
/// <summary>
/// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy()
/// </summary>
/// <param name="columnName">The name of the column that whose value will be evaluated for deletion</param>
/// <param name="oValue">The value that will be compared against columnName to determine deletion</param>
/// <param name="userName">The userName that the record will be updated with. Only relevant if the record contains Deleted or IsDeleted properties</param>
/// <returns>Number of rows affected by the operation</returns>
private static int DeleteByParameter(string columnName, object oValue, string userName)
{
return ActiveHelper<T>.Delete(columnName, oValue, userName);
}
/// <summary>
/// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns
/// </summary>
/// <param name="keyID">The key ID.</param>
/// <returns>Number of rows affected by the operation</returns>
public static int Destroy(object keyID)
{
return ActiveHelper<T>.DestroyByParameter(BaseSchema.PrimaryKey.ColumnName, keyID);
}
/// <summary>
/// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns
/// </summary>
/// <param name="columnName">The name of the column that whose value will be evaluated for deletion</param>
/// <param name="oValue">The value that will be compared against columnName to determine deletion</param>
/// <returns>Number of rows affected by the operation</returns>
public static int Destroy(string columnName, object oValue)
{
return ActiveHelper<T>.DestroyByParameter(columnName, oValue);
}
/// <summary>
/// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns
/// </summary>
/// <param name="columnName">The name of the column that whose value will be evaluated for deletion</param>
/// <param name="oValue">The value that will be compared against columnName to determine deletion</param>
/// <returns>Number of rows affected by the operation</returns>
private static int DestroyByParameter(string columnName, object oValue)
{
return ActiveHelper<T>.DestroyByParameter(columnName, oValue);
}
#endregion
/// <summary>
/// Gets the column value.
/// </summary>
/// <typeparam name="CT">The type of the T.</typeparam>
/// <param name="columnName">Name of the column.</param>
/// <returns></returns>
public new CT GetColumnValue<CT>(string columnName)
{
return base.GetColumnValue<CT>(columnName);
}
/// <summary>
/// Gets the column value.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <returns></returns>
public object GetColumnValue(string columnName)
{
return GetColumnValue<object>(columnName);
}
}
}