Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / Update / Internal / UpdateCommand.cs
1 //---------------------------------------------------------------------
2 // <copyright file="UpdateCommand.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System.Data.Metadata.Edm;
11 using System.Data.Common;
12 using System.Collections.Generic;
13 using System.Text;
14 using System.Diagnostics;
15 using System.Globalization;
16 using System.Data.Common.Utils;
17 using System.Data.Common.CommandTrees;
18 using System.Data.Objects;
19 using System.Linq;
20 using System.Data.EntityClient;
21 using System.Threading;
22 namespace System.Data.Mapping.Update.Internal
23 {
24     internal enum UpdateCommandKind
25     {
26         Dynamic,
27         Function,
28     }
29
30     /// <summary>
31     /// Class storing the result of compiling an instance DML command.
32     /// </summary>
33     internal abstract class UpdateCommand : IComparable<UpdateCommand>, IEquatable<UpdateCommand>
34     {
35         protected UpdateCommand(PropagatorResult originalValues, PropagatorResult currentValues)
36         {
37             m_originalValues = originalValues;
38             m_currentValues = currentValues;
39         }
40
41         private readonly PropagatorResult m_originalValues;
42         private readonly PropagatorResult m_currentValues;
43
44         // When it is not possible to order two commands based on their contents, we assign an 'ordering identifier'
45         // so that one will consistently precede the other.
46         private static int s_orderingIdentifierCounter;
47         private int m_orderingIdentifier;
48
49         /// <summary>
50         /// Gets all identifiers (key values basically) generated by this command. For instance,
51         /// @@IDENTITY values.
52         /// </summary>
53         internal abstract IEnumerable<int> OutputIdentifiers { get; }
54
55         /// <summary>
56         /// Gets all identifiers required by this command.
57         /// </summary>
58         internal abstract IEnumerable<int> InputIdentifiers { get; }
59
60         /// <summary>
61         /// Gets table (if any) associated with the current command. FunctionUpdateCommand has no table.
62         /// </summary>
63         internal virtual EntitySet Table
64         {
65             get
66             {
67                 return null;
68             }
69         }
70
71         /// <summary>
72         /// Gets type of command.
73         /// </summary>
74         internal abstract UpdateCommandKind Kind
75         {
76             get;
77         }
78
79         /// <summary>
80         /// Gets original values of row/entity handled by this command.
81         /// </summary>
82         internal PropagatorResult OriginalValues { get { return m_originalValues; } }
83
84         /// <summary>
85         /// Gets current values of row/entity handled by this command.
86         /// </summary>
87         internal PropagatorResult CurrentValues { get { return m_currentValues; } }
88
89         /// <summary>
90         /// Yields all state entries contributing to this command. Used for error reporting.
91         /// </summary>
92         /// <param name="translator">Translator context.</param>
93         /// <returns>Related state entries.</returns>
94         internal abstract IList<IEntityStateEntry> GetStateEntries(UpdateTranslator translator);
95
96         /// <summary>
97         /// Determines model level dependencies for the current command. Dependencies are based
98         /// on the model operations performed by the command (adding or deleting entities or relationships).
99         /// </summary>
100         internal void GetRequiredAndProducedEntities(UpdateTranslator translator,
101             KeyToListMap<EntityKey, UpdateCommand> addedEntities,
102             KeyToListMap<EntityKey, UpdateCommand> deletedEntities,
103             KeyToListMap<EntityKey, UpdateCommand> addedRelationships,
104             KeyToListMap<EntityKey, UpdateCommand> deletedRelationships)
105         {
106             IList<IEntityStateEntry> stateEntries = GetStateEntries(translator);
107
108             foreach (IEntityStateEntry stateEntry in stateEntries)
109             {
110                 if (!stateEntry.IsRelationship)
111                 {
112                     if (stateEntry.State == EntityState.Added)
113                     {
114                         addedEntities.Add(stateEntry.EntityKey, this);
115                     }
116                     else if (stateEntry.State == EntityState.Deleted)
117                     {
118                         deletedEntities.Add(stateEntry.EntityKey, this);
119                     }
120                 }
121             }
122
123             // process foreign keys
124             if (null != this.OriginalValues)
125             {
126                 // if a foreign key being deleted, it 'frees' or 'produces' the referenced key
127                 AddReferencedEntities(translator, this.OriginalValues, deletedRelationships);
128             }
129             if (null != this.CurrentValues)
130             {
131                 // if a foreign key is being added, if requires the referenced key
132                 AddReferencedEntities(translator, this.CurrentValues, addedRelationships);
133             }
134
135             // process relationships
136             foreach (IEntityStateEntry stateEntry in stateEntries)
137             {
138                 if (stateEntry.IsRelationship)
139                 {
140                     // only worry about the relationship if it is being added or deleted
141                     bool isAdded = stateEntry.State == EntityState.Added;
142                     if (isAdded || stateEntry.State == EntityState.Deleted)
143                     {
144                         DbDataRecord record = isAdded ? (DbDataRecord)stateEntry.CurrentValues : stateEntry.OriginalValues;
145                         Debug.Assert(2 == record.FieldCount, "non-binary relationship?");
146                         EntityKey end1 = (EntityKey)record[0];
147                         EntityKey end2 = (EntityKey)record[1];
148
149                         // relationships require the entity when they're added and free the entity when they're deleted...
150                         KeyToListMap<EntityKey, UpdateCommand> affected = isAdded ? addedRelationships : deletedRelationships;
151
152                         // both ends are being modified by the relationship
153                         affected.Add(end1, this);
154                         affected.Add(end2, this);
155                     }
156                 }
157             }
158         }
159
160         private void AddReferencedEntities(UpdateTranslator translator, PropagatorResult result, KeyToListMap<EntityKey, UpdateCommand> referencedEntities)
161         {
162             foreach (PropagatorResult property in result.GetMemberValues())
163             {
164                 if (property.IsSimple && property.Identifier != PropagatorResult.NullIdentifier &&
165                     (PropagatorFlags.ForeignKey == (property.PropagatorFlags & PropagatorFlags.ForeignKey)))
166                 {
167                     foreach (int principal in translator.KeyManager.GetDirectReferences(property.Identifier))
168                     {
169                         PropagatorResult owner;
170                         if (translator.KeyManager.TryGetIdentifierOwner(principal, out owner) &&
171                             null != owner.StateEntry)
172                         {
173                             Debug.Assert(!owner.StateEntry.IsRelationship, "owner must not be a relationship");
174                             referencedEntities.Add(owner.StateEntry.EntityKey, this);
175                         }
176                     }
177                 }
178             }
179         }
180
181         /// <summary>
182         /// Executes the current update command.
183         /// </summary>
184         /// <param name="translator">Translator context.</param>
185         /// <param name="connection">EntityConnection to use (and implicitly, the EntityTransaction to use).</param>
186         /// <param name="identifierValues">Aggregator for identifier values (read for InputIdentifiers; write for
187         /// OutputIdentifiers</param>
188         /// <param name="generatedValues">Aggregator for server generated values.</param>
189         /// <returns>Number of rows affected by the command.</returns>
190         internal abstract long Execute(UpdateTranslator translator, EntityConnection connection, Dictionary<int, object> identifierValues,
191             List<KeyValuePair<PropagatorResult, object>> generatedValues);
192
193         /// <summary>
194         /// Implementation of CompareTo for concrete subclass of UpdateCommand.
195         /// </summary>
196         internal abstract int CompareToType(UpdateCommand other);
197
198         /// <summary>
199         /// Provides a suggested ordering between two commands. Ensuring a consistent ordering is important to avoid deadlocks
200         /// between two clients because it means locks are acquired in the same order where possible. The ordering criteria are as 
201         /// follows (and are partly implemented in the CompareToType method). In some cases there are specific secondary
202         /// reasons for the order (e.g. operator kind), but for the most case we just care that a consistent ordering
203         /// is applied:
204         /// 
205         /// - The kind of command (dynamic or function). This is an arbitrary criteria.
206         /// - The kind of operator (insert, update, delete). See <see cref="ModificationOperator"/> for details of the ordering.
207         /// - The target of the modification (table for dynamic, set for function).
208         /// - Primary key for the modification (table key for dynamic, entity keys for function).
209         /// 
210         /// If it is not possible to differentiate between two commands (e.g., where the user is inserting entities with server-generated
211         /// primary keys and has not given explicit values), arbitrary ordering identifiers are assigned to the commands to
212         /// ensure CompareTo is well-behaved (doesn't return 0 for different commands and suggests consistent ordering).
213         /// </summary>
214         public int CompareTo(UpdateCommand other)
215         {
216             // If the commands are the same (by reference), return 0 immediately. Otherwise, we try to find (and eventually
217             // force) an ordering between them by returning a value that is non-zero.
218             if (this.Equals(other)) { return 0; }
219             Debug.Assert(null != other, "comparing to null UpdateCommand");
220             int result = (int)this.Kind - (int)other.Kind;
221             if (0 != result) { return result; }
222
223             // defer to specific type for other comparisons...
224             result = CompareToType(other);
225             if (0 != result) { return result; }
226
227             // if the commands are indistinguishable, assign arbitrary identifiers to them to ensure consistent ordering
228             unchecked
229             {
230                 if (this.m_orderingIdentifier == 0)
231                 {
232                     this.m_orderingIdentifier = Interlocked.Increment(ref s_orderingIdentifierCounter);
233                 }
234                 if (other.m_orderingIdentifier == 0)
235                 {
236                     other.m_orderingIdentifier = Interlocked.Increment(ref s_orderingIdentifierCounter);
237                 }
238
239                 return this.m_orderingIdentifier - other.m_orderingIdentifier;
240             }
241         }
242
243         #region IEquatable: note that we use reference equality
244         public bool Equals(UpdateCommand other)
245         {
246             return base.Equals(other);
247         }
248
249         public override bool Equals(object obj)
250         {
251             return base.Equals(obj);
252         }
253
254         public override int GetHashCode()
255         {
256             return base.GetHashCode();
257         }
258         #endregion
259     }
260 }