Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / Edm / EntityType.cs
blob60d95712d4faefc3d8aaaab0a59dbd898b8d19f2
1 //---------------------------------------------------------------------
2 // <copyright file="EntityType.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Data.Common;
13 using System.Diagnostics;
14 using System.Text;
15 using System.Threading;
16 using System.Security.Cryptography;
17 using System.Globalization;
19 namespace System.Data.Metadata.Edm
21 /// <summary>
22 /// concrete Representation the Entity Type
23 /// </summary>
24 public class EntityType : EntityTypeBase
26 #region Constructors
27 /// <summary>
28 /// Initializes a new instance of Entity Type
29 /// </summary>
30 /// <param name="name">name of the entity type</param>
31 /// <param name="namespaceName">namespace of the entity type</param>
32 /// <param name="version">version of the entity type</param>
33 /// <param name="dataSpace">dataspace in which the EntityType belongs to</param>
34 /// <exception cref="System.ArgumentNullException">Thrown if either name, namespace or version arguments are null</exception>
35 internal EntityType(string name, string namespaceName, DataSpace dataSpace)
36 : base(name, namespaceName, dataSpace)
40 /// <param name="name">name of the entity type</param>
41 /// <param name="namespaceName">namespace of the entity type</param>
42 /// <param name="version">version of the entity type</param>
43 /// <param name="dataSpace">dataspace in which the EntityType belongs to</param>
44 /// <param name="members">members of the entity type [property and navigational property]</param>
45 /// <param name="keyMemberNames">key members for the type</param>
46 /// <exception cref="System.ArgumentNullException">Thrown if either name, namespace or version arguments are null</exception>
47 internal EntityType(string name,
48 string namespaceName,
49 DataSpace dataSpace,
50 IEnumerable<string> keyMemberNames,
51 IEnumerable<EdmMember> members)
52 : base(name, namespaceName, dataSpace)
54 //--- first add the properties
55 if (null != members)
57 CheckAndAddMembers(members, this);
59 //--- second add the key members
60 if (null != keyMemberNames)
62 //Validation should make sure that base type of this type does not have keymembers when this type has keymembers.
63 CheckAndAddKeyMembers(keyMemberNames);
68 #endregion
70 #region Fields
71 /// <summary>cached dynamic method to construct a CLR instance</summary>
72 private RefType _referenceType;
73 private ReadOnlyMetadataCollection<EdmProperty> _properties;
74 private RowType _keyRow;
75 private Dictionary<EdmMember, string> _memberSql;
76 private object _memberSqlLock = new object();
77 #endregion
79 #region Methods
80 /// <summary>
81 /// Returns the kind of the type
82 /// </summary>
83 public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.EntityType; } }
85 /// <summary>
86 /// Validates a EdmMember object to determine if it can be added to this type's
87 /// Members collection. If this method returns without throwing, it is assumed
88 /// the member is valid.
89 /// </summary>
90 /// <param name="member">The member to validate</param>
91 /// <exception cref="System.ArgumentException">Thrown if the member is not a EdmProperty</exception>
92 internal override void ValidateMemberForAdd(EdmMember member)
94 Debug.Assert(Helper.IsEdmProperty(member) || Helper.IsNavigationProperty(member),
95 "Only members of type Property may be added to Entity types.");
98 /// <summary>
99 /// Get SQL description of a member of this entity type.
100 /// Requires: member must belong to this type
101 /// </summary>
102 /// <param name="member">Member for which to retrieve SQL</param>
103 /// <param name="sql">Outputs SQL describing this member</param>
104 /// <returns>Whether sql is cached for this member</returns>
105 internal bool TryGetMemberSql(EdmMember member, out string sql)
107 Debug.Assert(Members.Contains(member));
108 sql = null;
109 return null != _memberSql && _memberSql.TryGetValue(member, out sql);
112 /// <summary>
113 /// Sets SQL describing a member of this entity type.
114 /// Requires: member must belong to this type
115 /// </summary>
116 /// <param name="member">Member for which to set SQL</param>
117 /// <param name="sql">SQL describing this member</param>
118 internal void SetMemberSql(EdmMember member, string sql)
120 Debug.Assert(Members.Contains(member));
122 // initialize dictionary on first use
123 lock (_memberSqlLock)
125 if (null == _memberSql)
127 _memberSql = new Dictionary<EdmMember, string>();
130 _memberSql[member] = sql;
133 #endregion
135 #region Properties
137 /// <summary>
138 /// Returns the list of Navigation Properties for this entity type
139 /// </summary>
140 public ReadOnlyMetadataCollection<NavigationProperty> NavigationProperties
144 return new FilteredReadOnlyMetadataCollection<NavigationProperty, EdmMember>(
145 ((ReadOnlyMetadataCollection<EdmMember>)this.Members), Helper.IsNavigationProperty);
149 /// <summary>
150 /// Returns just the properties from the collection
151 /// of members on this type
152 /// </summary>
153 public ReadOnlyMetadataCollection<EdmProperty> Properties
157 Debug.Assert(IsReadOnly, "this is a wrapper around this.Members, don't call it during metadata loading, only call it after the metadata is set to readonly");
158 if (null == _properties)
160 Interlocked.CompareExchange(ref _properties,
161 new FilteredReadOnlyMetadataCollection<EdmProperty, EdmMember>(
162 this.Members, Helper.IsEdmProperty), null);
164 return _properties;
168 #endregion // Properties
170 /// <summary>
171 /// Returns the Reference type pointing to this entity type
172 /// </summary>
173 /// <returns></returns>
174 public RefType GetReferenceType()
176 if (_referenceType == null)
178 Interlocked.CompareExchange<RefType>(ref _referenceType, new RefType(this), null);
180 return _referenceType;
183 internal RowType GetKeyRowType(MetadataWorkspace metadataWorkspace)
185 if (_keyRow == null)
187 List<EdmProperty> keyProperties = new List<EdmProperty>(KeyMembers.Count);
188 foreach (EdmMember keyMember in KeyMembers)
190 keyProperties.Add(new EdmProperty(keyMember.Name, Helper.GetModelTypeUsage(keyMember)));
192 Interlocked.CompareExchange<RowType>(ref _keyRow, new RowType(keyProperties), null);
194 return _keyRow;
197 /// <summary>
198 /// Attempts to get the property name for the ----oication between the two given end
199 /// names. Note that this property may not exist if a navigation property is defined
200 /// in one direction but not in the other.
201 /// </summary>
202 /// <param name="relationshipType">the relationship for which a nav property is required</param>
203 /// <param name="fromName">the 'from' end of the association</param>
204 /// <param name="toName">the 'to' end of the association</param>
205 /// <param name="navigationProperty">the property name, or null if none was found</param>
206 /// <returns>true if a property was found, false otherwise</returns>
207 internal bool TryGetNavigationProperty(string relationshipType, string fromName, string toName, out NavigationProperty navigationProperty)
209 // This is a linear search but it's probably okay because the number of entries
210 // is generally small and this method is only called to generate code during lighweight
211 // code gen.
212 foreach (NavigationProperty navProperty in NavigationProperties)
214 if (navProperty.RelationshipType.FullName == relationshipType &&
215 navProperty.FromEndMember.Name == fromName &&
216 navProperty.ToEndMember.Name == toName)
218 navigationProperty = navProperty;
219 return true;
222 navigationProperty = null;
223 return false;
227 internal sealed class ClrEntityType : EntityType
229 /// <summary>cached CLR type handle, allowing the Type reference to be GC'd</summary>
230 private readonly System.RuntimeTypeHandle _type;
232 /// <summary>cached dynamic method to construct a CLR instance</summary>
233 private Delegate _constructor;
235 private readonly string _cspaceTypeName;
237 private readonly string _cspaceNamespaceName;
239 private string _hash;
241 /// <summary>
242 /// Initializes a new instance of Complex Type with properties from the type.
243 /// </summary>
244 /// <param name="type">The CLR type to construct from</param>
245 internal ClrEntityType(Type type, string cspaceNamespaceName, string cspaceTypeName)
246 : base(EntityUtil.GenericCheckArgumentNull(type, "type").Name, type.Namespace ?? string.Empty,
247 DataSpace.OSpace)
249 System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(cspaceNamespaceName) &&
250 !String.IsNullOrEmpty(cspaceTypeName), "Mapping information must never be null");
252 _type = type.TypeHandle;
253 _cspaceNamespaceName = cspaceNamespaceName;
254 _cspaceTypeName = cspaceNamespaceName + "." + cspaceTypeName;
255 this.Abstract = type.IsAbstract;
258 /// <summary>cached dynamic method to construct a CLR instance</summary>
259 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
260 internal Delegate Constructor
262 get { return _constructor; }
265 // It doesn't matter which delegate wins, but only one should be jitted
266 Interlocked.CompareExchange(ref _constructor, value, null);
270 /// <summary>
271 /// </summary>
272 internal override System.Type ClrType
274 get { return Type.GetTypeFromHandle(_type); }
277 internal string CSpaceTypeName { get { return _cspaceTypeName; } }
279 internal string CSpaceNamespaceName { get { return _cspaceNamespaceName; } }
281 /// <summary>
282 /// Gets a collision resistent (SHA256) hash of the information used to build
283 /// a proxy for this type. This hash is very, very unlikely to be the same for two
284 /// proxies generated from the same CLR type but with different metadata, and is
285 /// guarenteed to be the same for proxies generated from the same metadata. This
286 /// means that when EntityType comparison fails because of metadata eviction,
287 /// the hash can be used to determine whether or not a proxy is of the correct type.
288 /// </summary>
289 internal string HashedDescription
293 if (_hash == null)
295 Interlocked.CompareExchange(ref _hash, BuildEntityTypeHash(), null);
297 return _hash;
301 /// <summary>
302 /// Creates an SHA256 hash of a description of all the metadata relevant to the creation of a proxy type
303 /// for this entity type.
304 /// </summary>
305 private string BuildEntityTypeHash()
307 var hash = System.Data.Common.Utils.MetadataHelper.CreateSHA256HashAlgorithm()
308 .ComputeHash(Encoding.ASCII.GetBytes(BuildEntityTypeDescription()));
310 // convert num bytes to num hex digits
311 var builder = new StringBuilder(hash.Length * 2);
312 foreach (byte bite in hash)
314 builder.Append(bite.ToString("X2", CultureInfo.InvariantCulture));
317 return builder.ToString();
320 /// <summary>
321 /// Creates a description of all the metadata relevant to the creation of a proxy type
322 /// for this entity type.
323 /// </summary>
324 private string BuildEntityTypeDescription()
326 var builder = new StringBuilder(512);
327 Debug.Assert(ClrType != null, "Expecting non-null CLRType of o-space EntityType.");
328 builder.Append("CLR:").Append(ClrType.FullName);
329 builder.Append("Conceptual:").Append(CSpaceTypeName);
331 var navProps = new SortedSet<string>();
332 foreach (var navProperty in NavigationProperties)
334 navProps.Add(navProperty.Name + "*" +
335 navProperty.FromEndMember.Name + "*" +
336 navProperty.FromEndMember.RelationshipMultiplicity + "*" +
337 navProperty.ToEndMember.Name + "*" +
338 navProperty.ToEndMember.RelationshipMultiplicity + "*");
340 builder.Append("NavProps:");
341 foreach (var navProp in navProps)
343 builder.Append(navProp);
346 var keys = new SortedSet<string>();
347 foreach (var member in KeyMemberNames)
349 keys.Add(member);
351 builder.Append("Keys:");
352 foreach (var key in keys)
354 builder.Append(key + "*");
357 var scalars = new SortedSet<string>();
358 foreach (var member in Members)
360 if (!keys.Contains(member.Name))
362 scalars.Add(member.Name + "*");
365 builder.Append("Scalars:");
366 foreach (var scalar in scalars)
368 builder.Append(scalar + "*");
371 return builder.ToString();