[sre] Wrap mono_image_create_token with HANDLE_FUNCTION_{ENTER,RETURN}
[mono-project.git] / mcs / class / referencesource / System.Data.Linq / ChangeDirector.cs
blob74f9bf1513310c6a13d9e12ccce9774da864250d
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Linq.Expressions;
5 using System.Text;
6 using System.Reflection;
7 using System.Linq;
8 using System.Security.Permissions;
9 using System.Security;
11 namespace System.Data.Linq {
12 using System.Data.Linq.Mapping;
13 using System.Data.Linq.Provider;
14 using System.Diagnostics.CodeAnalysis;
16 /// <summary>
17 /// Controls how inserts, updates and deletes are performed.
18 /// </summary>
19 internal abstract class ChangeDirector {
20 internal abstract int Insert(TrackedObject item);
21 internal abstract int DynamicInsert(TrackedObject item);
22 internal abstract void AppendInsertText(TrackedObject item, StringBuilder appendTo);
24 internal abstract int Update(TrackedObject item);
25 internal abstract int DynamicUpdate(TrackedObject item);
26 internal abstract void AppendUpdateText(TrackedObject item, StringBuilder appendTo);
28 internal abstract int Delete(TrackedObject item);
29 internal abstract int DynamicDelete(TrackedObject item);
30 internal abstract void AppendDeleteText(TrackedObject item, StringBuilder appendTo);
32 internal abstract void RollbackAutoSync();
33 internal abstract void ClearAutoSyncRollback();
35 internal static ChangeDirector CreateChangeDirector(DataContext context) {
36 return new StandardChangeDirector(context);
39 /// <summary>
40 /// Implementation of ChangeDirector which calls user code if possible
41 /// and othewise falls back to creating SQL for 'INSERT', 'UPDATE' and 'DELETE'.
42 /// </summary>
43 internal class StandardChangeDirector : ChangeDirector {
44 private enum UpdateType { Insert, Update, Delete };
45 private enum AutoSyncBehavior { ApplyNewAutoSync, RollbackSavedValues }
47 DataContext context;
48 [SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification="Microsoft: FxCop bug Dev10:423110 -- List<KeyValuePair<object, object>> is not supposed to be flagged as a violation.")]
49 List<KeyValuePair<TrackedObject, object[]>> syncRollbackItems;
51 internal StandardChangeDirector(DataContext context) {
52 this.context = context;
55 [SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification="Microsoft: FxCop bug Dev10:423110 -- List<KeyValuePair<object, object>> is not supposed to be flagged as a violation.")]
56 private List<KeyValuePair<TrackedObject, object[]>> SyncRollbackItems {
57 get {
58 if (syncRollbackItems == null) {
59 syncRollbackItems = new List<KeyValuePair<TrackedObject, object[]>>();
61 return syncRollbackItems;
65 internal override int Insert(TrackedObject item) {
66 if (item.Type.Table.InsertMethod != null) {
67 try {
68 item.Type.Table.InsertMethod.Invoke(this.context, new object[] { item.Current });
70 catch (TargetInvocationException tie) {
71 if (tie.InnerException != null) {
72 throw tie.InnerException;
74 throw;
76 return 1;
78 else {
79 return DynamicInsert(item);
83 internal override int DynamicInsert(TrackedObject item) {
84 Expression cmd = this.GetInsertCommand(item);
85 if (cmd.Type == typeof(int)) {
86 return (int)this.context.Provider.Execute(cmd).ReturnValue;
88 else {
89 IEnumerable<object> facts = (IEnumerable<object>)this.context.Provider.Execute(cmd).ReturnValue;
90 object[] syncResults = (object[])facts.FirstOrDefault();
91 if (syncResults != null) {
92 // sync any auto gen or computed members
93 AutoSyncMembers(syncResults, item, UpdateType.Insert, AutoSyncBehavior.ApplyNewAutoSync);
94 return 1;
96 else {
97 throw Error.InsertAutoSyncFailure();
102 internal override void AppendInsertText(TrackedObject item, StringBuilder appendTo) {
103 if (item.Type.Table.InsertMethod != null) {
104 appendTo.Append(Strings.InsertCallbackComment);
106 else {
107 Expression cmd = this.GetInsertCommand(item);
108 appendTo.Append(this.context.Provider.GetQueryText(cmd));
109 appendTo.AppendLine();
113 /// <summary>
114 /// Update the item, returning 0 if the update fails, 1 if it succeeds.
115 /// </summary>
116 internal override int Update(TrackedObject item) {
117 if (item.Type.Table.UpdateMethod != null) {
118 // create a copy - don't allow the override to modify our
119 // internal original values
120 try {
121 item.Type.Table.UpdateMethod.Invoke(this.context, new object[] { item.Current });
123 catch (TargetInvocationException tie) {
124 if (tie.InnerException != null) {
125 throw tie.InnerException;
127 throw;
129 return 1;
131 else {
132 return DynamicUpdate(item);
136 internal override int DynamicUpdate(TrackedObject item) {
137 Expression cmd = this.GetUpdateCommand(item);
138 if (cmd.Type == typeof(int)) {
139 return (int)this.context.Provider.Execute(cmd).ReturnValue;
141 else {
142 IEnumerable<object> facts = (IEnumerable<object>)this.context.Provider.Execute(cmd).ReturnValue;
143 object[] syncResults = (object[])facts.FirstOrDefault();
144 if (syncResults != null) {
145 // sync any auto gen or computed members
146 AutoSyncMembers(syncResults, item, UpdateType.Update, AutoSyncBehavior.ApplyNewAutoSync);
147 return 1;
149 else {
150 return 0;
155 internal override void AppendUpdateText(TrackedObject item, StringBuilder appendTo) {
156 if (item.Type.Table.UpdateMethod != null) {
157 appendTo.Append(Strings.UpdateCallbackComment);
159 else {
160 Expression cmd = this.GetUpdateCommand(item);
161 appendTo.Append(this.context.Provider.GetQueryText(cmd));
162 appendTo.AppendLine();
166 internal override int Delete(TrackedObject item) {
167 if (item.Type.Table.DeleteMethod != null) {
168 try {
169 item.Type.Table.DeleteMethod.Invoke(this.context, new object[] { item.Current });
171 catch (TargetInvocationException tie) {
172 if (tie.InnerException != null) {
173 throw tie.InnerException;
175 throw;
177 return 1;
179 else {
180 return DynamicDelete(item);
184 internal override int DynamicDelete(TrackedObject item) {
185 Expression cmd = this.GetDeleteCommand(item);
186 int ret = (int)this.context.Provider.Execute(cmd).ReturnValue;
187 if (ret == 0) {
188 // we don't yet know if the delete failed because the check constaint did not match
189 // or item was already deleted. Verify the item exists
190 cmd = this.GetDeleteVerificationCommand(item);
191 ret = ((int?)this.context.Provider.Execute(cmd).ReturnValue) ?? -1;
193 return ret;
196 internal override void AppendDeleteText(TrackedObject item, StringBuilder appendTo) {
197 if (item.Type.Table.DeleteMethod != null) {
198 appendTo.Append(Strings.DeleteCallbackComment);
200 else {
201 Expression cmd = this.GetDeleteCommand(item);
202 appendTo.Append(this.context.Provider.GetQueryText(cmd));
203 appendTo.AppendLine();
207 [SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification="Microsoft: FxCop bug Dev10:423110 -- List<KeyValuePair<object, object>> is not supposed to be flagged as a violation.")]
208 internal override void RollbackAutoSync() {
209 // Rolls back any AutoSync values that may have been set already
210 // Those values are no longer valid since the transaction will be rolled back on the server
211 if (this.syncRollbackItems != null) {
212 foreach (KeyValuePair<TrackedObject, object[]> rollbackItemPair in this.SyncRollbackItems) {
213 TrackedObject rollbackItem = rollbackItemPair.Key;
214 object[] rollbackValues = rollbackItemPair.Value;
216 AutoSyncMembers(
217 rollbackValues,
218 rollbackItem,
219 rollbackItem.IsNew ? UpdateType.Insert : UpdateType.Update,
220 AutoSyncBehavior.RollbackSavedValues);
225 [SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification="Microsoft: FxCop bug Dev10:423110 -- List<KeyValuePair<object, object>> is not supposed to be flagged as a violation.")]
226 internal override void ClearAutoSyncRollback() {
227 this.syncRollbackItems = null;
230 private Expression GetInsertCommand(TrackedObject item) {
231 MetaType mt = item.Type;
233 // bind to InsertFacts if there are any members to syncronize
234 List<MetaDataMember> membersToSync = GetAutoSyncMembers(mt, UpdateType.Insert);
235 ParameterExpression p = Expression.Parameter(item.Type.Table.RowType.Type, "p");
236 if (membersToSync.Count > 0) {
237 Expression autoSync = this.CreateAutoSync(membersToSync, p);
238 LambdaExpression resultSelector = Expression.Lambda(autoSync, p);
239 return Expression.Call(typeof(DataManipulation), "Insert", new Type[] { item.Type.InheritanceRoot.Type, resultSelector.Body.Type }, Expression.Constant(item.Current), resultSelector);
241 else {
242 return Expression.Call(typeof(DataManipulation), "Insert", new Type[] { item.Type.InheritanceRoot.Type }, Expression.Constant(item.Current));
246 /// <summary>
247 /// For the meta members specified, create an array initializer for each and bind to
248 /// an output array.
249 /// </summary>
250 private Expression CreateAutoSync(List<MetaDataMember> membersToSync, Expression source) {
251 System.Diagnostics.Debug.Assert(membersToSync.Count > 0);
252 int i = 0;
253 Expression[] initializers = new Expression[membersToSync.Count];
254 foreach (MetaDataMember mm in membersToSync) {
255 initializers[i++] = Expression.Convert(this.GetMemberExpression(source, mm.Member), typeof(object));
257 return Expression.NewArrayInit(typeof(object), initializers);
260 private static List<MetaDataMember> GetAutoSyncMembers(MetaType metaType, UpdateType updateType) {
261 List<MetaDataMember> membersToSync = new List<MetaDataMember>();
262 foreach (MetaDataMember metaMember in metaType.PersistentDataMembers.OrderBy(m => m.Ordinal)) {
263 // add all auto generated members for the specified update type to the auto-sync list
264 if ((updateType == UpdateType.Insert && metaMember.AutoSync == AutoSync.OnInsert) ||
265 (updateType == UpdateType.Update && metaMember.AutoSync == AutoSync.OnUpdate) ||
266 metaMember.AutoSync == AutoSync.Always) {
267 membersToSync.Add(metaMember);
270 return membersToSync;
273 /// <summary>
274 /// Synchronize the specified item by copying in data from the specified results.
275 /// Used to sync members after successful insert or update, but also used to rollback to previous values if a failure
276 /// occurs on other entities in the same SubmitChanges batch.
277 /// </summary>
278 /// <param name="autoSyncBehavior">
279 /// If AutoSyncBehavior.ApplyNewAutoSync, the current value of the property is saved before the sync occurs. This is used for normal synchronization after a successful update/insert.
280 /// Otherwise, the current value is not saved. This is used for rollback operations when something in the SubmitChanges batch failed, rendering the previously-sync'd values invalid.
281 /// </param>
282 [SuppressMessage("Microsoft.MSInternal", "CA908:AvoidTypesThatRequireJitCompilationInPrecompiledAssemblies", Justification="Microsoft: FxCop bug Dev10:423110 -- List<KeyValuePair<object, object>> is not supposed to be flagged as a violation.")]
283 private void AutoSyncMembers(object[] syncResults, TrackedObject item, UpdateType updateType, AutoSyncBehavior autoSyncBehavior) {
284 System.Diagnostics.Debug.Assert(item != null);
285 System.Diagnostics.Debug.Assert(item.IsNew || item.IsPossiblyModified, "AutoSyncMembers should only be called for new and modified objects.");
286 object[] syncRollbackValues = null;
287 if (syncResults != null) {
288 int idx = 0;
289 List<MetaDataMember> membersToSync = GetAutoSyncMembers(item.Type, updateType);
290 System.Diagnostics.Debug.Assert(syncResults.Length == membersToSync.Count);
291 if (autoSyncBehavior == AutoSyncBehavior.ApplyNewAutoSync) {
292 syncRollbackValues = new object[syncResults.Length];
294 foreach (MetaDataMember mm in membersToSync) {
295 object value = syncResults[idx];
296 object current = item.Current;
297 MetaAccessor accessor =
298 (mm.Member is PropertyInfo && ((PropertyInfo)mm.Member).CanWrite)
299 ? mm.MemberAccessor
300 : mm.StorageAccessor;
302 if (syncRollbackValues != null) {
303 syncRollbackValues[idx] = accessor.GetBoxedValue(current);
305 accessor.SetBoxedValue(ref current, DBConvert.ChangeType(value, mm.Type));
306 idx++;
309 if (syncRollbackValues != null) {
310 this.SyncRollbackItems.Add(new KeyValuePair<TrackedObject, object[]>(item, syncRollbackValues));
314 private Expression GetUpdateCommand(TrackedObject tracked) {
315 object database = tracked.Original;
316 MetaType rowType = tracked.Type.GetInheritanceType(database.GetType());
317 MetaType rowTypeRoot = rowType.InheritanceRoot;
319 ParameterExpression p = Expression.Parameter(rowTypeRoot.Type, "p");
320 Expression pv = p;
321 if (rowType != rowTypeRoot) {
322 pv = Expression.Convert(p, rowType.Type);
325 Expression check = this.GetUpdateCheck(pv, tracked);
326 if (check != null) {
327 check = Expression.Lambda(check, p);
330 // bind to out array if there are any members to synchronize
331 List<MetaDataMember> membersToSync = GetAutoSyncMembers(rowType, UpdateType.Update);
332 if (membersToSync.Count > 0) {
333 Expression autoSync = this.CreateAutoSync(membersToSync, pv);
334 LambdaExpression resultSelector = Expression.Lambda(autoSync, p);
335 if (check != null) {
336 return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type, resultSelector.Body.Type }, Expression.Constant(tracked.Current), check, resultSelector);
338 else {
339 return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type, resultSelector.Body.Type }, Expression.Constant(tracked.Current), resultSelector);
342 else if (check != null) {
343 return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type }, Expression.Constant(tracked.Current), check);
345 else {
346 return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type }, Expression.Constant(tracked.Current));
350 private Expression GetUpdateCheck(Expression serverItem, TrackedObject tracked) {
351 MetaType mt = tracked.Type;
352 if (mt.VersionMember != null) {
353 return Expression.Equal(
354 this.GetMemberExpression(serverItem, mt.VersionMember.Member),
355 this.GetMemberExpression(Expression.Constant(tracked.Current), mt.VersionMember.Member)
358 else {
359 Expression expr = null;
360 foreach (MetaDataMember mm in mt.PersistentDataMembers) {
361 if (!mm.IsPrimaryKey) {
362 UpdateCheck check = mm.UpdateCheck;
363 if (check == UpdateCheck.Always ||
364 (check == UpdateCheck.WhenChanged && tracked.HasChangedValue(mm))) {
365 object memberValue = mm.MemberAccessor.GetBoxedValue(tracked.Original);
366 Expression eq =
367 Expression.Equal(
368 this.GetMemberExpression(serverItem, mm.Member),
369 Expression.Constant(memberValue, mm.Type)
371 expr = (expr != null) ? Expression.And(expr, eq) : eq;
375 return expr;
379 private Expression GetDeleteCommand(TrackedObject tracked) {
380 MetaType rowType = tracked.Type;
381 MetaType rowTypeRoot = rowType.InheritanceRoot;
382 ParameterExpression p = Expression.Parameter(rowTypeRoot.Type, "p");
383 Expression pv = p;
384 if (rowType != rowTypeRoot) {
385 pv = Expression.Convert(p, rowType.Type);
387 object original = tracked.CreateDataCopy(tracked.Original);
388 Expression check = this.GetUpdateCheck(pv, tracked);
389 if (check != null) {
390 check = Expression.Lambda(check, p);
391 return Expression.Call(typeof(DataManipulation), "Delete", new Type[] { rowTypeRoot.Type }, Expression.Constant(original), check);
393 else {
394 return Expression.Call(typeof(DataManipulation), "Delete", new Type[] { rowTypeRoot.Type }, Expression.Constant(original));
398 private Expression GetDeleteVerificationCommand(TrackedObject tracked) {
399 ITable table = this.context.GetTable(tracked.Type.InheritanceRoot.Type);
400 System.Diagnostics.Debug.Assert(table != null);
401 ParameterExpression p = Expression.Parameter(table.ElementType, "p");
402 Expression pred = Expression.Lambda(Expression.Equal(p, Expression.Constant(tracked.Current)), p);
403 Expression where = Expression.Call(typeof(Queryable), "Where", new Type[] { table.ElementType }, table.Expression, pred);
404 Expression selector = Expression.Lambda(Expression.Constant(0, typeof(int?)), p);
405 Expression select = Expression.Call(typeof(Queryable), "Select", new Type[] { table.ElementType, typeof(int?) }, where, selector);
406 Expression singleOrDefault = Expression.Call(typeof(Queryable), "SingleOrDefault", new Type[] { typeof(int?) }, select);
407 return singleOrDefault;
410 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
411 private Expression GetMemberExpression(Expression exp, MemberInfo mi) {
412 FieldInfo fi = mi as FieldInfo;
413 if (fi != null)
414 return Expression.Field(exp, fi);
415 PropertyInfo pi = (PropertyInfo)mi;
416 return Expression.Property(exp, pi);