2009-06-12 Bill Holmes <billholmes54@gmail.com>
[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 : IEntityTracker\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         /// <summary>\r
59         /// Finds an entity tracking info by object reference\r
60         /// </summary>\r
61         /// <param name="entity"></param>\r
62         /// <returns></returns>\r
63         private EntityTrack FindByReference(object entity)\r
64         {\r
65             //    return (from e in entities where e.Entity == entity select e).FirstOrDefault();\r
66             return this.entities.Find(e => object.ReferenceEquals(entity, e.Entity));\r
67         }\r
68 \r
69         /// <summary>\r
70         /// Finds entity by key (PK)\r
71         /// </summary>\r
72         /// <param name="identityKey"></param>\r
73         /// <returns></returns>\r
74         public EntityTrack FindByIdentity(IdentityKey identityKey)\r
75         {\r
76             EntityTrack entityTrack;\r
77             entitiesByKey.TryGetValue(identityKey, out entityTrack);\r
78             return entityTrack;\r
79         }\r
80 \r
81         /// <summary>\r
82         /// Returns true if the list contains the entity\r
83         /// </summary>\r
84         /// <param name="entity"></param>\r
85         /// <returns></returns>\r
86         public bool ContainsReference(object entity)\r
87         {\r
88             return FindByReference(entity) != null;\r
89         }\r
90 \r
91         /// <summary>\r
92         /// Registers an entity to be inserted\r
93         /// </summary>\r
94         /// <param name="entity"></param>\r
95         public void RegisterToInsert(object entity)\r
96         {\r
97             if (entity == null)\r
98                 throw new ArgumentNullException("entity");\r
99 \r
100             var entityTrack = FindByReference(entity);\r
101             if (entityTrack == null)\r
102             {\r
103                 entityTrack = new EntityTrack(entity, EntityState.ToInsert);\r
104                 entities.Add(entityTrack);\r
105             }\r
106             else\r
107             {\r
108                 switch (entityTrack.EntityState)\r
109                 {\r
110                 // if already registered for insert/update, then this is an error\r
111                 case EntityState.ToInsert:\r
112                 case EntityState.ToWatch:\r
113                     throw new InvalidOperationException();\r
114                 // whenever the object is registered for deletion, the fact of\r
115                 // registering it for insertion sets it back to watch\r
116                 case EntityState.ToDelete:\r
117                     entityTrack.EntityState = EntityState.ToWatch;\r
118                     entitiesByKey[entityTrack.IdentityKey] = entityTrack;\r
119                     break;\r
120                 default:\r
121                     throw new ArgumentOutOfRangeException();\r
122                 }\r
123             }\r
124         }\r
125 \r
126         /// <summary>\r
127         /// Registers an entity to be watched\r
128         /// </summary>\r
129         /// <param name="entity"></param>\r
130         /// <param name="identityKey"></param>\r
131         public void RegisterToWatch(object entity, IdentityKey identityKey)\r
132         {\r
133             var entityTrack = FindByReference(entity);\r
134             if (entityTrack == null)\r
135             {\r
136                 entityTrack = new EntityTrack(entity, EntityState.ToWatch) { IdentityKey = identityKey };\r
137                 entities.Add(entityTrack);\r
138                 entitiesByKey[identityKey] = entityTrack;\r
139             }\r
140             else\r
141             {\r
142                 // changes the state of the current entity\r
143                 switch (entityTrack.EntityState)\r
144                 {\r
145                 case EntityState.ToInsert:\r
146                     entityTrack.EntityState = EntityState.ToWatch;\r
147                     entityTrack.IdentityKey = identityKey;\r
148                     entitiesByKey[identityKey] = entityTrack;\r
149                     break;\r
150                 // watched entities should not be registered again\r
151                 case EntityState.ToWatch:\r
152                 case EntityState.ToDelete:\r
153                     throw new InvalidOperationException();\r
154                 default:\r
155                     throw new ArgumentOutOfRangeException();\r
156                 }\r
157             }\r
158         }\r
159 \r
160         /// <summary>\r
161         /// Registers entity to be deleted\r
162         /// </summary>\r
163         /// <param name="entity"></param>\r
164         public void RegisterToDelete(object entity)\r
165         {\r
166             if (entity == null)\r
167                 throw new ArgumentNullException("entity");\r
168 \r
169             var entityTrack = FindByReference(entity);\r
170             if (entityTrack == null)\r
171             {\r
172                 entityTrack = new EntityTrack(entity, EntityState.ToDelete);\r
173                 entities.Add(entityTrack);\r
174             }\r
175             else\r
176             {\r
177                 // changes the state of the current entity\r
178                 switch (entityTrack.EntityState)\r
179                 {\r
180                 // if entity was to be inserted, we just remove it from the list\r
181                 // as if it never came here\r
182                 case EntityState.ToInsert:\r
183                     entities.Remove(entityTrack);\r
184                     break;\r
185                 // watched entities are registered to be removed\r
186                 case EntityState.ToWatch:\r
187                     entityTrack.EntityState = EntityState.ToDelete;\r
188                     entitiesByKey.Remove(entityTrack.IdentityKey);\r
189                     break;\r
190                 case EntityState.ToDelete:\r
191                     throw new InvalidOperationException();\r
192                 default:\r
193                     throw new ArgumentOutOfRangeException();\r
194                 }\r
195             }\r
196         }\r
197 \r
198         /// <summary>\r
199         /// Unregisters the entity after deletion\r
200         /// </summary>\r
201         /// <param name="entity"></param>\r
202         public void RegisterDeleted(object entity)\r
203         {\r
204             // TODO: we could require an index\r
205             var entityTrack = FindByReference(entity);\r
206             if (entityTrack == null)\r
207             {\r
208                 throw new ArgumentException("entity");\r
209             }\r
210             // changes the state of the current entity\r
211             switch (entityTrack.EntityState)\r
212             {\r
213             case EntityState.ToDelete:\r
214                 entities.Remove(entityTrack);\r
215                 break;\r
216             case EntityState.ToInsert:\r
217             case EntityState.ToWatch:\r
218                 throw new InvalidOperationException();\r
219             default:\r
220                 throw new ArgumentOutOfRangeException();\r
221             }\r
222         }\r
223 \r
224         /// <summary>\r
225         /// Enumerates all registered entities\r
226         /// </summary>\r
227         /// <returns></returns>\r
228         public IEnumerable<EntityTrack> EnumerateAll()\r
229         {\r
230             return entities;\r
231         }\r
232     }\r
233 }\r