Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / ObjectItemCollection.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectItemCollection.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System.Collections.Generic;
11 // Using an alias for this because a lot of names in this namespace conflicts with names in metadata
12 using System.Data.Entity;
13 using System.Diagnostics;
14 using System.Reflection;
15
16 namespace System.Data.Metadata.Edm
17 {
18     /// <summary>
19     /// Class for representing a collection of items for the object layer.
20     /// Most of the implemetation for actual maintainance of the collection is
21     /// done by ItemCollection
22     /// </summary>
23     [CLSCompliant(false)]
24     public sealed partial class ObjectItemCollection : ItemCollection
25     {
26         #region Constructors
27
28         /// <summary>
29         /// The ObjectItemCollection that loads metadata from assemblies
30         /// </summary>
31         public ObjectItemCollection()
32             : base(DataSpace.OSpace)
33         {
34             foreach (PrimitiveType type in ClrProviderManifest.Instance.GetStoreTypes())
35             {
36                 AddInternal(type);
37                 _primitiveTypeMaps.Add(type);
38             }
39         }
40         #endregion
41
42         #region Fields
43
44         // Cache for primitive type maps for Edm to provider
45         private readonly CacheForPrimitiveTypes _primitiveTypeMaps = new CacheForPrimitiveTypes();
46
47         // Used for tracking the loading of an assembly and its referenced assemblies. Though the value of an entry is bool, the logic represented
48         // by an entry is tri-state, the third state represented by a "missing" entry. To summarize:
49         // 1. The <value> associated with an <entry> is "true"  : Specified and all referenced assemblies have been loaded 
50         // 2. The <value> associated with an <entry> is "false" : Specified assembly loaded. Its referenced assemblies may not be loaded
51         // 3. The <entry> is missing                            : Specified assembly has not been loaded
52         private KnownAssembliesSet _knownAssemblies = new KnownAssembliesSet();
53
54         // Dictionary which keeps tracks of oc mapping information - the key is the conceptual name of the type
55         // and the value is the reference to the ospace type
56         private Dictionary<string, EdmType> _ocMapping = new Dictionary<string, EdmType>();
57
58         private object _loaderCookie;
59         private object _loadAssemblyLock = new object();
60
61         internal object LoadAssemblyLock
62         {
63             get
64             {
65                 return _loadAssemblyLock;
66             }
67         }
68
69         internal static IList<Assembly> ViewGenerationAssemblies
70         {
71             get
72             {
73                 return AssemblyCache.ViewGenerationAssemblies;
74             }
75         }
76
77         #endregion
78
79         #region Methods
80
81
82         internal static bool IsCompiledViewGenAttributePresent(Assembly assembly)
83         {
84             return assembly.IsDefined(typeof(System.Data.Mapping.EntityViewGenerationAttribute), false /*inherit*/);
85         }
86
87         /// <summary>
88         /// The method loads the O-space metadata for all the referenced assemblies starting from the given assembly 
89         /// in a recursive way.
90         /// The assembly should be from Assembly.GetCallingAssembly via one of our public API's.
91         /// </summary>
92         /// <param name="assembly">assembly whose dependency list we are going to traverse</param>
93         internal void ImplicitLoadAllReferencedAssemblies(Assembly assembly, EdmItemCollection edmItemCollection)
94         {
95             if (!MetadataAssemblyHelper.ShouldFilterAssembly(assembly))
96             {
97                 bool loadAllReferencedAssemblies = true;
98                 LoadAssemblyFromCache(this, assembly, loadAllReferencedAssemblies, edmItemCollection, null);
99             }
100         }
101
102         internal void ImplicitLoadViewsFromAllReferencedAssemblies(Assembly assembly)
103         {
104             // we filter implicit loads
105             if (MetadataAssemblyHelper.ShouldFilterAssembly(assembly))
106             {
107                 return;
108             }
109             lock (this)
110             {
111                 CollectIfViewGenAssembly(assembly);
112
113                 foreach (Assembly referenceAssembly in MetadataAssemblyHelper.GetNonSystemReferencedAssemblies(assembly))
114                 {
115                     CollectIfViewGenAssembly(referenceAssembly);
116                 }
117             }
118         }
119
120         /// <summary>
121         /// Load metadata from the given assembly
122         /// </summary>
123         /// <param name="assembly">The assembly from which to load metadata</param>
124         /// <exception cref="System.ArgumentNullException">thrown if assembly argument is null</exception>
125         public void LoadFromAssembly(Assembly assembly)
126         {
127             ExplicitLoadFromAssembly(assembly, null, null);
128         }
129
130         /// <summary>
131         /// Load metadata from the given assembly
132         /// </summary>
133         /// <param name="assembly">The assembly from which to load metadata</param>
134         /// <exception cref="System.ArgumentNullException">thrown if assembly argument is null</exception>
135         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")]
136         public void LoadFromAssembly(Assembly assembly, EdmItemCollection edmItemCollection, Action<String> logLoadMessage)
137         {
138             EntityUtil.CheckArgumentNull(assembly, "assembly");
139             EntityUtil.CheckArgumentNull(edmItemCollection, "edmItemCollection");
140             EntityUtil.CheckArgumentNull(logLoadMessage, "logLoadMessage");
141
142             ExplicitLoadFromAssembly(assembly, edmItemCollection, logLoadMessage);
143         }
144
145         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")]
146         public void LoadFromAssembly(Assembly assembly, EdmItemCollection edmItemCollection)
147         {
148             EntityUtil.CheckArgumentNull(assembly, "assembly");
149             EntityUtil.CheckArgumentNull(edmItemCollection, "edmItemCollection");
150
151             ExplicitLoadFromAssembly(assembly, edmItemCollection, null);
152         }
153         /// <summary>
154         /// Explicit loading means that the user specifically asked us to load this assembly.
155         /// We won't do any filtering, they "know what they are doing"
156         /// </summary>
157         internal void ExplicitLoadFromAssembly(Assembly assembly, EdmItemCollection edmItemCollection, Action<String> logLoadMessage)
158         {
159             LoadAssemblyFromCache(this, assembly, false /*loadAllReferencedAssemblies*/, edmItemCollection, logLoadMessage);
160             //Since User called LoadFromAssembly, so we should collect the generated views if present
161             //even if the schema attribute is not present
162             if (IsCompiledViewGenAttributePresent(assembly) && !ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
163             {
164                 CollectIfViewGenAssembly(assembly);
165             }
166         }
167
168         /// <summary>
169         /// Implicit loading means that we are trying to help the user find the right 
170         /// assembly, but they didn't explicitly ask for it. Our Implicit rules require that
171         /// we filter out assemblies with the Ecma or MicrosoftPublic PublicKeyToken on them
172         /// </summary>
173         internal void ImplicitLoadFromAssembly(Assembly assembly, EdmItemCollection edmItemCollection)
174         {
175             if (!MetadataAssemblyHelper.ShouldFilterAssembly(assembly))
176             {
177                 // it meets the Implicit rules Load it
178                 ExplicitLoadFromAssembly(assembly, edmItemCollection, null);
179             }
180         }
181         
182         /// <summary>
183         /// Implicit loading means that we are trying to help the user find the right 
184         /// assembly, but they didn't explicitly ask for it. Our Implicit rules require that
185         /// we filter out assemblies with the Ecma or MicrosoftPublic PublicKeyToken on them
186         /// 
187         /// Load metadata from the type's assembly.
188         /// </summary>
189         /// <param name="type">The type's assembly is loaded into the OSpace ItemCollection</param>
190         /// <returns>true if the type and all its generic arguments are filtered out (did not attempt to load assembly)</returns>
191         internal bool ImplicitLoadAssemblyForType(Type type, EdmItemCollection edmItemCollection)
192         {
193             bool result;
194
195             if (!MetadataAssemblyHelper.ShouldFilterAssembly(type.Assembly))
196             {
197                 // InternalLoadFromAssembly will check _knownAssemblies
198                 result = LoadAssemblyFromCache(this, type.Assembly, false /*loadAllReferencedAssemblies*/, edmItemCollection, null);
199             }
200             else
201             {
202                 result = false;
203             }
204
205             if (type.IsGenericType)
206             {
207                 // recursively load all generic types
208                 // interesting code paths are ObjectQuery<Nullable<Int32>>, ObjectQuery<IEnumerable<Product>>
209                 foreach (Type t in type.GetGenericArguments())
210                 {
211                     result |= ImplicitLoadAssemblyForType(t, edmItemCollection);
212                 }
213             }
214             return result;
215         }
216
217         /// <summary>
218         /// internal static method to get the relationship name
219         /// </summary>
220         /// <param name="clrType"></param>
221         /// <param name="relationshipName"></param>
222         /// <returns></returns>
223         internal AssociationType GetRelationshipType(Type entityClrType, string relationshipName)
224         {
225             AssociationType associationType;
226             if (TryGetItem<AssociationType>(relationshipName, out associationType))
227             {
228                 return associationType;
229             }
230             return null;
231         }
232
233         /// <summary>
234         /// Loads the OSpace types in the assembly and returns them as a dictionary
235         /// </summary>
236         /// <param name="assembly">The assembly to load</param>
237         /// <returns>A mapping from names to OSpace EdmTypes</returns>
238         internal static Dictionary<string, EdmType> LoadTypesExpensiveWay(Assembly assembly)
239         {
240             Dictionary<string, EdmType> typesInLoading = null;
241                 
242             List<EdmItemError> errors;
243             KnownAssembliesSet knownAssemblies = new KnownAssembliesSet();
244
245             AssemblyCache.LoadAssembly(assembly, false /*loadAllReferencedAssemblies*/,
246                 knownAssemblies, out typesInLoading, out errors);
247
248             // Check for errors
249             if (errors.Count != 0)
250             {
251                 throw EntityUtil.InvalidSchemaEncountered(Helper.CombineErrorMessage(errors));
252             }
253
254             return typesInLoading;
255         }
256
257         /// <summary>
258         /// internal static method to get the relationship name
259         /// </summary>
260         /// <param name="clrType"></param>
261         /// <param name="relationshipName"></param>
262         /// <returns></returns>
263         internal static AssociationType GetRelationshipTypeExpensiveWay(Type entityClrType, string relationshipName)
264         {
265             Dictionary<string, EdmType> typesInLoading = LoadTypesExpensiveWay(entityClrType.Assembly);
266             if (typesInLoading != null)
267             {
268                 EdmType edmType;
269                 // Look in typesInLoading for relationship type
270                 if (typesInLoading.TryGetValue(relationshipName, out edmType) && Helper.IsRelationshipType(edmType))
271                 {
272                     return (AssociationType)edmType;
273                 }
274             }
275             return null;
276         }
277
278         /// <summary>
279         /// internal static method to get all the AssociationTypes from an assembly 
280         /// </summary>
281         /// <param name="assembly">The assembly from which to load relationship types</param>
282         /// <returns>An enumeration of OSpace AssociationTypes that are present in this assembly</returns>
283         internal static IEnumerable<AssociationType> GetAllRelationshipTypesExpensiveWay(Assembly assembly)
284         {
285             Dictionary<string, EdmType> typesInLoading = LoadTypesExpensiveWay(assembly);
286             if (typesInLoading != null)
287             {
288                 // Iterate through the EdmTypes looking for AssociationTypes
289                 foreach (EdmType edmType in typesInLoading.Values)
290                 {
291                     if (Helper.IsAssociationType(edmType))
292                     {
293                         yield return (AssociationType)edmType;
294                     }
295                 }
296             }
297             yield break;
298         }
299         
300         private static bool LoadAssemblyFromCache(ObjectItemCollection objectItemCollection, Assembly assembly,
301             bool loadReferencedAssemblies, EdmItemCollection edmItemCollection, Action<String> logLoadMessage)
302         {
303             // Check if its loaded in the cache - if the call is for loading referenced assemblies, make sure that all referenced
304             // assemblies are also loaded
305             KnownAssemblyEntry entry;
306             if (objectItemCollection._knownAssemblies.TryGetKnownAssembly(assembly, objectItemCollection._loaderCookie, edmItemCollection, out entry))
307             {
308                 // Proceed if only we need to load the referenced assemblies and they are not loaded
309                 if (loadReferencedAssemblies == false)
310                 {
311                     // don't say we loaded anything, unless we actually did before
312                     return entry.CacheEntry.TypesInAssembly.Count != 0;
313                 }
314                 else if (entry.ReferencedAssembliesAreLoaded == true)
315                 {
316                     // this assembly was part of a all hands reference search
317                     return true;
318                 }
319             }
320
321             lock (objectItemCollection.LoadAssemblyLock)
322             {
323                 // Check after acquiring the lock, since the known assemblies might have got modified
324                 // Check if the assembly is already loaded. The reason we need to check if the assembly is already loaded, is that 
325                 if (objectItemCollection._knownAssemblies.TryGetKnownAssembly(assembly, objectItemCollection._loaderCookie, edmItemCollection, out entry))
326                 {
327                     // Proceed if only we need to load the referenced assemblies and they are not loaded
328                     if (loadReferencedAssemblies == false || entry.ReferencedAssembliesAreLoaded == true)
329                     {
330                         return true;
331                     }
332                 }
333
334                 Dictionary<string, EdmType> typesInLoading;
335                 List<EdmItemError> errors;
336                 KnownAssembliesSet knownAssemblies;
337
338                 if (objectItemCollection != null)
339                 {
340                     knownAssemblies = new KnownAssembliesSet(objectItemCollection._knownAssemblies);
341                 }
342                 else
343                 {
344                     knownAssemblies = new KnownAssembliesSet();
345                 }
346
347                 // Load the assembly from the cache
348                 AssemblyCache.LoadAssembly(assembly, loadReferencedAssemblies, knownAssemblies, edmItemCollection, logLoadMessage, ref objectItemCollection._loaderCookie, out typesInLoading, out errors);
349
350                 // Throw if we have encountered errors
351                 if (errors.Count != 0)
352                 {
353                     throw EntityUtil.InvalidSchemaEncountered(Helper.CombineErrorMessage(errors));
354                 }
355
356                 // We can encounter new assemblies, but they may not have any time in them
357                 if (typesInLoading.Count != 0)
358                 {
359                     // No errors, so go ahead and add the types and make them readonly
360                     // The existence of the loading lock tells us whether we should be thread safe or not, if we need
361                     // to be thread safe, then we need to use AtomicAddRange. We don't need to actually use the lock
362                     // because the caller should have done it already
363                     // Recheck the assemblies added, another list is created just to match up the collection type
364                     // taken in by AtomicAddRange()
365                     List<GlobalItem> globalItems = new List<GlobalItem>();
366                     foreach (EdmType edmType in typesInLoading.Values)
367                     {
368                         globalItems.Add(edmType);
369
370                         string cspaceTypeName = "";
371                         try
372                         {
373                             // Also populate the ocmapping information
374                             if (Helper.IsEntityType(edmType))
375                             {
376                                 cspaceTypeName = ((ClrEntityType)edmType).CSpaceTypeName;
377                                 objectItemCollection._ocMapping.Add(cspaceTypeName, edmType);
378                             }
379                             else if (Helper.IsComplexType(edmType))
380                             {
381                                 cspaceTypeName = ((ClrComplexType)edmType).CSpaceTypeName;
382                                 objectItemCollection._ocMapping.Add(cspaceTypeName, edmType);
383                             }
384                             else if (Helper.IsEnumType(edmType))
385                             {
386                                 cspaceTypeName = ((ClrEnumType)edmType).CSpaceTypeName;
387                                 objectItemCollection._ocMapping.Add(cspaceTypeName, edmType);
388                             }
389                             // for the rest of the types like a relationship type, we do not have oc mapping, 
390                             // so we don't keep that information
391                         }
392                         catch (ArgumentException e)
393                         {
394                             throw new MappingException(Strings.Mapping_CannotMapCLRTypeMultipleTimes(cspaceTypeName), e);
395                         }
396                     }
397
398                     // Create a new ObjectItemCollection and add all the global items to it. 
399                     // Also copy all the existing items from the existing collection
400                     objectItemCollection.AtomicAddRange(globalItems);
401                 }
402                 
403                 
404                 // Update the value of known assemblies
405                 objectItemCollection._knownAssemblies = knownAssemblies;
406
407                 foreach (Assembly loadedAssembly in knownAssemblies.Assemblies)
408                 {
409                     CollectIfViewGenAssembly(loadedAssembly);
410                 }
411
412                 return typesInLoading.Count != 0;
413             }
414         }
415
416         /// <summary>
417         /// Check to see if the assembly has the custom view generation attribute AND
418         /// collect the assembly into the local list if it has cutom attribute.
419         /// </summary>
420         /// <param name="assembly"></param>
421         /// <param name="viewGenAssemblies"></param>
422         private static void CollectIfViewGenAssembly(Assembly assembly) 
423         {
424             if (assembly.IsDefined(typeof(System.Data.Mapping.EntityViewGenerationAttribute), false /*inherit*/))
425             {
426                 if (!AssemblyCache.ViewGenerationAssemblies.Contains(assembly))
427                 {
428                     AssemblyCache.ViewGenerationAssemblies.Add(assembly);
429                 }
430             }
431         }
432
433         /// <summary>
434         /// Get the list of primitive types for the given space
435         /// </summary>
436         /// <returns></returns> 
437         public IEnumerable<PrimitiveType> GetPrimitiveTypes()
438         {
439             return _primitiveTypeMaps.GetTypes();
440         }
441
442         /// <summary>
443         /// The method returns the underlying CLR type for the specified OSpace type argument.
444         /// If the DataSpace of the parameter is not OSpace, an ArgumentException is thrown.
445         /// </summary>
446         /// <param name="objectSpaceType">The OSpace type to look up</param>
447         /// <returns>The CLR type of the OSpace argument</returns>
448         public Type GetClrType(StructuralType objectSpaceType)
449         {
450             return ObjectItemCollection.GetClrType((EdmType)objectSpaceType);
451         }
452
453         /// <summary>
454         /// The method returns the underlying CLR type for the specified OSpace type argument.
455         /// If the DataSpace of the parameter is not OSpace, the method returns false and sets
456         /// the out parameter to null.
457         /// </summary>
458         /// <param name="objectSpaceType">The OSpace type to look up</param>
459         /// <param name="clrType">The CLR type of the OSpace argument</param>
460         /// <returns>true on success, false on failure</returns>
461         public bool TryGetClrType(StructuralType objectSpaceType, out Type clrType)
462         {
463             return ObjectItemCollection.TryGetClrType((EdmType)objectSpaceType, out clrType);
464         }
465
466         /// <summary>
467         /// The method returns the underlying CLR type for the specified OSpace type argument.
468         /// If the DataSpace of the parameter is not OSpace, an ArgumentException is thrown.
469         /// </summary>
470         /// <param name="objectSpaceType">The OSpace type to look up</param>
471         /// <returns>The CLR type of the OSpace argument</returns>
472         public Type GetClrType(EnumType objectSpaceType)
473         {
474             return ObjectItemCollection.GetClrType((EdmType)objectSpaceType);
475         }
476
477         /// <summary>
478         /// The method returns the underlying CLR type for the specified OSpace enum type argument.
479         /// If the DataSpace of the parameter is not OSpace, the method returns false and sets
480         /// the out parameter to null.
481         /// </summary>
482         /// <param name="objectSpaceType">The OSpace enum type to look up</param>
483         /// <param name="clrType">The CLR enum type of the OSpace argument</param>
484         /// <returns>true on success, false on failure</returns>
485         public bool TryGetClrType(EnumType objectSpaceType, out Type clrType)
486         {
487             return ObjectItemCollection.TryGetClrType((EdmType)objectSpaceType, out clrType);
488         }
489
490         /// <summary>
491         /// A helper method returning the underlying CLR type for the specified OSpace Enum or Structural type argument.
492         /// If the DataSpace of the parameter is not OSpace, an ArgumentException is thrown.
493         /// </summary>
494         /// <param name="objectSpaceType">The OSpace type to look up</param>
495         /// <returns>The CLR type of the OSpace argument</returns>
496         private static Type GetClrType(EdmType objectSpaceType)
497         {
498             Debug.Assert(objectSpaceType == null || objectSpaceType is StructuralType || objectSpaceType is EnumType,
499                 "Only enum or structural type expected");
500
501             Type clrType;
502             if (!ObjectItemCollection.TryGetClrType(objectSpaceType, out clrType))
503             {
504                 throw EntityUtil.Argument(Strings.FailedToFindClrTypeMapping(objectSpaceType.Identity));
505             }
506
507             return clrType;
508         }
509
510         /// <summary>
511         /// A helper method returning the underlying CLR type for the specified OSpace enum or structural type argument.
512         /// If the DataSpace of the parameter is not OSpace, the method returns false and sets
513         /// the out parameter to null.
514         /// </summary>
515         /// <param name="objectSpaceType">The OSpace enum type to look up</param>
516         /// <param name="clrType">The CLR enum type of the OSpace argument</param>
517         /// <returns>true on success, false on failure</returns>
518         private static bool TryGetClrType(EdmType objectSpaceType, out Type clrType)
519         {
520             Debug.Assert(objectSpaceType == null || objectSpaceType is StructuralType || objectSpaceType is EnumType, 
521                 "Only enum or structural type expected");
522
523             EntityUtil.CheckArgumentNull(objectSpaceType, "objectSpaceType");
524
525             if (objectSpaceType.DataSpace != DataSpace.OSpace)
526             {
527                 throw EntityUtil.Argument(Strings.ArgumentMustBeOSpaceType, "objectSpaceType");
528             }
529
530             clrType = null;
531
532             if (Helper.IsEntityType(objectSpaceType) || Helper.IsComplexType(objectSpaceType) || Helper.IsEnumType(objectSpaceType))
533             {
534                 Debug.Assert(objectSpaceType is ClrEntityType || objectSpaceType is ClrComplexType || objectSpaceType is ClrEnumType,
535                     "Unexpected OSpace object type.");
536
537                 clrType = objectSpaceType.ClrType;
538
539                 Debug.Assert(clrType != null, "ClrType property of ClrEntityType/ClrComplexType/ClrEnumType objects must not be null");
540             }
541
542             return clrType != null;
543         }
544
545         /// <summary>
546         /// Given the canonical primitive type, get the mapping primitive type in the given dataspace
547         /// </summary>
548         /// <param name="modelType">canonical primitive type</param>
549         /// <returns>The mapped scalar type</returns>
550         internal override PrimitiveType GetMappedPrimitiveType(PrimitiveTypeKind modelType)
551         {
552             if (Helper.IsGeometricTypeKind(modelType))
553             {
554                 modelType = PrimitiveTypeKind.Geometry;
555             }
556             else if (Helper.IsGeographicTypeKind(modelType))
557             {
558                 modelType = PrimitiveTypeKind.Geography;
559             }
560
561             PrimitiveType type = null;
562             _primitiveTypeMaps.TryGetType(modelType, null, out type);
563             return type;
564         }
565
566         /// <summary>
567         /// Get the OSpace type given the CSpace typename
568         /// </summary>
569         /// <param name="cspaceTypeName"></param>
570         /// <param name="ignoreCase"></param>
571         /// <param name="edmType"></param>
572         /// <returns></returns>
573         internal bool TryGetOSpaceType(EdmType cspaceType, out EdmType edmType)
574         {
575             Debug.Assert(DataSpace.CSpace == cspaceType.DataSpace, "DataSpace should be CSpace");
576
577             // check if there is an entity, complex type or enum type mapping with this name
578             if (Helper.IsEntityType(cspaceType) || Helper.IsComplexType(cspaceType) || Helper.IsEnumType(cspaceType))
579             {
580                 return _ocMapping.TryGetValue(cspaceType.Identity, out edmType);
581             }
582
583             return TryGetItem<EdmType>(cspaceType.Identity, out edmType);
584         }
585
586         /// <summary>
587         /// Given the ospace type, returns the fullname of the mapped cspace type.
588         /// Today, since we allow non-default mapping between entity type and complex type,
589         /// this is only possible for entity and complex type.
590         /// </summary>
591         /// <param name="edmType"></param>
592         /// <returns></returns>
593         internal static string TryGetMappingCSpaceTypeIdentity(EdmType edmType)
594         {
595             Debug.Assert(DataSpace.OSpace == edmType.DataSpace, "DataSpace must be OSpace");
596
597             if (Helper.IsEntityType(edmType))
598             {
599                 return ((ClrEntityType)edmType).CSpaceTypeName;
600             }
601             else if (Helper.IsComplexType(edmType))
602             {
603                 return ((ClrComplexType)edmType).CSpaceTypeName;
604             }
605             else if (Helper.IsEnumType(edmType))
606             {
607                 return ((ClrEnumType)edmType).CSpaceTypeName;
608             }
609
610             return edmType.Identity;
611         }
612
613         public override System.Collections.ObjectModel.ReadOnlyCollection<T> GetItems<T>()
614         {
615             return base.InternalGetItems(typeof(T)) as System.Collections.ObjectModel.ReadOnlyCollection<T>;
616         }
617         #endregion
618     }
619 }