Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / Edm / EntityType.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityType.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
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;
18
19 namespace System.Data.Metadata.Edm
20 {
21     /// <summary>
22     /// concrete Representation the Entity Type
23     /// </summary>
24     public class EntityType : EntityTypeBase
25     {
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)
37         {
38         }
39
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)
53         {
54             //--- first add the properties 
55             if (null != members)
56             {
57                 CheckAndAddMembers(members, this);
58             }
59             //--- second add the key members
60             if (null != keyMemberNames)
61             {
62                 //Validation should make sure that base type of this type does not have keymembers when this type has keymembers. 
63                 CheckAndAddKeyMembers(keyMemberNames);
64             }
65         }
66
67
68         #endregion
69
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
78
79         #region Methods
80         /// <summary>
81         /// Returns the kind of the type
82         /// </summary>
83         public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.EntityType; } }
84
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)
93         {
94             Debug.Assert(Helper.IsEdmProperty(member) || Helper.IsNavigationProperty(member),
95                 "Only members of type Property may be added to Entity types.");
96         }
97
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)
106         {
107             Debug.Assert(Members.Contains(member));
108             sql = null;
109             return null != _memberSql && _memberSql.TryGetValue(member, out sql);
110         }
111
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)
119         {
120             Debug.Assert(Members.Contains(member));
121
122             // initialize dictionary on first use
123             lock (_memberSqlLock)
124             {
125                 if (null == _memberSql)
126                 {
127                     _memberSql = new Dictionary<EdmMember, string>();
128                 }
129
130                 _memberSql[member] = sql;
131             }
132         }
133         #endregion
134
135         #region Properties
136
137         /// <summary>
138         /// Returns the list of Navigation Properties for this entity type
139         /// </summary>
140         public ReadOnlyMetadataCollection<NavigationProperty> NavigationProperties
141         {
142             get
143             {
144                 return new FilteredReadOnlyMetadataCollection<NavigationProperty, EdmMember>(
145                     ((ReadOnlyMetadataCollection<EdmMember>)this.Members), Helper.IsNavigationProperty);
146             }
147         }
148
149         /// <summary>
150         /// Returns just the properties from the collection
151         /// of members on this type
152         /// </summary>
153         public ReadOnlyMetadataCollection<EdmProperty> Properties
154         {
155             get
156             {
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)
159                 {
160                     Interlocked.CompareExchange(ref _properties,
161                         new FilteredReadOnlyMetadataCollection<EdmProperty, EdmMember>(
162                             this.Members, Helper.IsEdmProperty), null);
163                 }
164                 return _properties;
165             }
166         }
167
168         #endregion // Properties
169
170         /// <summary>
171         /// Returns the Reference type pointing to this entity type
172         /// </summary>
173         /// <returns></returns>
174         public RefType GetReferenceType()
175         {
176             if (_referenceType == null)
177             {
178                 Interlocked.CompareExchange<RefType>(ref _referenceType, new RefType(this), null);
179             }
180             return _referenceType;
181         }
182
183         internal RowType GetKeyRowType(MetadataWorkspace metadataWorkspace)
184         {
185             if (_keyRow == null)
186             {
187                 List<EdmProperty> keyProperties = new List<EdmProperty>(KeyMembers.Count);
188                 foreach (EdmMember keyMember in KeyMembers)
189                 {
190                     keyProperties.Add(new EdmProperty(keyMember.Name, Helper.GetModelTypeUsage(keyMember)));
191                 }
192                 Interlocked.CompareExchange<RowType>(ref _keyRow, new RowType(keyProperties), null);
193             }
194             return _keyRow;
195         }
196
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)
208         {
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)
213             {
214                 if (navProperty.RelationshipType.FullName == relationshipType &&
215                     navProperty.FromEndMember.Name == fromName &&
216                     navProperty.ToEndMember.Name == toName)
217                 {
218                     navigationProperty = navProperty;
219                     return true;
220                 }
221             }
222             navigationProperty = null;
223             return false;
224         }
225     }
226
227     internal sealed class ClrEntityType : EntityType
228     {
229         /// <summary>cached CLR type handle, allowing the Type reference to be GC'd</summary>
230         private readonly System.RuntimeTypeHandle _type;
231
232         /// <summary>cached dynamic method to construct a CLR instance</summary>
233         private Delegate _constructor;
234
235         private readonly string _cspaceTypeName;
236
237         private readonly string _cspaceNamespaceName;
238
239         private string _hash;
240
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)
248         {
249             System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(cspaceNamespaceName) &&
250                 !String.IsNullOrEmpty(cspaceTypeName), "Mapping information must never be null");
251
252             _type = type.TypeHandle;
253             _cspaceNamespaceName = cspaceNamespaceName;
254             _cspaceTypeName = cspaceNamespaceName + "." + cspaceTypeName;
255             this.Abstract = type.IsAbstract;
256         }
257
258         /// <summary>cached dynamic method to construct a CLR instance</summary>
259         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
260         internal Delegate Constructor
261         {
262             get { return _constructor; }
263             set
264             {
265                 // It doesn't matter which delegate wins, but only one should be jitted
266                 Interlocked.CompareExchange(ref _constructor, value, null);
267             }
268         }
269
270         /// <summary>
271         /// </summary>
272         internal override System.Type ClrType
273         {
274             get { return Type.GetTypeFromHandle(_type); }
275         }
276
277         internal string CSpaceTypeName { get { return _cspaceTypeName; } }
278
279         internal string CSpaceNamespaceName { get { return _cspaceNamespaceName; } }
280
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
290         {
291             get
292             {
293                 if (_hash == null)
294                 {
295                     Interlocked.CompareExchange(ref _hash, BuildEntityTypeHash(), null);
296                 }
297                 return _hash;
298             }
299         }
300
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()
306         {
307             var hash = System.Data.Common.Utils.MetadataHelper.CreateSHA256HashAlgorithm()
308                 .ComputeHash(Encoding.ASCII.GetBytes(BuildEntityTypeDescription()));
309
310             // convert num bytes to num hex digits
311             var builder = new StringBuilder(hash.Length * 2);
312             foreach (byte bite in hash)
313             {
314                 builder.Append(bite.ToString("X2", CultureInfo.InvariantCulture));
315             }
316
317             return builder.ToString();
318         }
319
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()
325         {
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);
330
331             var navProps = new SortedSet<string>();
332             foreach (var navProperty in NavigationProperties)
333             {
334                 navProps.Add(navProperty.Name + "*" +
335                              navProperty.FromEndMember.Name + "*" +
336                              navProperty.FromEndMember.RelationshipMultiplicity + "*" +
337                              navProperty.ToEndMember.Name + "*" +
338                              navProperty.ToEndMember.RelationshipMultiplicity + "*");
339             }
340             builder.Append("NavProps:");
341             foreach (var navProp in navProps)
342             {
343                 builder.Append(navProp);
344             }
345
346             var keys = new SortedSet<string>();
347             foreach (var member in KeyMemberNames)
348             {
349                 keys.Add(member);
350             }
351             builder.Append("Keys:");
352             foreach (var key in keys)
353             {
354                 builder.Append(key + "*");
355             }
356
357             var scalars = new SortedSet<string>();
358             foreach (var member in Members)
359             {
360                 if (!keys.Contains(member.Name))
361                 {
362                     scalars.Add(member.Name + "*");
363                 }
364             }
365             builder.Append("Scalars:");
366             foreach (var scalar in scalars)
367             {
368                 builder.Append(scalar + "*");
369             }
370
371             return builder.ToString();
372         }
373     }
374 }