Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Mapping / FunctionImportMapping.cs / 1 / FunctionImportMapping.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System.Data.Metadata.Edm; using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Xml; using System.Data.Common.Utils; using System.Data.Entity; using OM = System.Collections.ObjectModel; using System.Globalization; using System.Diagnostics; using System.Collections; using System.Data.Common.Utils.Boolean; using System.Xml.XPath; namespace System.Data.Mapping { ////// Represents a mapping from a model FunctionImport to a store /// non-composable Function. /// internal sealed class FunctionImportMapping { internal FunctionImportMapping(EdmFunction targetFunction, EdmFunction functionImport, IEnumerableentityTypeMappings, ItemCollection itemCollection) { this.TargetFunction = EntityUtil.CheckArgumentNull(targetFunction, "targetFunction"); this.FunctionImport = EntityUtil.CheckArgumentNull(functionImport, "functionImport"); EntityUtil.CheckArgumentNull(itemCollection, "itemCollection"); // collect all mapped entity types this.MappedEntityTypes = entityTypeMappings .SelectMany(mapping => mapping.GetMappedEntityTypes(itemCollection)) .Distinct() .ToList() .AsReadOnly(); // collect all discriminator columns this.DiscriminatorColumns = entityTypeMappings .SelectMany(mapping => mapping.GetDiscriminatorColumns()) .Distinct() .ToList() .AsReadOnly(); this.EntityTypeLineInfos = new KeyToListMap (EqualityComparer .Default); this.IsTypeOfLineInfos = new KeyToListMap (EqualityComparer .Default); // normalize the type mappings var normalizedEntityTypeMappings = new List (); foreach (var entityTypeMapping in entityTypeMappings) { // remember LineInfos for error reporting foreach (var entityType in entityTypeMapping.EntityTypes) { this.EntityTypeLineInfos.Add(entityType, entityTypeMapping.LineInfo); } foreach (var isTypeOf in entityTypeMapping.IsOfTypeEntityTypes) { this.IsTypeOfLineInfos.Add(isTypeOf, entityTypeMapping.LineInfo); } // create map from column name to condition var columnMap = entityTypeMapping.Conditions.ToDictionary( condition => condition.ColumnName, condition => condition); // align conditions with discriminator columns var columnMappings = new List (this.DiscriminatorColumns.Count); for (int i = 0; i < this.DiscriminatorColumns.Count; i++) { string discriminatorColumn = this.DiscriminatorColumns[i]; FunctionImportEntityTypeMappingCondition mappingCondition; if (columnMap.TryGetValue(discriminatorColumn, out mappingCondition)) { columnMappings.Add(mappingCondition); } else { // null indicates the value for this discriminator doesn't matter columnMappings.Add(null); } } // create bit map for implied entity types bool[] impliedEntityTypesBitMap = new bool[this.MappedEntityTypes.Count]; var impliedEntityTypesSet = new Set (entityTypeMapping.GetMappedEntityTypes(itemCollection)); for (int i = 0; i < this.MappedEntityTypes.Count; i++) { impliedEntityTypesBitMap[i] = impliedEntityTypesSet.Contains(this.MappedEntityTypes[i]); } // construct normalized mapping normalizedEntityTypeMappings.Add(new FunctionImportNormalizedEntityTypeMapping(this, columnMappings, new BitArray(impliedEntityTypesBitMap))); } this.NormalizedEntityTypeMappings = new OM.ReadOnlyCollection ( normalizedEntityTypeMappings); } /// /// Gets store function (or target of the mapping) /// internal readonly EdmFunction TargetFunction; ////// Gets model function (or source of the mapping) /// internal readonly EdmFunction FunctionImport; ////// Gets all types in scope for this mapping. /// internal readonly OM.ReadOnlyCollectionMappedEntityTypes; /// /// Gets a list of all discriminator columns used in this mapping. /// internal readonly OM.ReadOnlyCollectionDiscriminatorColumns; /// /// Gets normalized representation of all EntityTypeMapping fragments for this /// function import mapping. /// internal readonly OM.ReadOnlyCollectionNormalizedEntityTypeMappings; /// /// Gets line infos for entity type mappings. /// internal readonly KeyToListMapEntityTypeLineInfos; /// /// Gets line infos for IsTypeOf mappings for entity types. /// internal readonly KeyToListMapIsTypeOfLineInfos; /// /// Given discriminator values (ordinally aligned with DiscriminatorColumns), determines /// the entity type to return. Throws a CommandExecutionException if the type is ambiguous. /// internal EntityType Discriminate(object[] discriminatorValues) { // initialize matching types bit map BitArray typeCandidates = new BitArray(this.MappedEntityTypes.Count, true); foreach (var typeMapping in this.NormalizedEntityTypeMappings) { // check if this type mapping is matched bool matches = true; var columnConditions = typeMapping.ColumnConditions; for (int i = 0; i < columnConditions.Count; i++) { if (null != columnConditions[i] && // this discriminator doesn't matter for the given condition !columnConditions[i].ColumnValueMatchesCondition(discriminatorValues[i])) { matches = false; break; } } if (matches) { // if the type condition is met, narrow the set of type candidates typeCandidates = typeCandidates.And(typeMapping.ImpliedEntityTypes); } else { // if the type condition fails, all implied types are eliminated // (the type mapping fragment is a co-implication, so a type is no longer // a candidate if any condition referring to it is false) typeCandidates = typeCandidates.And(typeMapping.ComplementImpliedEntityTypes); } } // find matching type condition EntityType entityType = null; for (int i = 0; i < typeCandidates.Length; i++) { if (typeCandidates[i]) { if (null != entityType) { throw EntityUtil.CommandExecution(System.Data.Entity.Strings.ADP_InvalidDataReaderUnableToDetermineType); } entityType = this.MappedEntityTypes[i]; } } // if there is no match, raise an exception if (null == entityType) { throw EntityUtil.CommandExecution(System.Data.Entity.Strings.ADP_InvalidDataReaderUnableToDetermineType); } return entityType; } ////// Determines which explicitly mapped types in the function import mapping cannot be generated. /// For IsTypeOf declarations, reports if no type in hierarchy can be produced. /// /// Works by: /// /// - Converting type mapping conditions into vertices /// - Checking that some assignment satisfies /// internal void GetUnreachableTypes(EdmItemCollection itemCollection, out KeyToListMapunreachableEntityTypes, out KeyToListMap unreachableIsTypeOfs) { // Contains, for each DiscriminatorColumn, a domain variable where the domain values are // integers representing the ordinal within discriminatorDomains DomainVariable [] variables = ConstructDomainVariables(); // Convert type mapping conditions to decision diagram vertices var converter = new DomainConstraintConversionContext (); Vertex[] mappingConditions = ConvertMappingConditionsToVertices(converter, variables); // Find reachable types Set reachableTypes = FindReachableTypes(converter, mappingConditions); CollectUnreachableTypes(itemCollection, reachableTypes, out unreachableEntityTypes, out unreachableIsTypeOfs); } /// /// Determines the expected shape of store results. We expect a column for every property /// of the mapped type (or types) and a column for every discriminator column. We make no /// assumptions about the order of columns: the provider is expected to determine appropriate /// types by looking at the names of the result columns, not the order of columns, which is /// different from the typical handling of row types in the EF. /// ////// Requires that the given function import mapping refers to a Collection(Entity) CSDL /// function. /// ///Row type. internal TypeUsage GetExpectedTargetResultType(MetadataWorkspace workspace) { // Collect all columns as name-type pairs. Dictionarycolumns = new Dictionary (); // Figure out which entity types we expect to yield from the function. IEnumerable entityTypes; if (0 == this.NormalizedEntityTypeMappings.Count) { // No explicit type mappings; just use the base entity type. EntityType entityType; MetadataHelper.TryGetFunctionImportReturnEntityType(this.FunctionImport, out entityType); Debug.Assert(null != entityType, "this method must be called only for entity reader function imports"); entityTypes = new EntityType[] { entityType }; } else { // Types are explicitly mapped. entityTypes = this.MappedEntityTypes; } // Gather columns corresponding to all entity properties. foreach (EntityType entityType in entityTypes) { foreach (EdmProperty property in TypeHelpers.GetAllStructuralMembers(entityType)) { // NOTE: if a complex type is encountered, the column map generator will // throw. For now, we just let them through. // We expect to see each property multiple times, so we use .item rather than // .Add. columns[property.Name] = property.TypeUsage; } } // Gather discriminator columns. foreach (string discriminatorColumn in this.DiscriminatorColumns) { if (!columns.ContainsKey(discriminatorColumn)) { // TypeUsage type = TypeUsage.CreateStringTypeUsage(workspace.GetModelPrimitiveType(PrimitiveTypeKind.String), true, false); columns.Add(discriminatorColumn, type); } } // Expected type is a collection of rows RowType rowType = new RowType(columns.Select(c => new EdmProperty(c.Key, c.Value))); TypeUsage result = TypeUsage.Create(new CollectionType(TypeUsage.Create(rowType))); return result; } /// /// Determines which types are produced by this mapping /// private SetFindReachableTypes(DomainConstraintConversionContext converter, Vertex[] mappingConditions) { Set reachableTypes = new Set (); // for each entity type, create a candidate function that evaluates to true given // discriminator assignments iff. all of that type's conditions evaluate to true // and its negative conditions evaluate to false Vertex[] candidateFunctions = new Vertex[this.MappedEntityTypes.Count]; for (int i = 0; i < candidateFunctions.Length; i++) { // seed the candidate function conjunction with 'true' Vertex candidateFunction = Vertex.One; for (int j = 0; j < this.NormalizedEntityTypeMappings.Count; j++) { var entityTypeMapping = this.NormalizedEntityTypeMappings[j]; // determine if this mapping is a positive or negative case for the current type if (entityTypeMapping.ImpliedEntityTypes[i]) { candidateFunction = converter.Solver.And(candidateFunction, mappingConditions[j]); } else { candidateFunction = converter.Solver.And(candidateFunction, converter.Solver.Not(mappingConditions[j])); } } candidateFunctions[i] = candidateFunction; } // for each type, make sure there is some assignment that resolves to only that type for (int i = 0; i < candidateFunctions.Length; i++) { // create a function that evaluates to true iff. the current candidate function is true // and every other candidate function is false Vertex isExactlyThisTypeCondition = converter.Solver.And( candidateFunctions.Select((typeCondition, ordinal) => ordinal == i ? typeCondition : converter.Solver.Not(typeCondition))); // if the above conjunction is satisfiable, it means some row configuration exists // producing the type if (!isExactlyThisTypeCondition.IsZero()) { reachableTypes.Add(this.MappedEntityTypes[i]); } } return reachableTypes; } private void CollectUnreachableTypes(EdmItemCollection itemCollection, Set reachableTypes, out KeyToListMap entityTypes, out KeyToListMap isTypeOfEntityTypes) { // Collect line infos for types in violation entityTypes = new KeyToListMap (EqualityComparer .Default); isTypeOfEntityTypes = new KeyToListMap (EqualityComparer .Default); if (reachableTypes.Count == this.MappedEntityTypes.Count) { // All types are reachable; nothing to check return; } // Find IsTypeOf mappings where no type in hierarchy can generate a row foreach (var isTypeOf in this.IsTypeOfLineInfos.Keys) { if (!MetadataHelper.GetTypeAndSubtypesOf(isTypeOf, itemCollection, false) .Cast () .Intersect(reachableTypes) .Any()) { // no type in the hierarchy is reachable... isTypeOfEntityTypes.AddRange(isTypeOf, this.IsTypeOfLineInfos.EnumerateValues(isTypeOf)); } } // Find explicit types not generating a value foreach (var entityType in this.EntityTypeLineInfos.Keys) { if (!reachableTypes.Contains(entityType)) { entityTypes.AddRange(entityType, this.EntityTypeLineInfos.EnumerateValues(entityType)); } } } private DomainVariable [] ConstructDomainVariables() { // determine domain for each discriminator column, including "other" and "null" placeholders var discriminatorDomains = new Set [this.DiscriminatorColumns.Count]; for (int i = 0; i < discriminatorDomains.Length; i++) { discriminatorDomains[i] = new Set (); discriminatorDomains[i].Add(ValueCondition.IsOther); discriminatorDomains[i].Add(ValueCondition.IsNull); } // collect all domain values foreach (var typeMapping in this.NormalizedEntityTypeMappings) { for (int i = 0; i < this.DiscriminatorColumns.Count; i++) { var discriminatorValue = typeMapping.ColumnConditions[i]; if (null != discriminatorValue && !discriminatorValue.ConditionValue.IsNotNullCondition) // NotNull is a special range (everything but IsNull) { discriminatorDomains[i].Add(discriminatorValue.ConditionValue); } } } var discriminatorVariables = new DomainVariable [discriminatorDomains.Length]; for (int i = 0; i < discriminatorVariables.Length; i++) { // domain variable is identified by the column name and takes all collected domain values discriminatorVariables[i] = new DomainVariable (this.DiscriminatorColumns[i], discriminatorDomains[i].MakeReadOnly()); } return discriminatorVariables; } private Vertex[] ConvertMappingConditionsToVertices(ConversionContext > converter, DomainVariable [] variables) { Vertex[] conditions = new Vertex[this.NormalizedEntityTypeMappings.Count]; for (int i = 0; i < conditions.Length; i++) { var typeMapping = this.NormalizedEntityTypeMappings[i]; // create conjunction representing the condition Vertex condition = Vertex.One; for (int j = 0; j < this.DiscriminatorColumns.Count; j++) { var columnCondition = typeMapping.ColumnConditions[j]; if (null != columnCondition) { var conditionValue = columnCondition.ConditionValue; if (conditionValue.IsNotNullCondition) { // the 'not null' condition is not actually part of the domain (since it // covers other elements), so create a Not(value in {null}) condition var isNull = new TermExpr >( new DomainConstraint (variables[j], ValueCondition.IsNull)); Vertex isNullVertex = converter.TranslateTermToVertex(isNull); condition = converter.Solver.And(condition, converter.Solver.Not(isNullVertex)); } else { var hasValue = new TermExpr >( new DomainConstraint (variables[j], conditionValue)); condition = converter.Solver.And(condition, converter.TranslateTermToVertex(hasValue)); } } } conditions[i] = condition; } return conditions; } } internal sealed class FunctionImportNormalizedEntityTypeMapping { internal FunctionImportNormalizedEntityTypeMapping(FunctionImportMapping parent, List columnConditions, BitArray impliedEntityTypes) { // validate arguments EntityUtil.CheckArgumentNull(parent, "parent"); EntityUtil.CheckArgumentNull(columnConditions, "discriminatorValues"); EntityUtil.CheckArgumentNull(impliedEntityTypes, "impliedEntityTypes"); Debug.Assert(columnConditions.Count == parent.DiscriminatorColumns.Count, "discriminator values must be ordinally aligned with discriminator columns"); Debug.Assert(impliedEntityTypes.Count == parent.MappedEntityTypes.Count, "implied entity types must be ordinally aligned with mapped entity types"); this.ColumnConditions = new OM.ReadOnlyCollection (columnConditions.ToList()); this.ImpliedEntityTypes = impliedEntityTypes; this.ComplementImpliedEntityTypes = (new BitArray(this.ImpliedEntityTypes)).Not(); } /// /// Gets discriminator values aligned with DiscriminatorColumns of the parent FunctionImportMapping. /// A null ValueCondition indicates 'anything goes'. /// internal readonly OM.ReadOnlyCollectionColumnConditions; /// /// Gets bit array with 'true' indicating the corresponding MappedEntityType of the parent /// FunctionImportMapping is implied by this fragment. /// internal readonly BitArray ImpliedEntityTypes; ////// Gets the complement of the ImpliedEntityTypes BitArray. /// internal readonly BitArray ComplementImpliedEntityTypes; public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "Values={0}, Types={1}", StringUtil.ToCommaSeparatedString(this.ColumnConditions), StringUtil.ToCommaSeparatedString(this.ImpliedEntityTypes)); } } internal sealed class FunctionImportEntityTypeMapping { internal FunctionImportEntityTypeMapping(IEnumerableisOfTypeEntityTypes, IEnumerable entityTypes, IEnumerable conditions, IXmlLineInfo lineInfo) { this.IsOfTypeEntityTypes = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(isOfTypeEntityTypes, "isOfTypeEntityTypes").ToList()); this.EntityTypes = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(entityTypes, "entityTypes").ToList()); this.Conditions = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(conditions, "conditions").ToList()); this.LineInfo = lineInfo; } internal readonly OM.ReadOnlyCollection Conditions; internal readonly OM.ReadOnlyCollection EntityTypes; internal readonly OM.ReadOnlyCollection IsOfTypeEntityTypes; internal readonly IXmlLineInfo LineInfo; /// /// Gets all (concrete) entity types implied by this type mapping. /// internal IEnumerableGetMappedEntityTypes(ItemCollection itemCollection) { const bool includeAbstractTypes = false; return this.EntityTypes.Concat( this.IsOfTypeEntityTypes.SelectMany(entityType => MetadataHelper.GetTypeAndSubtypesOf(entityType, itemCollection, includeAbstractTypes) .Cast ())); } internal IEnumerable GetDiscriminatorColumns() { return this.Conditions.Select(condition => condition.ColumnName); } } internal abstract class FunctionImportEntityTypeMappingCondition { protected FunctionImportEntityTypeMappingCondition(string columnName) { this.ColumnName = EntityUtil.CheckArgumentNull(columnName, "columnName"); } internal abstract ValueCondition ConditionValue { get; } internal readonly string ColumnName; internal abstract bool ColumnValueMatchesCondition(object columnValue); public override string ToString() { return this.ConditionValue.ToString(); } } internal sealed class FunctionImportEntityTypeMappingConditionValue : FunctionImportEntityTypeMappingCondition { internal FunctionImportEntityTypeMappingConditionValue(string columnName, XPathNavigator columnValue) : base(columnName) { this._xPathValue = EntityUtil.CheckArgumentNull(columnValue, "columnValue"); this._convertedValues = new Memoizer (this.GetConditionValue, null); } private readonly XPathNavigator _xPathValue; private readonly Memoizer _convertedValues; internal override ValueCondition ConditionValue { get { return new ValueCondition(_xPathValue.Value); } } internal override bool ColumnValueMatchesCondition(object columnValue) { if (null == columnValue || Convert.IsDBNull(columnValue)) { // only FunctionImportEntityTypeMappingConditionIsNull can match a null // column value return false; } Type columnValueType = columnValue.GetType(); // check if we've interpreted this column type yet object conditionValue = _convertedValues.Evaluate(columnValueType); return CdpEqualityComparer.DefaultEqualityComparer.Equals(columnValue, conditionValue); } private object GetConditionValue(Type columnValueType) { object conditionValue; // check that the type is supported PrimitiveType primitiveType; if (!ClrProviderManifest.Instance.TryGetPrimitiveType(columnValueType, out primitiveType) || !StorageMappingItemLoader.IsTypeSupportedForCondition(primitiveType.PrimitiveTypeKind)) { throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_UnsupportedType( this.ColumnName, columnValueType.FullName)); } try { conditionValue = _xPathValue.ValueAs(columnValueType); } catch (FormatException) { throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_ConditionValueTypeMismatch( StorageMslConstructs.FunctionImportMappingElement, this.ColumnName, columnValueType.FullName)); } return conditionValue; } } internal sealed class FunctionImportEntityTypeMappingConditionIsNull : FunctionImportEntityTypeMappingCondition { internal FunctionImportEntityTypeMappingConditionIsNull(string columnName, bool isNull) : base(columnName) { this.IsNull = isNull; } internal readonly bool IsNull; internal override ValueCondition ConditionValue { get { return IsNull ? ValueCondition.IsNull : ValueCondition.IsNotNull; } } internal override bool ColumnValueMatchesCondition(object columnValue) { bool valueIsNull = null == columnValue || Convert.IsDBNull(columnValue); return valueIsNull == this.IsNull; } } /// /// Represents a simple value condition of the form (value IS NULL), (value IS NOT NULL) /// or (value EQ X). Supports IEquatable(Of ValueCondition) so that equivalent conditions /// can be identified. /// internal class ValueCondition : IEquatable{ internal readonly string Description; internal readonly bool IsSentinel; internal const string IsNullDescription = "NULL"; internal const string IsNotNullDescription = "NOT NULL"; internal const string IsOtherDescription = "OTHER"; internal readonly static ValueCondition IsNull = new ValueCondition(IsNullDescription, true); internal readonly static ValueCondition IsNotNull = new ValueCondition(IsNotNullDescription, true); internal readonly static ValueCondition IsOther = new ValueCondition(IsOtherDescription, true); private ValueCondition(string description, bool isSentinel) { Description = description; IsSentinel = isSentinel; } internal ValueCondition(string description) : this(description, false) { } internal bool IsNotNullCondition { get { return object.ReferenceEquals(this, IsNotNull); } } public bool Equals(ValueCondition other) { return other.IsSentinel == this.IsSentinel && other.Description == this.Description; } public override int GetHashCode() { return Description.GetHashCode(); } public override string ToString() { return this.Description; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] //--------------------------------------------------------------------- using System.Data.Metadata.Edm; using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Xml; using System.Data.Common.Utils; using System.Data.Entity; using OM = System.Collections.ObjectModel; using System.Globalization; using System.Diagnostics; using System.Collections; using System.Data.Common.Utils.Boolean; using System.Xml.XPath; namespace System.Data.Mapping { ////// Represents a mapping from a model FunctionImport to a store /// non-composable Function. /// internal sealed class FunctionImportMapping { internal FunctionImportMapping(EdmFunction targetFunction, EdmFunction functionImport, IEnumerableentityTypeMappings, ItemCollection itemCollection) { this.TargetFunction = EntityUtil.CheckArgumentNull(targetFunction, "targetFunction"); this.FunctionImport = EntityUtil.CheckArgumentNull(functionImport, "functionImport"); EntityUtil.CheckArgumentNull(itemCollection, "itemCollection"); // collect all mapped entity types this.MappedEntityTypes = entityTypeMappings .SelectMany(mapping => mapping.GetMappedEntityTypes(itemCollection)) .Distinct() .ToList() .AsReadOnly(); // collect all discriminator columns this.DiscriminatorColumns = entityTypeMappings .SelectMany(mapping => mapping.GetDiscriminatorColumns()) .Distinct() .ToList() .AsReadOnly(); this.EntityTypeLineInfos = new KeyToListMap (EqualityComparer .Default); this.IsTypeOfLineInfos = new KeyToListMap (EqualityComparer .Default); // normalize the type mappings var normalizedEntityTypeMappings = new List (); foreach (var entityTypeMapping in entityTypeMappings) { // remember LineInfos for error reporting foreach (var entityType in entityTypeMapping.EntityTypes) { this.EntityTypeLineInfos.Add(entityType, entityTypeMapping.LineInfo); } foreach (var isTypeOf in entityTypeMapping.IsOfTypeEntityTypes) { this.IsTypeOfLineInfos.Add(isTypeOf, entityTypeMapping.LineInfo); } // create map from column name to condition var columnMap = entityTypeMapping.Conditions.ToDictionary( condition => condition.ColumnName, condition => condition); // align conditions with discriminator columns var columnMappings = new List (this.DiscriminatorColumns.Count); for (int i = 0; i < this.DiscriminatorColumns.Count; i++) { string discriminatorColumn = this.DiscriminatorColumns[i]; FunctionImportEntityTypeMappingCondition mappingCondition; if (columnMap.TryGetValue(discriminatorColumn, out mappingCondition)) { columnMappings.Add(mappingCondition); } else { // null indicates the value for this discriminator doesn't matter columnMappings.Add(null); } } // create bit map for implied entity types bool[] impliedEntityTypesBitMap = new bool[this.MappedEntityTypes.Count]; var impliedEntityTypesSet = new Set (entityTypeMapping.GetMappedEntityTypes(itemCollection)); for (int i = 0; i < this.MappedEntityTypes.Count; i++) { impliedEntityTypesBitMap[i] = impliedEntityTypesSet.Contains(this.MappedEntityTypes[i]); } // construct normalized mapping normalizedEntityTypeMappings.Add(new FunctionImportNormalizedEntityTypeMapping(this, columnMappings, new BitArray(impliedEntityTypesBitMap))); } this.NormalizedEntityTypeMappings = new OM.ReadOnlyCollection ( normalizedEntityTypeMappings); } /// /// Gets store function (or target of the mapping) /// internal readonly EdmFunction TargetFunction; ////// Gets model function (or source of the mapping) /// internal readonly EdmFunction FunctionImport; ////// Gets all types in scope for this mapping. /// internal readonly OM.ReadOnlyCollectionMappedEntityTypes; /// /// Gets a list of all discriminator columns used in this mapping. /// internal readonly OM.ReadOnlyCollectionDiscriminatorColumns; /// /// Gets normalized representation of all EntityTypeMapping fragments for this /// function import mapping. /// internal readonly OM.ReadOnlyCollectionNormalizedEntityTypeMappings; /// /// Gets line infos for entity type mappings. /// internal readonly KeyToListMapEntityTypeLineInfos; /// /// Gets line infos for IsTypeOf mappings for entity types. /// internal readonly KeyToListMapIsTypeOfLineInfos; /// /// Given discriminator values (ordinally aligned with DiscriminatorColumns), determines /// the entity type to return. Throws a CommandExecutionException if the type is ambiguous. /// internal EntityType Discriminate(object[] discriminatorValues) { // initialize matching types bit map BitArray typeCandidates = new BitArray(this.MappedEntityTypes.Count, true); foreach (var typeMapping in this.NormalizedEntityTypeMappings) { // check if this type mapping is matched bool matches = true; var columnConditions = typeMapping.ColumnConditions; for (int i = 0; i < columnConditions.Count; i++) { if (null != columnConditions[i] && // this discriminator doesn't matter for the given condition !columnConditions[i].ColumnValueMatchesCondition(discriminatorValues[i])) { matches = false; break; } } if (matches) { // if the type condition is met, narrow the set of type candidates typeCandidates = typeCandidates.And(typeMapping.ImpliedEntityTypes); } else { // if the type condition fails, all implied types are eliminated // (the type mapping fragment is a co-implication, so a type is no longer // a candidate if any condition referring to it is false) typeCandidates = typeCandidates.And(typeMapping.ComplementImpliedEntityTypes); } } // find matching type condition EntityType entityType = null; for (int i = 0; i < typeCandidates.Length; i++) { if (typeCandidates[i]) { if (null != entityType) { throw EntityUtil.CommandExecution(System.Data.Entity.Strings.ADP_InvalidDataReaderUnableToDetermineType); } entityType = this.MappedEntityTypes[i]; } } // if there is no match, raise an exception if (null == entityType) { throw EntityUtil.CommandExecution(System.Data.Entity.Strings.ADP_InvalidDataReaderUnableToDetermineType); } return entityType; } ////// Determines which explicitly mapped types in the function import mapping cannot be generated. /// For IsTypeOf declarations, reports if no type in hierarchy can be produced. /// /// Works by: /// /// - Converting type mapping conditions into vertices /// - Checking that some assignment satisfies /// internal void GetUnreachableTypes(EdmItemCollection itemCollection, out KeyToListMapunreachableEntityTypes, out KeyToListMap unreachableIsTypeOfs) { // Contains, for each DiscriminatorColumn, a domain variable where the domain values are // integers representing the ordinal within discriminatorDomains DomainVariable [] variables = ConstructDomainVariables(); // Convert type mapping conditions to decision diagram vertices var converter = new DomainConstraintConversionContext (); Vertex[] mappingConditions = ConvertMappingConditionsToVertices(converter, variables); // Find reachable types Set reachableTypes = FindReachableTypes(converter, mappingConditions); CollectUnreachableTypes(itemCollection, reachableTypes, out unreachableEntityTypes, out unreachableIsTypeOfs); } /// /// Determines the expected shape of store results. We expect a column for every property /// of the mapped type (or types) and a column for every discriminator column. We make no /// assumptions about the order of columns: the provider is expected to determine appropriate /// types by looking at the names of the result columns, not the order of columns, which is /// different from the typical handling of row types in the EF. /// ////// Requires that the given function import mapping refers to a Collection(Entity) CSDL /// function. /// ///Row type. internal TypeUsage GetExpectedTargetResultType(MetadataWorkspace workspace) { // Collect all columns as name-type pairs. Dictionarycolumns = new Dictionary (); // Figure out which entity types we expect to yield from the function. IEnumerable entityTypes; if (0 == this.NormalizedEntityTypeMappings.Count) { // No explicit type mappings; just use the base entity type. EntityType entityType; MetadataHelper.TryGetFunctionImportReturnEntityType(this.FunctionImport, out entityType); Debug.Assert(null != entityType, "this method must be called only for entity reader function imports"); entityTypes = new EntityType[] { entityType }; } else { // Types are explicitly mapped. entityTypes = this.MappedEntityTypes; } // Gather columns corresponding to all entity properties. foreach (EntityType entityType in entityTypes) { foreach (EdmProperty property in TypeHelpers.GetAllStructuralMembers(entityType)) { // NOTE: if a complex type is encountered, the column map generator will // throw. For now, we just let them through. // We expect to see each property multiple times, so we use .item rather than // .Add. columns[property.Name] = property.TypeUsage; } } // Gather discriminator columns. foreach (string discriminatorColumn in this.DiscriminatorColumns) { if (!columns.ContainsKey(discriminatorColumn)) { // TypeUsage type = TypeUsage.CreateStringTypeUsage(workspace.GetModelPrimitiveType(PrimitiveTypeKind.String), true, false); columns.Add(discriminatorColumn, type); } } // Expected type is a collection of rows RowType rowType = new RowType(columns.Select(c => new EdmProperty(c.Key, c.Value))); TypeUsage result = TypeUsage.Create(new CollectionType(TypeUsage.Create(rowType))); return result; } /// /// Determines which types are produced by this mapping /// private SetFindReachableTypes(DomainConstraintConversionContext converter, Vertex[] mappingConditions) { Set reachableTypes = new Set (); // for each entity type, create a candidate function that evaluates to true given // discriminator assignments iff. all of that type's conditions evaluate to true // and its negative conditions evaluate to false Vertex[] candidateFunctions = new Vertex[this.MappedEntityTypes.Count]; for (int i = 0; i < candidateFunctions.Length; i++) { // seed the candidate function conjunction with 'true' Vertex candidateFunction = Vertex.One; for (int j = 0; j < this.NormalizedEntityTypeMappings.Count; j++) { var entityTypeMapping = this.NormalizedEntityTypeMappings[j]; // determine if this mapping is a positive or negative case for the current type if (entityTypeMapping.ImpliedEntityTypes[i]) { candidateFunction = converter.Solver.And(candidateFunction, mappingConditions[j]); } else { candidateFunction = converter.Solver.And(candidateFunction, converter.Solver.Not(mappingConditions[j])); } } candidateFunctions[i] = candidateFunction; } // for each type, make sure there is some assignment that resolves to only that type for (int i = 0; i < candidateFunctions.Length; i++) { // create a function that evaluates to true iff. the current candidate function is true // and every other candidate function is false Vertex isExactlyThisTypeCondition = converter.Solver.And( candidateFunctions.Select((typeCondition, ordinal) => ordinal == i ? typeCondition : converter.Solver.Not(typeCondition))); // if the above conjunction is satisfiable, it means some row configuration exists // producing the type if (!isExactlyThisTypeCondition.IsZero()) { reachableTypes.Add(this.MappedEntityTypes[i]); } } return reachableTypes; } private void CollectUnreachableTypes(EdmItemCollection itemCollection, Set reachableTypes, out KeyToListMap entityTypes, out KeyToListMap isTypeOfEntityTypes) { // Collect line infos for types in violation entityTypes = new KeyToListMap (EqualityComparer .Default); isTypeOfEntityTypes = new KeyToListMap (EqualityComparer .Default); if (reachableTypes.Count == this.MappedEntityTypes.Count) { // All types are reachable; nothing to check return; } // Find IsTypeOf mappings where no type in hierarchy can generate a row foreach (var isTypeOf in this.IsTypeOfLineInfos.Keys) { if (!MetadataHelper.GetTypeAndSubtypesOf(isTypeOf, itemCollection, false) .Cast () .Intersect(reachableTypes) .Any()) { // no type in the hierarchy is reachable... isTypeOfEntityTypes.AddRange(isTypeOf, this.IsTypeOfLineInfos.EnumerateValues(isTypeOf)); } } // Find explicit types not generating a value foreach (var entityType in this.EntityTypeLineInfos.Keys) { if (!reachableTypes.Contains(entityType)) { entityTypes.AddRange(entityType, this.EntityTypeLineInfos.EnumerateValues(entityType)); } } } private DomainVariable [] ConstructDomainVariables() { // determine domain for each discriminator column, including "other" and "null" placeholders var discriminatorDomains = new Set [this.DiscriminatorColumns.Count]; for (int i = 0; i < discriminatorDomains.Length; i++) { discriminatorDomains[i] = new Set (); discriminatorDomains[i].Add(ValueCondition.IsOther); discriminatorDomains[i].Add(ValueCondition.IsNull); } // collect all domain values foreach (var typeMapping in this.NormalizedEntityTypeMappings) { for (int i = 0; i < this.DiscriminatorColumns.Count; i++) { var discriminatorValue = typeMapping.ColumnConditions[i]; if (null != discriminatorValue && !discriminatorValue.ConditionValue.IsNotNullCondition) // NotNull is a special range (everything but IsNull) { discriminatorDomains[i].Add(discriminatorValue.ConditionValue); } } } var discriminatorVariables = new DomainVariable [discriminatorDomains.Length]; for (int i = 0; i < discriminatorVariables.Length; i++) { // domain variable is identified by the column name and takes all collected domain values discriminatorVariables[i] = new DomainVariable (this.DiscriminatorColumns[i], discriminatorDomains[i].MakeReadOnly()); } return discriminatorVariables; } private Vertex[] ConvertMappingConditionsToVertices(ConversionContext > converter, DomainVariable [] variables) { Vertex[] conditions = new Vertex[this.NormalizedEntityTypeMappings.Count]; for (int i = 0; i < conditions.Length; i++) { var typeMapping = this.NormalizedEntityTypeMappings[i]; // create conjunction representing the condition Vertex condition = Vertex.One; for (int j = 0; j < this.DiscriminatorColumns.Count; j++) { var columnCondition = typeMapping.ColumnConditions[j]; if (null != columnCondition) { var conditionValue = columnCondition.ConditionValue; if (conditionValue.IsNotNullCondition) { // the 'not null' condition is not actually part of the domain (since it // covers other elements), so create a Not(value in {null}) condition var isNull = new TermExpr >( new DomainConstraint (variables[j], ValueCondition.IsNull)); Vertex isNullVertex = converter.TranslateTermToVertex(isNull); condition = converter.Solver.And(condition, converter.Solver.Not(isNullVertex)); } else { var hasValue = new TermExpr >( new DomainConstraint (variables[j], conditionValue)); condition = converter.Solver.And(condition, converter.TranslateTermToVertex(hasValue)); } } } conditions[i] = condition; } return conditions; } } internal sealed class FunctionImportNormalizedEntityTypeMapping { internal FunctionImportNormalizedEntityTypeMapping(FunctionImportMapping parent, List columnConditions, BitArray impliedEntityTypes) { // validate arguments EntityUtil.CheckArgumentNull(parent, "parent"); EntityUtil.CheckArgumentNull(columnConditions, "discriminatorValues"); EntityUtil.CheckArgumentNull(impliedEntityTypes, "impliedEntityTypes"); Debug.Assert(columnConditions.Count == parent.DiscriminatorColumns.Count, "discriminator values must be ordinally aligned with discriminator columns"); Debug.Assert(impliedEntityTypes.Count == parent.MappedEntityTypes.Count, "implied entity types must be ordinally aligned with mapped entity types"); this.ColumnConditions = new OM.ReadOnlyCollection (columnConditions.ToList()); this.ImpliedEntityTypes = impliedEntityTypes; this.ComplementImpliedEntityTypes = (new BitArray(this.ImpliedEntityTypes)).Not(); } /// /// Gets discriminator values aligned with DiscriminatorColumns of the parent FunctionImportMapping. /// A null ValueCondition indicates 'anything goes'. /// internal readonly OM.ReadOnlyCollectionColumnConditions; /// /// Gets bit array with 'true' indicating the corresponding MappedEntityType of the parent /// FunctionImportMapping is implied by this fragment. /// internal readonly BitArray ImpliedEntityTypes; ////// Gets the complement of the ImpliedEntityTypes BitArray. /// internal readonly BitArray ComplementImpliedEntityTypes; public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "Values={0}, Types={1}", StringUtil.ToCommaSeparatedString(this.ColumnConditions), StringUtil.ToCommaSeparatedString(this.ImpliedEntityTypes)); } } internal sealed class FunctionImportEntityTypeMapping { internal FunctionImportEntityTypeMapping(IEnumerableisOfTypeEntityTypes, IEnumerable entityTypes, IEnumerable conditions, IXmlLineInfo lineInfo) { this.IsOfTypeEntityTypes = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(isOfTypeEntityTypes, "isOfTypeEntityTypes").ToList()); this.EntityTypes = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(entityTypes, "entityTypes").ToList()); this.Conditions = new OM.ReadOnlyCollection ( EntityUtil.CheckArgumentNull(conditions, "conditions").ToList()); this.LineInfo = lineInfo; } internal readonly OM.ReadOnlyCollection Conditions; internal readonly OM.ReadOnlyCollection EntityTypes; internal readonly OM.ReadOnlyCollection IsOfTypeEntityTypes; internal readonly IXmlLineInfo LineInfo; /// /// Gets all (concrete) entity types implied by this type mapping. /// internal IEnumerableGetMappedEntityTypes(ItemCollection itemCollection) { const bool includeAbstractTypes = false; return this.EntityTypes.Concat( this.IsOfTypeEntityTypes.SelectMany(entityType => MetadataHelper.GetTypeAndSubtypesOf(entityType, itemCollection, includeAbstractTypes) .Cast ())); } internal IEnumerable GetDiscriminatorColumns() { return this.Conditions.Select(condition => condition.ColumnName); } } internal abstract class FunctionImportEntityTypeMappingCondition { protected FunctionImportEntityTypeMappingCondition(string columnName) { this.ColumnName = EntityUtil.CheckArgumentNull(columnName, "columnName"); } internal abstract ValueCondition ConditionValue { get; } internal readonly string ColumnName; internal abstract bool ColumnValueMatchesCondition(object columnValue); public override string ToString() { return this.ConditionValue.ToString(); } } internal sealed class FunctionImportEntityTypeMappingConditionValue : FunctionImportEntityTypeMappingCondition { internal FunctionImportEntityTypeMappingConditionValue(string columnName, XPathNavigator columnValue) : base(columnName) { this._xPathValue = EntityUtil.CheckArgumentNull(columnValue, "columnValue"); this._convertedValues = new Memoizer (this.GetConditionValue, null); } private readonly XPathNavigator _xPathValue; private readonly Memoizer _convertedValues; internal override ValueCondition ConditionValue { get { return new ValueCondition(_xPathValue.Value); } } internal override bool ColumnValueMatchesCondition(object columnValue) { if (null == columnValue || Convert.IsDBNull(columnValue)) { // only FunctionImportEntityTypeMappingConditionIsNull can match a null // column value return false; } Type columnValueType = columnValue.GetType(); // check if we've interpreted this column type yet object conditionValue = _convertedValues.Evaluate(columnValueType); return CdpEqualityComparer.DefaultEqualityComparer.Equals(columnValue, conditionValue); } private object GetConditionValue(Type columnValueType) { object conditionValue; // check that the type is supported PrimitiveType primitiveType; if (!ClrProviderManifest.Instance.TryGetPrimitiveType(columnValueType, out primitiveType) || !StorageMappingItemLoader.IsTypeSupportedForCondition(primitiveType.PrimitiveTypeKind)) { throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_UnsupportedType( this.ColumnName, columnValueType.FullName)); } try { conditionValue = _xPathValue.ValueAs(columnValueType); } catch (FormatException) { throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_ConditionValueTypeMismatch( StorageMslConstructs.FunctionImportMappingElement, this.ColumnName, columnValueType.FullName)); } return conditionValue; } } internal sealed class FunctionImportEntityTypeMappingConditionIsNull : FunctionImportEntityTypeMappingCondition { internal FunctionImportEntityTypeMappingConditionIsNull(string columnName, bool isNull) : base(columnName) { this.IsNull = isNull; } internal readonly bool IsNull; internal override ValueCondition ConditionValue { get { return IsNull ? ValueCondition.IsNull : ValueCondition.IsNotNull; } } internal override bool ColumnValueMatchesCondition(object columnValue) { bool valueIsNull = null == columnValue || Convert.IsDBNull(columnValue); return valueIsNull == this.IsNull; } } /// /// Represents a simple value condition of the form (value IS NULL), (value IS NOT NULL) /// or (value EQ X). Supports IEquatable(Of ValueCondition) so that equivalent conditions /// can be identified. /// internal class ValueCondition : IEquatable{ internal readonly string Description; internal readonly bool IsSentinel; internal const string IsNullDescription = "NULL"; internal const string IsNotNullDescription = "NOT NULL"; internal const string IsOtherDescription = "OTHER"; internal readonly static ValueCondition IsNull = new ValueCondition(IsNullDescription, true); internal readonly static ValueCondition IsNotNull = new ValueCondition(IsNotNullDescription, true); internal readonly static ValueCondition IsOther = new ValueCondition(IsOtherDescription, true); private ValueCondition(string description, bool isSentinel) { Description = description; IsSentinel = isSentinel; } internal ValueCondition(string description) : this(description, false) { } internal bool IsNotNullCondition { get { return object.ReferenceEquals(this, IsNotNull); } } public bool Equals(ValueCondition other) { return other.IsSentinel == this.IsSentinel && other.Description == this.Description; } public override int GetHashCode() { return Description.GetHashCode(); } public override string ToString() { return this.Description; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProtocolsConfigurationEntry.cs
- RuntimeArgumentHandle.cs
- ClientBuildManagerCallback.cs
- PersistenceProviderFactory.cs
- ErrorHandler.cs
- LogLogRecord.cs
- ConnectionPoolManager.cs
- CodeTypeReferenceCollection.cs
- AutomationIdentifier.cs
- SmiXetterAccessMap.cs
- IApplicationTrustManager.cs
- SQLResource.cs
- CodeSubDirectory.cs
- Symbol.cs
- IDictionary.cs
- QueueProcessor.cs
- WindowsSolidBrush.cs
- FieldMetadata.cs
- AnchorEditor.cs
- HostUtils.cs
- Vector3D.cs
- ToolStripSettings.cs
- XmlDocument.cs
- DefaultMemberAttribute.cs
- FloaterBaseParaClient.cs
- MdiWindowListStrip.cs
- UserControl.cs
- StringValidator.cs
- Timer.cs
- HttpRequest.cs
- TogglePatternIdentifiers.cs
- ObjectCloneHelper.cs
- Int64AnimationBase.cs
- DataGridViewRowConverter.cs
- StyleTypedPropertyAttribute.cs
- AssociationTypeEmitter.cs
- Int32Rect.cs
- EditorPartCollection.cs
- AppModelKnownContentFactory.cs
- Vector3DConverter.cs
- NativeObjectSecurity.cs
- ResourceExpressionEditor.cs
- StateMachine.cs
- WindowsPrincipal.cs
- SessionPageStatePersister.cs
- WorkerRequest.cs
- UIntPtr.cs
- XmlSchemaDocumentation.cs
- ContravarianceAdapter.cs
- RelationshipSet.cs
- SafeProcessHandle.cs
- HScrollProperties.cs
- X509CertificateStore.cs
- TdsRecordBufferSetter.cs
- HtmlTextArea.cs
- COM2IDispatchConverter.cs
- smtpconnection.cs
- PipeStream.cs
- XhtmlBasicSelectionListAdapter.cs
- CryptoApi.cs
- Properties.cs
- AnimationClock.cs
- SessionEndedEventArgs.cs
- ToolStripDropDownClosingEventArgs.cs
- AutomationPropertyInfo.cs
- SerialErrors.cs
- LifetimeServices.cs
- SafePEFileHandle.cs
- ConfigurationException.cs
- DataFieldEditor.cs
- BufferModesCollection.cs
- FrameworkRichTextComposition.cs
- AlternateViewCollection.cs
- StringWriter.cs
- DataBoundControl.cs
- PrtCap_Reader.cs
- DataGridPagerStyle.cs
- OdbcUtils.cs
- XmlToDatasetMap.cs
- Line.cs
- SettingsPropertyValueCollection.cs
- PrintDialogException.cs
- EdmToObjectNamespaceMap.cs
- Exceptions.cs
- PackWebResponse.cs
- ResourceIDHelper.cs
- PointIndependentAnimationStorage.cs
- DSACryptoServiceProvider.cs
- Accessors.cs
- ConfigurationCollectionAttribute.cs
- RangeContentEnumerator.cs
- ViewgenContext.cs
- oledbconnectionstring.cs
- StreamWithDictionary.cs
- SqlCacheDependencyDatabaseCollection.cs
- SqlCommandSet.cs
- ReliableChannelListener.cs
- ConfigurationValidatorBase.cs
- Grid.cs
- SecurityManager.cs