//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- namespace System.Data.EntityModel.SchemaObjectModel { using System; using System.Collections.Generic; using System.Data.Common; using System.Data.Common.Utils; using System.Data.Entity; using System.Data.Metadata.Edm; using System.Data.Objects.DataClasses; using System.Diagnostics; using System.Globalization; using System.IO; using System.Xml; using System.Xml.Schema; /// /// class representing the Schema element in the schema /// [System.Diagnostics.DebuggerDisplay("Namespace={Namespace}, PublicKeyToken={PublicKeyToken}, Version={Version}")] internal class Schema : SchemaElement { #region Instance Fields private const int RootDepth = 2; // if adding properties also add to InitializeObject()! private List _errors = new List(); // We need to keep track of functions seperately, since we can't deduce the strong name of the function, // until we have resolved the parameter names. Hence we keep track of functions seperately and add them // to the schema types list, in the validate phase private List _functions = null; private AliasResolver _aliasResolver = null; private string _location = null; private string _alias = null; protected string _namespaceName = null; private string _schemaXmlNamespace = null; private List _schemaTypes = null; private int _depth = 0; // recursion depth in Parse used by *Handlers to know which hander set to set private double _schemaVersion = XmlConstants.UndefinedVersion; private SchemaManager _schemaManager; private bool? _useStrongSpatialTypes; #endregion #region Static Fields private static IList _emptyPathList = new List(0).AsReadOnly(); #endregion #region Public Methods public Schema(SchemaManager schemaManager) : base(null) { Debug.Assert(schemaManager != null, "SchemaManager parameter should never be null"); _schemaManager = schemaManager; _errors = new List(); } internal IList Resolve() { ResolveTopLevelNames(); if (_errors.Count != 0) { return ResetErrors(); } ResolveSecondLevelNames(); return ResetErrors(); } internal IList ValidateSchema() { Validate(); return ResetErrors(); } internal void AddError(EdmSchemaError error) { _errors.Add(error); } /// /// Populate the schema object from a schema /// /// TextReader containing the schema xml definition /// Uri containing path to a schema file (may be null) /// list of errors internal IList Parse(XmlReader sourceReader, string sourceLocation) { // We don't Assert (sourceReader != null) here any more because third-party // providers that extend XmlEnabledProvidermanifest could hit this code. The // following code eventually detects the anomaly and a ProviderIncompatible // exception is thrown (which is the right thing to do in such cases). XmlReader wrappedReader = null; try { // user specified a stream to read from, read from it. // The Uri is just used to identify the stream in errors. XmlReaderSettings readerSettings = CreateXmlReaderSettings(); wrappedReader = XmlReader.Create(sourceReader, readerSettings); return InternalParse(wrappedReader, sourceLocation); } catch (System.IO.IOException ex) { AddError(ErrorCode.IOException, EdmSchemaErrorSeverity.Error, sourceReader, ex); } // do not close the reader here (SQLBUDT 522950) return ResetErrors(); } /// /// Populate the schema object from a schema /// /// TextReader containing the schema xml definition /// Uri containing path to a schema file (may be null) /// list of errors private IList InternalParse(XmlReader sourceReader, string sourceLocation) { Debug.Assert(sourceReader != null, "sourceReader parameter is null"); // these need to be set before any calls to AddError are made. Schema = this; Location = sourceLocation; try { // to make life simpler, we skip down to the first/root element, unless we're // already there if (sourceReader.NodeType != XmlNodeType.Element) { while (sourceReader.Read() && sourceReader.NodeType != XmlNodeType.Element) { } } GetPositionInfo(sourceReader); List expectedNamespaces = SomSchemaSetHelper.GetPrimarySchemaNamespaces(DataModel); // the root element needs to be either TDL or Schema in our namespace if (sourceReader.EOF) { if (sourceLocation != null) { AddError(ErrorCode.EmptyFile, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.EmptyFile(sourceLocation)); } else { AddError(ErrorCode.EmptyFile, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.EmptySchemaTextReader); } } else if (!expectedNamespaces.Contains(sourceReader.NamespaceURI)) { Func messageFormat = Strings.UnexpectedRootElement; if (string.IsNullOrEmpty(sourceReader.NamespaceURI)) messageFormat = Strings.UnexpectedRootElementNoNamespace; String expectedNamespacesString = Helper.GetCommaDelimitedString(expectedNamespaces); AddError(ErrorCode.UnexpectedXmlElement, EdmSchemaErrorSeverity.Error, messageFormat(sourceReader.NamespaceURI, sourceReader.LocalName, expectedNamespacesString)); } else { this.SchemaXmlNamespace = sourceReader.NamespaceURI; if (DataModel == SchemaDataModelOption.EntityDataModel) { if (this.SchemaXmlNamespace == XmlConstants.ModelNamespace_1) { SchemaVersion = XmlConstants.EdmVersionForV1; } else if (this.SchemaXmlNamespace == XmlConstants.ModelNamespace_1_1) { SchemaVersion = XmlConstants.EdmVersionForV1_1; } else if (this.SchemaXmlNamespace == XmlConstants.ModelNamespace_2) { SchemaVersion = XmlConstants.EdmVersionForV2; } else { Debug.Assert(this.SchemaXmlNamespace == XmlConstants.ModelNamespace_3, "Unknown namespace in CSDL"); SchemaVersion = XmlConstants.EdmVersionForV3; } } else if (DataModel == SchemaDataModelOption.ProviderDataModel) { if (this.SchemaXmlNamespace == XmlConstants.TargetNamespace_1) { SchemaVersion = XmlConstants.StoreVersionForV1; } else if (this.SchemaXmlNamespace == XmlConstants.TargetNamespace_2) { SchemaVersion = XmlConstants.StoreVersionForV2; } else { Debug.Assert(this.SchemaXmlNamespace == XmlConstants.TargetNamespace_3, "Unknown namespace in SSDL"); SchemaVersion = XmlConstants.StoreVersionForV3; } } switch (sourceReader.LocalName) { case "Schema": case "ProviderManifest": HandleTopLevelSchemaElement(sourceReader); // this forces the reader to look beyond this top // level element, and complain if there is another one. sourceReader.Read(); break; default: AddError(ErrorCode.UnexpectedXmlElement, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.UnexpectedRootElement(sourceReader.NamespaceURI, sourceReader.LocalName, SchemaXmlNamespace)); break; } } } catch (InvalidOperationException ex) { AddError(ErrorCode.InternalError, EdmSchemaErrorSeverity.Error, ex.Message); } catch (System.UnauthorizedAccessException ex) { AddError(ErrorCode.UnauthorizedAccessException, EdmSchemaErrorSeverity.Error, sourceReader, ex); } catch (System.IO.IOException ex) { AddError(ErrorCode.IOException, EdmSchemaErrorSeverity.Error, sourceReader, ex); } catch (System.Security.SecurityException ex) { AddError(ErrorCode.SecurityError, EdmSchemaErrorSeverity.Error, sourceReader, ex); } catch (XmlException ex) { AddError(ErrorCode.XmlError, EdmSchemaErrorSeverity.Error, sourceReader, ex); } return ResetErrors(); } internal static XmlReaderSettings CreateEdmStandardXmlReaderSettings() { XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.CheckCharacters = true; readerSettings.CloseInput = false; readerSettings.IgnoreWhitespace = true; readerSettings.ConformanceLevel = ConformanceLevel.Auto; readerSettings.IgnoreComments = true; readerSettings.IgnoreProcessingInstructions = true; readerSettings.DtdProcessing = DtdProcessing.Prohibit; // remove flags // the ProcessInlineSchema, and ProcessSchemaLocation flags must be removed for the same // xsd schema to be used on multiple threads readerSettings.ValidationFlags &= ~System.Xml.Schema.XmlSchemaValidationFlags.ProcessIdentityConstraints; readerSettings.ValidationFlags &= ~System.Xml.Schema.XmlSchemaValidationFlags.ProcessSchemaLocation; readerSettings.ValidationFlags &= ~System.Xml.Schema.XmlSchemaValidationFlags.ProcessInlineSchema; return readerSettings; } private XmlReaderSettings CreateXmlReaderSettings() { XmlReaderSettings readerSettings = CreateEdmStandardXmlReaderSettings(); // add flags readerSettings.ValidationFlags |= System.Xml.Schema.XmlSchemaValidationFlags.ReportValidationWarnings; readerSettings.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(OnSchemaValidationEvent); readerSettings.ValidationType = ValidationType.Schema; XmlSchemaSet schemaSet = SomSchemaSetHelper.GetSchemaSet(DataModel); // Do not use readerSetting.Schemas.Add(schemaSet) // you must use the line below for this to work in // a multithread environment readerSettings.Schemas = schemaSet; return readerSettings; } /// /// Called by the validating reader when the schema is xsd invalid /// /// the validating reader /// information about the validation error internal void OnSchemaValidationEvent(object sender, System.Xml.Schema.ValidationEventArgs e) { Debug.Assert(e != null); XmlReader reader = sender as XmlReader; if (reader != null && !IsValidateableXmlNamespace(reader.NamespaceURI, reader.NodeType == XmlNodeType.Attribute)) { //For V1 Schemas, we never returned errors for elements in custom namespaces. //But the behavior is not totally correct since the error might have occured inside a known namespace //even though the element that the reader pointing to is in a custom namespace. But if we fix that, it would //cause lot of breaking changes for V1 customers since we can not change the xsd for them. //For attributes, we can ignore the errors always since attributes are unordered and custom attributes should always be allowed. if ((this.SchemaVersion == XmlConstants.EdmVersionForV1) || (this.SchemaVersion == XmlConstants.EdmVersionForV1_1)) { return; } // For V2 Schemas that have custom namespaces, the only thing we would not catch are warnings. //We also need to ignore any errors reported on custom namespace since they would become annotations. Debug.Assert(this.SchemaVersion >= XmlConstants.EdmVersionForV2 || SchemaVersion == XmlConstants.UndefinedVersion, "Have you added a new Edm Version?"); if ( (reader.NodeType == XmlNodeType.Attribute) || (e.Severity == System.Xml.Schema.XmlSeverityType.Warning)) { return; } } //Ignore the warnings for attributes in V2 since we would see warnings for undeclared attributes in empty namespace //that are on elements in custom namespace. For undeclared attributes in known namespace, we would see errors. if ((this.SchemaVersion >= XmlConstants.EdmVersionForV2) && (reader.NodeType == XmlNodeType.Attribute) && (e.Severity == System.Xml.Schema.XmlSeverityType.Warning)) { return; } EdmSchemaErrorSeverity severity = EdmSchemaErrorSeverity.Error; if (e.Severity == System.Xml.Schema.XmlSeverityType.Warning) severity = EdmSchemaErrorSeverity.Warning; AddError(ErrorCode.XmlError, severity, e.Exception.LineNumber, e.Exception.LinePosition, e.Message); } public bool IsParseableXmlNamespace(string xmlNamespaceUri, bool isAttribute) { if (string.IsNullOrEmpty(xmlNamespaceUri) && isAttribute) { // we own the empty namespace for attributes return true; } if (_parseableXmlNamespaces == null) { _parseableXmlNamespaces = new HashSet(); foreach (var schemaResource in XmlSchemaResource.GetMetadataSchemaResourceMap(this.SchemaVersion).Values) { _parseableXmlNamespaces.Add(schemaResource.NamespaceUri); } } return _parseableXmlNamespaces.Contains(xmlNamespaceUri); } HashSet _validatableXmlNamespaces; HashSet _parseableXmlNamespaces; public bool IsValidateableXmlNamespace(string xmlNamespaceUri, bool isAttribute) { if (string.IsNullOrEmpty(xmlNamespaceUri) && isAttribute) { // we own the empty namespace for attributes return true; } if(_validatableXmlNamespaces == null) { HashSet validatableXmlNamespaces = new HashSet(); double schemaVersion = SchemaVersion == XmlConstants.UndefinedVersion ? XmlConstants.SchemaVersionLatest : SchemaVersion; foreach (var schemaResource in XmlSchemaResource.GetMetadataSchemaResourceMap(schemaVersion).Values) { AddAllSchemaResourceNamespaceNames(validatableXmlNamespaces, schemaResource); } if (SchemaVersion == XmlConstants.UndefinedVersion) { // we are getting called before the version is set return validatableXmlNamespaces.Contains(xmlNamespaceUri); } _validatableXmlNamespaces = validatableXmlNamespaces; } return _validatableXmlNamespaces.Contains(xmlNamespaceUri); } private static void AddAllSchemaResourceNamespaceNames(HashSet hashSet, XmlSchemaResource schemaResource) { hashSet.Add(schemaResource.NamespaceUri); foreach(var import in schemaResource.ImportedSchemas) { AddAllSchemaResourceNamespaceNames(hashSet, import); } } internal override void ResolveTopLevelNames() { base.ResolveTopLevelNames(); // Resolve all the referenced namespace to make sure that this namespace is valid AliasResolver.ResolveNamespaces(); foreach (SchemaElement element in SchemaTypes) { element.ResolveTopLevelNames(); } foreach (Function function in Functions) { function.ResolveTopLevelNames(); } } internal override void ResolveSecondLevelNames() { base.ResolveSecondLevelNames(); foreach (SchemaElement element in SchemaTypes) { element.ResolveSecondLevelNames(); } foreach (Function function in Functions) { function.ResolveSecondLevelNames(); } } /// /// Vaidate the schema. /// /// list of errors internal override void Validate() { if (String.IsNullOrEmpty(Namespace)) { AddError(ErrorCode.MissingNamespaceAttribute, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.MissingNamespaceAttribute); return; } // Also check for alias to be system namespace if (!String.IsNullOrEmpty(Alias) && EdmItemCollection.IsSystemNamespace(ProviderManifest, Alias)) { AddError(ErrorCode.CannotUseSystemNamespaceAsAlias, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.CannotUseSystemNamespaceAsAlias(Alias)); } // Check whether the schema namespace is a system namespace. We set the provider manifest to edm provider manifest // if we need to check for system namespace. Otherwise, it will be set to null (if we are loading edm provider manifest) if (ProviderManifest != null && EdmItemCollection.IsSystemNamespace(ProviderManifest, Namespace)) { AddError(ErrorCode.SystemNamespace, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.SystemNamespaceEncountered(Namespace)); } foreach (SchemaElement schemaType in this.SchemaTypes) { schemaType.Validate(); } foreach (Function function in this.Functions) { AddFunctionType(function); function.Validate(); } } #endregion #region Public Properties /// /// The namespaceUri of the winfs xml namespace /// internal string SchemaXmlNamespace { get { return _schemaXmlNamespace; } private set { _schemaXmlNamespace = value; } } internal DbProviderManifest ProviderManifest { get { return _schemaManager.GetProviderManifest((string message, ErrorCode code, EdmSchemaErrorSeverity severity)=>AddError(code, severity, message)); } } /// /// Version of the EDM that this schema represents. /// internal double SchemaVersion { get { return _schemaVersion; } set { _schemaVersion = value; } } /// /// Alias for the schema (null if none) /// internal virtual string Alias { get { return _alias; } private set { _alias = value; } } /// /// Namespace of the schema /// internal virtual string Namespace { get { return _namespaceName; } private set { _namespaceName = value; } } // ISSUE: jthunter-03/14/05 - The Sync "schemas" don't follow the ".Store" assembly // naming convention but need to have the right StoreNamespace reported. // private static readonly string[] ClientNamespaceOfSchemasMissingStoreSuffix = { "System.Storage.Sync.Utility", "System.Storage.Sync.Services" }; /// /// Uri containing the file that defines the schema /// internal string Location { get { return _location; } private set { _location = value; } } private MetadataProperty _schemaSourceProperty; internal MetadataProperty SchemaSource { get { if (_schemaSourceProperty == null) { // create the System MetadataProperty for the SchemaSource _schemaSourceProperty = new MetadataProperty("SchemaSource", EdmProviderManifest.Instance.GetPrimitiveType(PrimitiveTypeKind.String), false, // IsCollection _location != null ? _location : string.Empty); } return _schemaSourceProperty; } } /// /// List of all types defined in the schema /// internal List SchemaTypes { get { if (_schemaTypes == null) { _schemaTypes = new List(); } return _schemaTypes; } } /// /// Fully qualified name of the schema (same as the namespace name) /// public override string FQName { get { return Namespace; } } private List Functions { get { if (_functions == null) { _functions = new List(); } return _functions; } } #endregion #region Protected Properties protected override bool HandleElement(XmlReader reader) { if (base.HandleElement(reader)) { return true; } else if (CanHandleElement(reader, XmlConstants.EntityType)) { HandleEntityTypeElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.ComplexType)) { HandleInlineTypeElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.Association)) { HandleAssociationElement(reader); return true; } // These elements are only supported in EntityDataModel if (DataModel == SchemaDataModelOption.EntityDataModel) { if (CanHandleElement(reader, XmlConstants.Using)) { HandleUsingElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.Function)) { HandleModelFunctionElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.EnumType)) { HandleEnumTypeElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.ValueTerm)) { // EF does not support this EDM 3.0 element, so ignore it. SkipElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.Annotations)) { // EF does not support this EDM 3.0 element, so ignore it. SkipElement(reader); return true; } } if (DataModel == SchemaDataModelOption.EntityDataModel || DataModel == SchemaDataModelOption.ProviderDataModel) { if (CanHandleElement(reader, XmlConstants.EntityContainer)) { HandleEntityContainerTypeElement(reader); return true; } else if (DataModel == SchemaDataModelOption.ProviderDataModel) { if (CanHandleElement(reader, XmlConstants.Function)) { HandleFunctionElement(reader); return true; } } } else { Debug.Assert(DataModel == SchemaDataModelOption.ProviderManifestModel, "Did you add a new option?"); if (CanHandleElement(reader, XmlConstants.TypesElement)) { SkipThroughElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.FunctionsElement)) { SkipThroughElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.Function)) { HandleFunctionElement(reader); return true; } else if (CanHandleElement(reader, XmlConstants.TypeElement)) { HandleTypeInformationElement(reader); return true; } } return false; } protected override bool ProhibitAttribute(string namespaceUri, string localName) { if (base.ProhibitAttribute(namespaceUri, localName)) { return true; } if (namespaceUri == null && localName == XmlConstants.Name) { return false; } return false; } protected override bool HandleAttribute(XmlReader reader) { Debug.Assert(_depth > 0); if (_depth == 1) { return false; } else { if (base.HandleAttribute(reader)) { return true; } else if (CanHandleAttribute(reader, XmlConstants.Alias)) { HandleAliasAttribute(reader); return true; } else if (CanHandleAttribute(reader, XmlConstants.Namespace)) { HandleNamespaceAttribute(reader); return true; } else if (CanHandleAttribute(reader, XmlConstants.Provider)) { HandleProviderAttribute(reader); return true; } else if (CanHandleAttribute(reader, XmlConstants.ProviderManifestToken)) { HandleProviderManifestTokenAttribute(reader); return true; } else if (reader.NamespaceURI == XmlConstants.AnnotationNamespace && reader.LocalName == XmlConstants.UseStrongSpatialTypes) { HandleUseStrongSpatialTypesAnnotation(reader); return true; } } return false; } #endregion #region Internal Methods /// /// Called when all attributes for the schema element have been handled /// protected override void HandleAttributesComplete() { if (_depth < RootDepth) { return; } else if (_depth == RootDepth) { // only call when done with the root element _schemaManager.EnsurePrimitiveSchemaIsLoaded(this.SchemaVersion); } base.HandleAttributesComplete(); } protected override void SkipThroughElement(XmlReader reader) { try { _depth++; base.SkipThroughElement(reader); } finally { _depth--; } } /// /// Look up a fully qualified type name reference. /// /// element containing the reference /// the fully qualified type name /// the referenced schema type /// false if there was an error internal bool ResolveTypeName(SchemaElement usingElement, string typeName, out SchemaType type) { Debug.Assert(usingElement != null); Debug.Assert(typeName != null); type = null; // get the schema(s) that match the namespace/alias string actualQualification; string unqualifiedTypeName; Utils.ExtractNamespaceAndName(DataModel, typeName, out actualQualification, out unqualifiedTypeName); string definingQualification = actualQualification; if (definingQualification == null) { definingQualification = this.ProviderManifest == null ? this._namespaceName : this.ProviderManifest.NamespaceName; } string namespaceName; // First check if there is an alias defined by this name. For primitive type namespace, we do not need to resolve // any alias, since that's a reserved keyword and we don't allow alias with that name if (actualQualification == null || !AliasResolver.TryResolveAlias(definingQualification, out namespaceName)) { namespaceName = definingQualification; } // Resolve the type name if (!SchemaManager.TryResolveType(namespaceName, unqualifiedTypeName, out type)) { // it must be an undefined type. if (actualQualification == null) { // Every type except the primitive type must be qualified usingElement.AddError(ErrorCode.NotInNamespace, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.NotNamespaceQualified(typeName)); } else if (!SchemaManager.IsValidNamespaceName(namespaceName)) { usingElement.AddError(ErrorCode.BadNamespace, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.BadNamespaceOrAlias(actualQualification)); } else { // if the type name was alias qualified if (namespaceName != definingQualification) { usingElement.AddError(ErrorCode.NotInNamespace, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.NotInNamespaceAlias(unqualifiedTypeName, namespaceName, definingQualification)); } else { usingElement.AddError(ErrorCode.NotInNamespace, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.NotInNamespaceNoAlias(unqualifiedTypeName, namespaceName)); } } return false; } // For ssdl and provider manifest, make sure that the type is present in this schema or primitive schema else if (this.DataModel != SchemaDataModelOption.EntityDataModel && type.Schema != this && type.Schema != this.SchemaManager.PrimitiveSchema) { Debug.Assert(type.Namespace != this.Namespace, "Using element is not allowed in the schema of ssdl and provider manifest"); usingElement.AddError(ErrorCode.InvalidNamespaceOrAliasSpecified, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.InvalidNamespaceOrAliasSpecified(actualQualification)); return false; } return true; } #endregion #region Internal Properties /// /// List containing the current schema and all referenced schemas. Used for alias and namespace lookup. /// internal AliasResolver AliasResolver { get { if (_aliasResolver == null) { _aliasResolver = new AliasResolver(this); } return _aliasResolver; } } /// /// The schema data model /// internal SchemaDataModelOption DataModel { get { return SchemaManager.DataModel; } } /// /// The schema data model /// internal SchemaManager SchemaManager { get { return _schemaManager; } } internal bool UseStrongSpatialTypes { get { return _useStrongSpatialTypes ?? true; } } #endregion #region Private Methods /// /// Handler for the Namespace attribute /// /// xml reader currently positioned at Namespace attribute private void HandleNamespaceAttribute(XmlReader reader) { Debug.Assert(reader != null); ReturnValue returnValue = HandleDottedNameAttribute(reader, Namespace, null); if (!returnValue.Succeeded) return; Namespace = returnValue.Value; } /// /// Handler for the Alias attribute /// /// xml reader currently positioned at Alias attribute private void HandleAliasAttribute(XmlReader reader) { Debug.Assert(reader != null); Alias = HandleUndottedNameAttribute(reader, Alias); } /// /// Handler for the Provider attribute /// /// xml reader currently positioned at Provider attribute private void HandleProviderAttribute(XmlReader reader) { Debug.Assert(reader != null); string provider = reader.Value; _schemaManager.ProviderNotification(provider, (string message, ErrorCode code, EdmSchemaErrorSeverity severity) => AddError(code, severity, reader, message)); } /// /// Handler for the ProviderManifestToken attribute /// /// xml reader currently positioned at ProviderManifestToken attribute private void HandleProviderManifestTokenAttribute(XmlReader reader) { Debug.Assert(reader != null); string providerManifestToken = reader.Value; _schemaManager.ProviderManifestTokenNotification(providerManifestToken, (string message, ErrorCode code, EdmSchemaErrorSeverity severity) => AddError(code, severity, reader, message)); } private void HandleUseStrongSpatialTypesAnnotation(XmlReader reader) { Debug.Assert(reader != null); bool isStrict = false; if (this.HandleBoolAttribute(reader, ref isStrict)) { this._useStrongSpatialTypes = isStrict; } } /// /// Handler for the using element /// /// private void HandleUsingElement(XmlReader reader) { UsingElement referencedNamespace = new UsingElement(this); referencedNamespace.Parse(reader); AliasResolver.Add(referencedNamespace); } /// /// Handler for the EnumType element. /// /// Source xml reader currently positioned on the EnumType element. private void HandleEnumTypeElement(XmlReader reader) { Debug.Assert(reader != null); SchemaEnumType enumType = new SchemaEnumType(this); enumType.Parse(reader); TryAddType(enumType, doNotAddErrorForEmptyName: true); } /// /// Handler for the top level element /// /// xml reader currently positioned at top level element private void HandleTopLevelSchemaElement(XmlReader reader) { Debug.Assert(reader != null); try { _depth += RootDepth; Parse(reader); } finally { _depth -= RootDepth; } } /// /// Handler for the EntityType element /// /// xml reader currently positioned at EntityType element private void HandleEntityTypeElement(XmlReader reader) { Debug.Assert(reader != null); SchemaEntityType itemType = new SchemaEntityType(this); itemType.Parse(reader); TryAddType(itemType, true/*doNotAddErrorForEmptyName*/); } /// /// Handler for the TypeInformation element /// /// xml reader currently positioned at EntityType element private void HandleTypeInformationElement(XmlReader reader) { Debug.Assert(reader != null); TypeElement type = new TypeElement(this); type.Parse(reader); TryAddType(type, true/*doNotAddErrorForEmptyName*/); } /// /// Handler for the Function element /// /// xml reader currently positioned at EntityType element private void HandleFunctionElement(XmlReader reader) { Debug.Assert(reader != null); Function function = new Function(this); function.Parse(reader); this.Functions.Add(function); } private void HandleModelFunctionElement(XmlReader reader) { Debug.Assert(reader != null); ModelFunction function = new ModelFunction(this); function.Parse(reader); this.Functions.Add(function); } /// /// Handler for the Association element /// /// xml reader currently positioned at Association element private void HandleAssociationElement(XmlReader reader) { Debug.Assert(reader != null); Relationship relationship = new Relationship(this, RelationshipKind.Association); relationship.Parse(reader); TryAddType(relationship, true/*doNotAddErrorForEmptyName*/); } /// /// Handler for the InlineType element /// /// xml reader currently positioned at InlineType element private void HandleInlineTypeElement(XmlReader reader) { Debug.Assert(reader != null); SchemaComplexType complexType = new SchemaComplexType(this); complexType.Parse(reader); TryAddType(complexType, true/*doNotAddErrorForEmptyName*/); } /// /// Handler for the EntityContainer element /// /// xml reader currently positioned at EntityContainer element private void HandleEntityContainerTypeElement(XmlReader reader) { Debug.Assert(reader != null); EntityContainer type = new EntityContainer(this); type.Parse(reader); TryAddContainer(type, true/*doNotAddErrorForEmptyName*/); } /// /// reset the error collection /// /// old error list private List ResetErrors() { List errors = _errors; _errors = new List(); return errors; } protected void TryAddType(SchemaType schemaType, bool doNotAddErrorForEmptyName) { this.SchemaManager.SchemaTypes.Add(schemaType, doNotAddErrorForEmptyName, Strings.TypeNameAlreadyDefinedDuplicate); this.SchemaTypes.Add(schemaType); } protected void TryAddContainer(SchemaType schemaType, bool doNotAddErrorForEmptyName) { this.SchemaManager.SchemaTypes.Add(schemaType, doNotAddErrorForEmptyName, Strings.EntityContainerAlreadyExists); this.SchemaTypes.Add(schemaType); } protected void AddFunctionType(Function function) { string space = DataModel == SchemaDataModelOption.EntityDataModel ? "Conceptual" : "Storage"; if (this.SchemaVersion >= XmlConstants.EdmVersionForV2 && this.SchemaManager.SchemaTypes.ContainsKey(function.FQName)) { function.AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.AmbiguousFunctionAndType(function.FQName, space)); } else { AddErrorKind error = this.SchemaManager.SchemaTypes.TryAdd(function); Debug.Assert(error != AddErrorKind.MissingNameError, "Function identity can never be null while adding global functions"); if (error != AddErrorKind.Succeeded) { function.AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, System.Data.Entity.Strings.AmbiguousFunctionOverload(function.FQName, space)); } else { this.SchemaTypes.Add(function); } } } #endregion #region Private Properties #endregion private static class SomSchemaSetHelper { private static Memoizer _cachedSchemaSets = new Memoizer(ComputeSchemaSet, EqualityComparer.Default); internal static List GetPrimarySchemaNamespaces(SchemaDataModelOption dataModel) { List namespaces = new List(); if (dataModel == SchemaDataModelOption.EntityDataModel) { namespaces.Add(XmlConstants.ModelNamespace_1); namespaces.Add(XmlConstants.ModelNamespace_1_1); namespaces.Add(XmlConstants.ModelNamespace_2); namespaces.Add(XmlConstants.ModelNamespace_3); } else if (dataModel == SchemaDataModelOption.ProviderDataModel) { namespaces.Add(XmlConstants.TargetNamespace_1); namespaces.Add(XmlConstants.TargetNamespace_2); namespaces.Add(XmlConstants.TargetNamespace_3); } else { Debug.Assert(dataModel == SchemaDataModelOption.ProviderManifestModel, "Unknown SchemaDataModelOption did you add one?"); namespaces.Add(XmlConstants.ProviderManifestNamespace); } return namespaces; } internal static XmlSchemaSet GetSchemaSet(SchemaDataModelOption dataModel) { return _cachedSchemaSets.Evaluate(dataModel); } private static XmlSchemaSet ComputeSchemaSet(SchemaDataModelOption dataModel) { List namespaceNames = GetPrimarySchemaNamespaces(dataModel); Debug.Assert(namespaceNames.Count > 0, "Unknown Datamodel"); XmlSchemaSet schemaSet = new XmlSchemaSet(); // remove the default XmlResolver which will look on // disk for the referenced schemas that we already provided schemaSet.XmlResolver = null; var schemaResourceMap = XmlSchemaResource.GetMetadataSchemaResourceMap(XmlConstants.SchemaVersionLatest); HashSet schemasAlreadyAdded = new HashSet(); foreach (string namespaceName in namespaceNames) { Debug.Assert(schemaResourceMap.ContainsKey(namespaceName), "the namespace name is not one we have a schema set for"); XmlSchemaResource schemaResource = schemaResourceMap[namespaceName]; AddXmlSchemaToSet(schemaSet, schemaResource, schemasAlreadyAdded); } schemaSet.Compile(); return schemaSet; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security.Xml", "CA3060:UseXmlReaderForSchemaRead", Justification = "The schema files are embedded in the product assembly as resources")] private static void AddXmlSchemaToSet(XmlSchemaSet schemaSet, XmlSchemaResource schemaResource, HashSet schemasAlreadyAdded) { // loop through the children to do a depth first load foreach (var import in schemaResource.ImportedSchemas) { AddXmlSchemaToSet(schemaSet, import, schemasAlreadyAdded); } if (!schemasAlreadyAdded.Contains(schemaResource.NamespaceUri)) { Stream xsdStream = GetResourceStream(schemaResource.ResourceName); XmlSchema schema = XmlSchema.Read(xsdStream, null); schemaSet.Add(schema); schemasAlreadyAdded.Add(schemaResource.NamespaceUri); } } private static Stream GetResourceStream(string resourceName) { Debug.Assert(resourceName != null, "resourceName cannot be null"); Stream resourceStream = null; System.Reflection.Assembly executingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); if (executingAssembly != null) { resourceStream = executingAssembly.GetManifestResourceStream(resourceName); } Debug.Assert(resourceStream != null, string.Format(CultureInfo.CurrentCulture, "Unable to load the resource {0} from assembly resources.", resourceName)); return resourceStream; } } } }