Revert 132601, 132602 as it made the csharp console stop working
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Implementation / EntityTracker.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections.Generic;\r
29 using System.Linq;\r
30 \r
31 #if MONO_STRICT\r
32 using System.Data.Linq.Identity;\r
33 #else\r
34 using DbLinq.Data.Linq.Identity;\r
35 #endif\r
36 \r
37 #if MONO_STRICT\r
38 namespace System.Data.Linq.Implementation\r
39 #else\r
40 namespace DbLinq.Data.Linq.Implementation\r
41 #endif\r
42 {\r
43     /// <summary>\r
44     /// List of entities, with their corresponding state (to insert, to watch, to delete)\r
45     /// </summary>\r
46     internal class EntityTracker\r
47     {\r
48         /// <summary>\r
49         /// Entities being watched\r
50         /// </summary>\r
51         private readonly List<EntityTrack> entities = new List<EntityTrack>();\r
52 \r
53         /// <summary>\r
54         /// Entities currently being watched and to be updated\r
55         /// </summary>\r
56         private readonly IDictionary<IdentityKey, EntityTrack> entitiesByKey = new Dictionary<IdentityKey, EntityTrack>();\r
57 \r
58         /// lock key, for thread safety\r
59         private readonly object lockObject = new object();\r
60 \r
61         /// <summary>\r
62         /// Finds an entity tracking info by object reference\r
63         /// </summary>\r
64         /// <param name="entity"></param>\r
65         /// <returns></returns>\r
66         public EntityTrack FindByReference(object entity)\r
67         {\r
68             lock (lockObject)\r
69                 return (from e in entities where e.Entity == entity select e).FirstOrDefault();\r
70         }\r
71 \r
72         /// <summary>\r
73         /// Finds entity by key (PK)\r
74         /// </summary>\r
75         /// <param name="identityKey"></param>\r
76         /// <returns></returns>\r
77         public EntityTrack FindByIdentity(IdentityKey identityKey)\r
78         {\r
79             EntityTrack entityTrack;\r
80             lock (lockObject)\r
81                 entitiesByKey.TryGetValue(identityKey, out entityTrack);\r
82             return entityTrack;\r
83         }\r
84 \r
85         /// <summary>\r
86         /// Returns true if the list contains the entity\r
87         /// </summary>\r
88         /// <param name="entity"></param>\r
89         /// <returns></returns>\r
90         public bool ContainsReference(object entity)\r
91         {\r
92             return FindByReference(entity) == null;\r
93         }\r
94 \r
95         /// <summary>\r
96         /// Registers an entity to be inserted\r
97         /// </summary>\r
98         /// <param name="entity"></param>\r
99         public void RegisterToInsert(object entity)\r
100         {\r
101             if (entity == null)\r
102                 throw new ArgumentNullException("entity");\r
103 \r
104             lock (lockObject)\r
105             {\r
106                 var entityTrack = FindByReference(entity);\r
107                 if (entityTrack == null)\r
108                 {\r
109                     entityTrack = new EntityTrack(entity, EntityState.ToInsert);\r
110                     entities.Add(entityTrack);\r
111                 }\r
112                 else\r
113                 {\r
114                     switch (entityTrack.EntityState)\r
115                     {\r
116                     // if already registered for insert/update, then this is an error\r
117                     case EntityState.ToInsert:\r
118                     case EntityState.ToWatch:\r
119                         throw new InvalidOperationException();\r
120                     // whenever the object is registered for deletion, the fact of\r
121                     // registering it for insertion sets it back to watch\r
122                     case EntityState.ToDelete:\r
123                         entityTrack.EntityState = EntityState.ToWatch;\r
124                         entitiesByKey[entityTrack.IdentityKey] = entityTrack;\r
125                         break;\r
126                     default:\r
127                         throw new ArgumentOutOfRangeException();\r
128                     }\r
129                 }\r
130             }\r
131         }\r
132 \r
133         /// <summary>\r
134         /// Registers an entity to be watched\r
135         /// </summary>\r
136         /// <param name="entity"></param>\r
137         /// <param name="identityKey"></param>\r
138         public void RegisterToWatch(object entity, IdentityKey identityKey)\r
139         {\r
140             lock (lockObject)\r
141             {\r
142                 var entityTrack = FindByReference(entity);\r
143                 Console.WriteLine("# RegisterToWatch({0}, {1})", entity, identityKey);\r
144                 Console.WriteLine("# entityTrack={0}", entityTrack);\r
145                 if (entityTrack == null)\r
146                 {\r
147                     entityTrack = new EntityTrack(entity, EntityState.ToWatch) { IdentityKey = identityKey };\r
148                     entities.Add(entityTrack);\r
149                     entitiesByKey[identityKey] = entityTrack;\r
150                 }\r
151                 else\r
152                 {\r
153                     Console.WriteLine("# have entityTrack; entityState={0}", entityTrack.EntityState);\r
154                     // changes the state of the current entity\r
155                     switch (entityTrack.EntityState)\r
156                     {\r
157                     case EntityState.ToInsert:\r
158                         entityTrack.EntityState = EntityState.ToWatch;\r
159                         entityTrack.IdentityKey = identityKey;\r
160                         entitiesByKey[identityKey] = entityTrack;\r
161                         break;\r
162                     // watched entities should not be registered again\r
163                     case EntityState.ToWatch:\r
164                     case EntityState.ToDelete:\r
165                         throw new InvalidOperationException();\r
166                     default:\r
167                         throw new ArgumentOutOfRangeException();\r
168                     }\r
169                 }\r
170             }\r
171         }\r
172 \r
173         /// <summary>\r
174         /// Registers entity to be deleted\r
175         /// </summary>\r
176         /// <param name="entity"></param>\r
177         public void RegisterToDelete(object entity)\r
178         {\r
179             if (entity == null)\r
180                 throw new ArgumentNullException("entity");\r
181 \r
182             lock (lockObject)\r
183             {\r
184                 var entityTrack = FindByReference(entity);\r
185                 if (entityTrack == null)\r
186                 {\r
187                     entityTrack = new EntityTrack(entity, EntityState.ToDelete);\r
188                     entities.Add(entityTrack);\r
189                 }\r
190                 else\r
191                 {\r
192                     // changes the state of the current entity\r
193                     switch (entityTrack.EntityState)\r
194                     {\r
195                     // if entity was to be inserted, we just remove it from the list\r
196                     // as if it never came here\r
197                     case EntityState.ToInsert:\r
198                         entities.Remove(entityTrack);\r
199                         break;\r
200                     // watched entities are registered to be removed\r
201                     case EntityState.ToWatch:\r
202                         entityTrack.EntityState = EntityState.ToDelete;\r
203                         entitiesByKey.Remove(entityTrack.IdentityKey);\r
204                         break;\r
205                     case EntityState.ToDelete:\r
206                         throw new InvalidOperationException();\r
207                     default:\r
208                         throw new ArgumentOutOfRangeException();\r
209                     }\r
210                 }\r
211             }\r
212         }\r
213 \r
214         /// <summary>\r
215         /// Unregisters the entity after deletion\r
216         /// </summary>\r
217         /// <param name="entity"></param>\r
218         public void RegisterDeleted(object entity)\r
219         {\r
220             lock (lockObject)\r
221             {\r
222                 // TODO: we could require an index\r
223                 var entityTrack = FindByReference(entity);\r
224                 if (entityTrack == null)\r
225                 {\r
226                     throw new ArgumentException("entity");\r
227                 }\r
228                 // changes the state of the current entity\r
229                 switch (entityTrack.EntityState)\r
230                 {\r
231                 case EntityState.ToDelete:\r
232                     entities.Remove(entityTrack);\r
233                     break;\r
234                 case EntityState.ToInsert:\r
235                 case EntityState.ToWatch:\r
236                     throw new InvalidOperationException();\r
237                 default:\r
238                     throw new ArgumentOutOfRangeException();\r
239                 }\r
240             }\r
241         }\r
242 \r
243         /// <summary>\r
244         /// Enumerates all registered entities\r
245         /// </summary>\r
246         /// <returns></returns>\r
247         public IEnumerable<EntityTrack> EnumerateAll()\r
248         {\r
249             return entities;\r
250         }\r
251     }\r
252 }\r