1 //---------------------------------------------------------------------
2 // <copyright file="EntityType.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System
.Collections
.Generic
;
11 using System
.Collections
.ObjectModel
;
12 using System
.Data
.Common
;
13 using System
.Diagnostics
;
15 using System
.Threading
;
16 using System
.Security
.Cryptography
;
17 using System
.Globalization
;
19 namespace System
.Data
.Metadata
.Edm
22 /// concrete Representation the Entity Type
24 public class EntityType
: EntityTypeBase
28 /// Initializes a new instance of Entity Type
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
,
50 IEnumerable
<string> keyMemberNames
,
51 IEnumerable
<EdmMember
> members
)
52 : base(name
, namespaceName
, dataSpace
)
54 //--- first add the properties
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
);
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();
81 /// Returns the kind of the type
83 public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.EntityType; }
}
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.
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.");
99 /// Get SQL description of a member of this entity type.
100 /// Requires: member must belong to this type
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
));
109 return null != _memberSql
&& _memberSql
.TryGetValue(member
, out sql
);
113 /// Sets SQL describing a member of this entity type.
114 /// Requires: member must belong to this type
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
;
138 /// Returns the list of Navigation Properties for this entity type
140 public ReadOnlyMetadataCollection
<NavigationProperty
> NavigationProperties
144 return new FilteredReadOnlyMetadataCollection
<NavigationProperty
, EdmMember
>(
145 ((ReadOnlyMetadataCollection
<EdmMember
>)this.Members
), Helper
.IsNavigationProperty
);
150 /// Returns just the properties from the collection
151 /// of members on this type
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);
168 #endregion // Properties
171 /// Returns the Reference type pointing to this entity type
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
)
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);
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.
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
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
;
222 navigationProperty
= null;
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
;
242 /// Initializes a new instance of Complex Type with properties from the type.
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
,
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);
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; }
}
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.
289 internal string HashedDescription
295 Interlocked
.CompareExchange(ref _hash
, BuildEntityTypeHash(), null);
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.
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();
321 /// Creates a description of all the metadata relevant to the creation of a proxy type
322 /// for this entity type.
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
)
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();