/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/Binding/BindingEntityInfo.cs
C# | 360 lines | 292 code | 58 blank | 10 comment | 41 complexity | dfc20d4bda1c39e8a1b754975cdfc888 MD5 | raw file
1//Copyright 2010 Microsoft Corporation
2//
3//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
4//You may obtain a copy of the License at
5//
6//http://www.apache.org/licenses/LICENSE-2.0
7//
8//Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
9//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10//See the License for the specific language governing permissions and limitations under the License.
11
12
13namespace System.Data.Services.Client
14{
15#region Namespaces
16 using System.Collections.Generic;
17 using System.ComponentModel;
18 using System.Data.Services.Common;
19 using System.Diagnostics;
20 using System.Linq;
21 using System.Reflection;
22 using System.Threading;
23#endregion
24
25 internal enum BindingPropertyKind
26 {
27 BindingPropertyKindComplex,
28
29 BindingPropertyKindEntity,
30
31 BindingPropertyKindCollection
32 }
33
34 internal class BindingEntityInfo
35 {
36 private static readonly object FalseObject = new object();
37
38 private static readonly object TrueObject = new object();
39
40 private static readonly ReaderWriterLockSlim metadataCacheLock = new ReaderWriterLockSlim();
41
42 private static readonly HashSet<Type> knownNonEntityTypes = new HashSet<Type>(EqualityComparer<Type>.Default);
43
44 private static readonly Dictionary<Type, object> knownObservableCollectionTypes = new Dictionary<Type, object>(EqualityComparer<Type>.Default);
45
46 private static readonly Dictionary<Type, BindingEntityInfoPerType> bindingEntityInfos = new Dictionary<Type, BindingEntityInfoPerType>(EqualityComparer<Type>.Default);
47
48 internal static IList<BindingPropertyInfo> GetObservableProperties(Type entityType)
49 {
50 return GetBindingEntityInfoFor(entityType).ObservableProperties;
51 }
52
53 internal static ClientType GetClientType(Type entityType)
54 {
55 return GetBindingEntityInfoFor(entityType).ClientType;
56 }
57
58 internal static string GetEntitySet(
59 object target,
60 string targetEntitySet)
61 {
62 Debug.Assert(target != null, "Argument 'target' cannot be null.");
63 Debug.Assert(BindingEntityInfo.IsEntityType(target.GetType()), "Argument 'target' must be an entity type.");
64
65 if (!String.IsNullOrEmpty(targetEntitySet))
66 {
67 return targetEntitySet;
68 }
69 else
70 {
71 return BindingEntityInfo.GetEntitySetAttribute(target.GetType());
72 }
73 }
74
75 internal static bool IsDataServiceCollection(Type collectionType)
76 {
77 Debug.Assert(collectionType != null, "Argument 'collectionType' cannot be null.");
78
79 metadataCacheLock.EnterReadLock();
80 try
81 {
82 object resultAsObject;
83 if (knownObservableCollectionTypes.TryGetValue(collectionType, out resultAsObject))
84 {
85 return resultAsObject == TrueObject;
86 }
87 }
88 finally
89 {
90 metadataCacheLock.ExitReadLock();
91 }
92
93 Type type = collectionType;
94 bool result = false;
95
96 while (type != null)
97 {
98 if (type.IsGenericType)
99 {
100 Type[] parms = type.GetGenericArguments();
101
102 if (parms != null && parms.Length == 1 && IsEntityType(parms[0]))
103 {
104 Type dataServiceCollection = WebUtil.GetDataServiceCollectionOfT(parms);
105 if (dataServiceCollection != null && dataServiceCollection.IsAssignableFrom(type))
106 {
107 result = true;
108 break;
109 }
110 }
111 }
112
113 type = type.BaseType;
114 }
115
116 metadataCacheLock.EnterWriteLock();
117 try
118 {
119 if (!knownObservableCollectionTypes.ContainsKey(collectionType))
120 {
121 knownObservableCollectionTypes[collectionType] = result ? TrueObject : FalseObject;
122 }
123 }
124 finally
125 {
126 metadataCacheLock.ExitWriteLock();
127 }
128
129 return result;
130 }
131
132 internal static bool IsEntityType(Type type)
133 {
134 Debug.Assert(type != null, "Argument 'type' cannot be null.");
135
136 metadataCacheLock.EnterReadLock();
137 try
138 {
139 if (knownNonEntityTypes.Contains(type))
140 {
141 return false;
142 }
143 }
144 finally
145 {
146 metadataCacheLock.ExitReadLock();
147 }
148
149 try
150 {
151 if (BindingEntityInfo.IsDataServiceCollection(type))
152 {
153 return false;
154 }
155
156 return ClientType.Create(type).IsEntityType;
157 }
158 catch (InvalidOperationException)
159 {
160 metadataCacheLock.EnterWriteLock();
161 try
162 {
163 if (!knownNonEntityTypes.Contains(type))
164 {
165 knownNonEntityTypes.Add(type);
166 }
167 }
168 finally
169 {
170 metadataCacheLock.ExitWriteLock();
171 }
172
173 return false;
174 }
175 }
176
177 internal static object GetPropertyValue(object source, string sourceProperty, out BindingPropertyInfo bindingPropertyInfo)
178 {
179 Type sourceType = source.GetType();
180
181 bindingPropertyInfo = BindingEntityInfo.GetObservableProperties(sourceType)
182 .SingleOrDefault(x => x.PropertyInfo.PropertyName == sourceProperty);
183
184 if (bindingPropertyInfo == null)
185 {
186 return BindingEntityInfo.GetClientType(sourceType)
187 .GetProperty(sourceProperty, false)
188 .GetValue(source);
189 }
190 else
191 {
192 return bindingPropertyInfo.PropertyInfo.GetValue(source);
193 }
194 }
195
196 private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType)
197 {
198 BindingEntityInfoPerType bindingEntityInfo;
199
200 metadataCacheLock.EnterReadLock();
201 try
202 {
203 if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo))
204 {
205 return bindingEntityInfo;
206 }
207 }
208 finally
209 {
210 metadataCacheLock.ExitReadLock();
211 }
212
213 bindingEntityInfo = new BindingEntityInfoPerType();
214
215 object[] attributes = entityType.GetCustomAttributes(typeof(EntitySetAttribute), true);
216
217 bindingEntityInfo.EntitySet = (attributes != null && attributes.Length == 1) ? ((EntitySetAttribute)attributes[0]).EntitySet : null;
218 bindingEntityInfo.ClientType = ClientType.Create(entityType);
219
220 foreach (ClientType.ClientProperty p in bindingEntityInfo.ClientType.Properties)
221 {
222 BindingPropertyInfo bpi = null;
223
224 Type propertyType = p.PropertyType;
225
226 if (p.CollectionType != null)
227 {
228 if (BindingEntityInfo.IsDataServiceCollection(propertyType))
229 {
230 bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindCollection };
231 }
232 }
233 else
234 if (BindingEntityInfo.IsEntityType(propertyType))
235 {
236 bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity };
237 }
238 else
239 if (BindingEntityInfo.CanBeComplexProperty(p))
240 {
241 bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex };
242 }
243
244 if (bpi != null)
245 {
246 bpi.PropertyInfo = p;
247
248 if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex)
249 {
250 bindingEntityInfo.ObservableProperties.Add(bpi);
251 }
252 }
253 }
254
255 metadataCacheLock.EnterWriteLock();
256 try
257 {
258 if (!bindingEntityInfos.ContainsKey(entityType))
259 {
260 bindingEntityInfos[entityType] = bindingEntityInfo;
261 }
262 }
263 finally
264 {
265 metadataCacheLock.ExitWriteLock();
266 }
267
268 return bindingEntityInfo;
269 }
270
271 private static bool CanBeComplexProperty(ClientType.ClientProperty property)
272 {
273 Debug.Assert(property != null, "property != null");
274 if (typeof(INotifyPropertyChanged).IsAssignableFrom(property.PropertyType))
275 {
276 Debug.Assert(!property.IsKnownType, "Known types do not implement INotifyPropertyChanged.");
277 return true;
278 }
279
280 return false;
281 }
282
283 private static string GetEntitySetAttribute(Type entityType)
284 {
285 return GetBindingEntityInfoFor(entityType).EntitySet;
286 }
287
288 internal class BindingPropertyInfo
289 {
290 public ClientType.ClientProperty PropertyInfo
291 {
292 get;
293 set;
294 }
295
296 public BindingPropertyKind PropertyKind
297 {
298 get;
299 set;
300 }
301 }
302
303 private sealed class BindingEntityInfoPerType
304 {
305 private List<BindingPropertyInfo> observableProperties;
306
307 public BindingEntityInfoPerType()
308 {
309 this.observableProperties = new List<BindingPropertyInfo>();
310 }
311
312 public String EntitySet
313 {
314 get;
315 set;
316 }
317
318 public ClientType ClientType
319 {
320 get;
321 set;
322 }
323
324 public List<BindingPropertyInfo> ObservableProperties
325 {
326 get
327 {
328 return this.observableProperties;
329 }
330 }
331 }
332
333#if ASTORIA_LIGHT
334 private sealed class ReaderWriterLockSlim
335 {
336 private object _lock = new object();
337
338 internal void EnterReadLock()
339 {
340 Monitor.Enter(_lock);
341 }
342
343 internal void EnterWriteLock()
344 {
345 Monitor.Enter(_lock);
346 }
347
348 internal void ExitReadLock()
349 {
350 Monitor.Exit(_lock);
351 }
352
353 internal void ExitWriteLock()
354 {
355 Monitor.Exit(_lock);
356 }
357 }
358#endif
359 }
360}