1 //---------------------------------------------------------------------
2 // <copyright file="TypeUsage.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Metadata.Edm
13 using System.Collections.Generic;
14 using System.Data.Common;
15 using System.Diagnostics;
19 /// Class representing a type information for an item
21 [DebuggerDisplay("EdmType={EdmType}, Facets.Count={Facets.Count}")]
22 public sealed class TypeUsage : MetadataItem
27 /// The constructor for TypeUsage taking in a type
29 /// <param name="edmType">The type which the TypeUsage object describes</param>
30 /// <exception cref="System.ArgumentNullException">Thrown if edmType argument is null</exception>
31 private TypeUsage(EdmType edmType)
32 :base(MetadataFlags.Readonly)
34 EntityUtil.GenericCheckArgumentNull(edmType, "edmType");
38 // I would like to be able to assert that the edmType is ReadOnly, but
39 // because some types are still in loading while the TypeUsage is being created
40 // that won't work. We should consider a way to change this
44 /// The constructor for TypeUsage taking in a type and a collection of facets
46 /// <param name="edmType">The type which the TypeUsage object describes</param>
47 /// <param name="facets">The replacement collection of facets</param>
48 /// <exception cref="System.ArgumentNullException">Thrown if edmType argument is null</exception>
49 private TypeUsage(EdmType edmType, IEnumerable<Facet> facets)
52 MetadataCollection<Facet> facetCollection = new MetadataCollection<Facet>(facets);
53 facetCollection.SetReadOnly();
54 _facets = facetCollection.AsReadOnlyMetadataCollection();
58 #region Factory Methods
60 /// Factory method for creating a TypeUsage with specified EdmType
62 /// <param name="edmType">EdmType for which to create a type usage</param>
63 /// <returns>new TypeUsage instance with default facet values</returns>
64 internal static TypeUsage Create(EdmType edmType)
66 return new TypeUsage(edmType);
70 /// Factory method for creating a TypeUsage with specified EdmType
72 /// <param name="edmType">EdmType for which to create a type usage</param>
73 /// <returns>new TypeUsage instance with default facet values</returns>
74 internal static TypeUsage Create(EdmType edmType, FacetValues values)
76 return new TypeUsage(edmType,
77 GetDefaultFacetDescriptionsAndOverrideFacetValues(edmType, values));
81 /// Factory method for creating a TypeUsage with specified EdmType and facets
83 /// <param name="edmType">EdmType for which to create a type usage</param>
84 /// <param name="facets">facets to be copied into the new TypeUsage</param>
85 /// <returns>new TypeUsage instance</returns>
86 internal static TypeUsage Create(EdmType edmType, IEnumerable<Facet> facets)
88 return new TypeUsage(edmType, facets);
91 internal TypeUsage ShallowCopy(FacetValues facetValues)
93 return TypeUsage.Create(_edmType, OverrideFacetValues(Facets, facetValues));
97 /// Factory method for creating a "readonly" TypeUsage with specified EdmType
99 /// <param name="edmType">An EdmType for which to create a TypeUsage</param>
100 /// <returns>A TypeUsage instance with default facet values for the specified EdmType</returns>
101 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "0#edm")]
102 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")]
103 public static TypeUsage CreateDefaultTypeUsage(EdmType edmType)
105 EntityUtil.CheckArgumentNull<EdmType>(edmType, "edmType");
107 TypeUsage type = TypeUsage.Create(edmType);
112 /// Factory method for creating a string TypeUsage object with the specified facets
114 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
115 /// <param name="isUnicode">Whether the string type is unicode or not</param>
116 /// <param name="isFixedLength">Whether the string type is fixed length or not</param>
117 /// <param name="maxLength">The max length of the string type</param>
118 /// <returns>A TypeUsage object describing a string type with the given facet values</returns>
119 public static TypeUsage CreateStringTypeUsage(PrimitiveType primitiveType,
124 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
126 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.String)
128 throw EntityUtil.NotStringTypeForTypeUsage();
131 ValidateMaxLength(maxLength);
133 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
134 new FacetValues{ MaxLength = maxLength, Unicode = isUnicode, FixedLength = isFixedLength});
140 /// Factory method for creating a string TypeUsage object with the specified facets and
141 /// unbounded MaxLength
143 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
144 /// <param name="isUnicode">Whether the string type is unicode or not</param>
145 /// <param name="isFixedLength">Whether the string type is fixed length or not</param>
146 /// <returns>A TypeUsage object describing a string type with the given facet values
147 /// and unbounded MaxLength</returns>
148 public static TypeUsage CreateStringTypeUsage(PrimitiveType primitiveType,
152 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
154 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.String)
156 throw EntityUtil.NotStringTypeForTypeUsage();
158 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
159 new FacetValues{ MaxLength = TypeUsage.DefaultMaxLengthFacetValue,
160 Unicode = isUnicode, FixedLength = isFixedLength});
167 /// Factory method for creating a Binary TypeUsage object with the specified facets
169 /// <param name="primitiveType">A PrimitiveType for which to construct TypeUsage</param>
170 /// <param name="isFixedLength">Whether the binary type is fixed length or not</param>
171 /// <param name="maxLength">The max length of the binary type</param>
172 /// <returns>A TypeUsage object describing a binary type with the given facet values</returns>
173 public static TypeUsage CreateBinaryTypeUsage(PrimitiveType primitiveType,
177 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
179 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Binary)
181 throw EntityUtil.NotBinaryTypeForTypeUsage();
184 ValidateMaxLength(maxLength);
186 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
187 new FacetValues{MaxLength = maxLength, FixedLength = isFixedLength});
193 /// Factory method for creating a Binary TypeUsage object with the specified facets and
194 /// unbounded MaxLength
196 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
197 /// <param name="isFixedLength">Whether the binary type is fixed length or not</param>
198 /// <returns>A TypeUsage object describing a binary type with the given facet values</returns>
199 public static TypeUsage CreateBinaryTypeUsage(PrimitiveType primitiveType, bool isFixedLength)
201 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
203 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Binary)
205 throw EntityUtil.NotBinaryTypeForTypeUsage();
207 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
208 new FacetValues{MaxLength = TypeUsage.DefaultMaxLengthFacetValue,
209 FixedLength = isFixedLength});
215 /// Factory method for creating a DateTime TypeUsage object with the specified facets
217 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
218 /// <param name="precision">Precision for seconds</param>
219 /// <returns>A TypeUsage object describing a DateTime type with the given facet values</returns>
220 public static TypeUsage CreateDateTimeTypeUsage(PrimitiveType primitiveType,
223 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
225 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.DateTime)
227 throw EntityUtil.NotDateTimeTypeForTypeUsage();
229 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
230 new FacetValues{Precision = precision});
236 /// Factory method for creating a DateTimeOffset TypeUsage object with the specified facets
238 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
239 /// <param name="precision">Precision for seconds</param>
240 /// <returns>A TypeUsage object describing a DateTime type with the given facet values</returns>
241 public static TypeUsage CreateDateTimeOffsetTypeUsage(PrimitiveType primitiveType,
244 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
246 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.DateTimeOffset)
248 throw EntityUtil.NotDateTimeOffsetTypeForTypeUsage();
251 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
252 new FacetValues{ Precision = precision });
258 /// Factory method for creating a Time TypeUsage object with the specified facets
260 /// <param name="primitiveType">A PrimitiveType for which to construct the TypeUsage</param>
261 /// <param name="precision">Precision for seconds</param>
262 /// <returns>A TypeUsage object describing a Time type with the given facet values</returns>
263 public static TypeUsage CreateTimeTypeUsage(PrimitiveType primitiveType,
266 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
268 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Time)
270 throw EntityUtil.NotTimeTypeForTypeUsage();
272 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
273 new FacetValues{ Precision = precision });
281 /// Factory method for creating a Decimal TypeUsage object with the specified facets
283 /// <param name="primitiveType">A PrimitiveType for which to construct type usage</param>
284 /// <param name="precision">The precision of the decimal type</param>
285 /// <param name="scale">The scale of the decimal type</param>
286 /// <returns>A TypeUsage object describing a decimal type with the given facet values</returns>
287 public static TypeUsage CreateDecimalTypeUsage(PrimitiveType primitiveType,
291 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
293 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal)
295 throw EntityUtil.NotDecimalTypeForTypeUsage();
298 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
299 new FacetValues{Precision = precision, Scale = scale });
305 /// Factory method for creating a Decimal TypeUsage object with unbounded precision and scale
307 /// <param name="primitiveType">The PrimitiveType for which to construct type usage</param>
308 /// <returns>A TypeUsage object describing a decimal type with unbounded precision and scale</returns>
309 public static TypeUsage CreateDecimalTypeUsage(PrimitiveType primitiveType)
311 EntityUtil.CheckArgumentNull<PrimitiveType>(primitiveType, "primitiveType");
313 if (primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal)
315 throw EntityUtil.NotDecimalTypeForTypeUsage();
317 TypeUsage typeUsage = TypeUsage.Create(primitiveType,
318 new FacetValues{ Precision = TypeUsage.DefaultPrecisionFacetValue, Scale = TypeUsage.DefaultScaleFacetValue });
325 private TypeUsage _modelTypeUsage;
326 private readonly EdmType _edmType;
327 private ReadOnlyMetadataCollection<Facet> _facets;
328 private string _identity;
331 /// Set of facets that should be included in identity for TypeUsage
333 /// <remarks>keep this sorted for binary searching</remarks>
334 private static readonly string[] s_identityFacets = new string[] {
335 DbProviderManifest.DefaultValueFacetName,
336 DbProviderManifest.FixedLengthFacetName,
337 DbProviderManifest.MaxLengthFacetName,
338 DbProviderManifest.NullableFacetName,
339 DbProviderManifest.PrecisionFacetName,
340 DbProviderManifest.ScaleFacetName,
341 DbProviderManifest.UnicodeFacetName,
342 DbProviderManifest.SridFacetName,
345 internal static readonly EdmConstants.Unbounded DefaultMaxLengthFacetValue = EdmConstants.UnboundedValue;
346 internal static readonly EdmConstants.Unbounded DefaultPrecisionFacetValue = EdmConstants.UnboundedValue;
347 internal static readonly EdmConstants.Unbounded DefaultScaleFacetValue = EdmConstants.UnboundedValue;
348 internal static readonly bool DefaultUnicodeFacetValue = true;
349 internal static readonly bool DefaultFixedLengthFacetValue = false;
350 internal static readonly byte? DefaultDateTimePrecisionFacetValue = null;
356 /// Returns the kind of the type
358 public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.TypeUsage; } }
361 /// Gets the type that this TypeUsage describes
363 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
364 [MetadataProperty(BuiltInTypeKind.EdmType, false)]
365 public EdmType EdmType
374 /// Gets the list of facets for the type in this TypeUsage
376 [MetadataProperty(BuiltInTypeKind.Facet, true)]
377 public ReadOnlyMetadataCollection<Facet> Facets
383 MetadataCollection<Facet> facets = new MetadataCollection<Facet>(GetFacets());
384 // we never modify the collection so we can set it readonly from the start
385 facets.SetReadOnly();
386 System.Threading.Interlocked.CompareExchange(ref _facets, facets.AsReadOnlyMetadataCollection(), null);
395 /// Returns a Model type usage for a provider type
397 /// <returns>model (CSpace) type usage</returns>
398 internal TypeUsage GetModelTypeUsage()
400 if (_modelTypeUsage == null)
402 EdmType edmType = this.EdmType;
404 // If the edm type is already a cspace type, return the same type
405 if (edmType.DataSpace == DataSpace.CSpace || edmType.DataSpace == DataSpace.OSpace)
411 if (Helper.IsRowType(edmType))
413 RowType sspaceRowType = (RowType)edmType;
414 EdmProperty[] properties = new EdmProperty[sspaceRowType.Properties.Count];
415 for (int i = 0; i < properties.Length; i++)
417 EdmProperty sspaceProperty = sspaceRowType.Properties[i];
418 TypeUsage newTypeUsage = sspaceProperty.TypeUsage.GetModelTypeUsage();
419 properties[i] = new EdmProperty(sspaceProperty.Name, newTypeUsage);
421 RowType edmRowType = new RowType(properties, sspaceRowType.InitializerMetadata);
422 result = TypeUsage.Create(edmRowType, this.Facets);
424 else if (Helper.IsCollectionType(edmType))
426 CollectionType sspaceCollectionType = ((CollectionType)edmType);
427 TypeUsage newTypeUsage = sspaceCollectionType.TypeUsage.GetModelTypeUsage();
428 result = TypeUsage.Create(new CollectionType(newTypeUsage), this.Facets);
430 else if (Helper.IsRefType(edmType))
432 System.Diagnostics.Debug.Assert(((RefType)edmType).ElementType.DataSpace == DataSpace.CSpace);
435 else if (Helper.IsPrimitiveType(edmType))
437 result = ((PrimitiveType)edmType).ProviderManifest.GetEdmType(this);
441 throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.Mapping_ProviderReturnsNullType(this.ToString()));
444 if (!TypeSemantics.IsNullable(this))
446 result = TypeUsage.Create(result.EdmType,
447 OverrideFacetValues(result.Facets,
448 new FacetValues{ Nullable = false }));
451 else if (Helper.IsEntityTypeBase(edmType) || Helper.IsComplexType(edmType))
457 System.Diagnostics.Debug.Assert(false, "Unexpected type found in entity data reader");
460 System.Threading.Interlocked.CompareExchange(ref _modelTypeUsage, result, null);
462 return _modelTypeUsage;
466 /// check if "this" is a subtype of the specified TypeUsage
468 /// <param name="typeUsage">The typeUsage to be checked</param>
469 /// <returns>true if this typeUsage is a subtype of the specified typeUsage</returns>
470 public bool IsSubtypeOf(TypeUsage typeUsage)
472 if (EdmType == null || typeUsage == null)
477 return EdmType.IsSubtypeOf(typeUsage.EdmType);
480 private IEnumerable<Facet> GetFacets()
482 foreach (FacetDescription facetDescription in _edmType.GetAssociatedFacetDescriptions())
484 yield return facetDescription.DefaultValueFacet;
488 internal override void SetReadOnly()
490 Debug.Fail("TypeUsage.SetReadOnly should not need to ever be called");
495 /// returns the identity of the type usage
497 internal override String Identity
501 if (this.Facets.Count == 0)
503 return this.EdmType.Identity;
506 if (this._identity == null)
508 StringBuilder builder = new StringBuilder(128);
509 BuildIdentity(builder);
510 string identity = builder.ToString();
511 System.Threading.Interlocked.CompareExchange(ref _identity, identity, null);
513 return this._identity;
517 private static IEnumerable<Facet> GetDefaultFacetDescriptionsAndOverrideFacetValues(EdmType type, FacetValues values)
519 return OverrideFacetValues(type.GetAssociatedFacetDescriptions(),
521 fd => fd.DefaultValueFacet,
526 private static IEnumerable<Facet> OverrideFacetValues(IEnumerable<Facet> facets, FacetValues values)
528 return OverrideFacetValues(facets,
535 private static IEnumerable<Facet> OverrideFacetValues<T>(IEnumerable<T> facetThings,
536 Func<T, FacetDescription> getDescription,
537 Func<T, Facet> getFacet,
540 // yield all the non custom values
541 foreach (var thing in facetThings)
543 FacetDescription description = getDescription(thing);
545 if (!description.IsConstant && values.TryGetFacet(description, out facet))
551 yield return getFacet(thing);
557 internal override void BuildIdentity(StringBuilder builder)
559 // if we've already cached the identity, simply append it
560 if (null != _identity)
562 builder.Append(_identity);
566 builder.Append(this.EdmType.Identity);
570 for (int j = 0; j < this.Facets.Count; j++)
572 Facet facet = this.Facets[j];
574 if (0 <= Array.BinarySearch(s_identityFacets, facet.Name, StringComparer.Ordinal))
576 if (first) { first = false; }
577 else { builder.Append(","); }
579 builder.Append(facet.Name);
581 // If the facet is present, add its value to the identity
582 // We only include built-in system facets for the identity
583 builder.Append(facet.Value ?? String.Empty);
591 public override string ToString()
593 return EdmType.ToString();
597 /// EdmEquals override verifying the equivalence of all facets. Two facets are considered
598 /// equal if they have the same name and the same value (Object.Equals)
600 /// <param name="item"></param>
601 /// <returns></returns>
602 internal override bool EdmEquals(MetadataItem item)
604 // short-circuit if this and other are reference equivalent
605 if (Object.ReferenceEquals(this, item)) { return true; }
607 // check type of item
608 if (null == item || BuiltInTypeKind.TypeUsage != item.BuiltInTypeKind) { return false; }
609 TypeUsage other = (TypeUsage)item;
611 // verify edm types are equivalent
612 if (!this.EdmType.EdmEquals(other.EdmType)) { return false; }
614 // if both usages have default facets, no need to compare
615 if (null == this._facets && null == other._facets) { return true; }
617 // initialize facets and compare
618 if (this.Facets.Count != other.Facets.Count) { return false; }
620 foreach (Facet thisFacet in this.Facets)
623 if (!other.Facets.TryGetValue(thisFacet.Name, false, out otherFacet))
625 // other type usage doesn't have the same facets as this type usage
629 // check that the facet values are the same
630 if (!Object.Equals(thisFacet.Value, otherFacet.Value))
639 private static void ValidateMaxLength(int maxLength)
643 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.InvalidMaxLengthSize, "maxLength");