Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / FunctionImportMapping.cs
blob6c3a297b64a2ad2d7c9dd1d54a5eda81917a964c
1 //---------------------------------------------------------------------
2 // <copyright file="FunctionImportMapping.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner willa
8 //---------------------------------------------------------------------
10 namespace System.Data.Mapping
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.Data.Common.Utils;
15 using System.Data.Common.Utils.Boolean;
16 using System.Data.Entity;
17 using System.Data.Metadata.Edm;
18 using System.Diagnostics;
19 using System.Globalization;
20 using System.Linq;
21 using System.Xml;
22 using System.Xml.XPath;
23 using OM = System.Collections.ObjectModel;
25 /// <summary>
26 /// Represents a mapping from a model function import to a store composable or non-composable function.
27 /// </summary>
28 internal abstract class FunctionImportMapping
30 internal FunctionImportMapping(EdmFunction functionImport, EdmFunction targetFunction)
32 this.FunctionImport = EntityUtil.CheckArgumentNull(functionImport, "functionImport");
33 this.TargetFunction = EntityUtil.CheckArgumentNull(targetFunction, "targetFunction");
36 /// <summary>
37 /// Gets model function (or source of the mapping)
38 /// </summary>
39 internal readonly EdmFunction FunctionImport;
41 /// <summary>
42 /// Gets store function (or target of the mapping)
43 /// </summary>
44 internal readonly EdmFunction TargetFunction;
47 internal sealed class FunctionImportStructuralTypeMappingKB
49 internal FunctionImportStructuralTypeMappingKB(
50 IEnumerable<FunctionImportStructuralTypeMapping> structuralTypeMappings,
51 ItemCollection itemCollection)
53 EntityUtil.CheckArgumentNull(structuralTypeMappings, "structuralTypeMappings");
54 m_itemCollection = EntityUtil.CheckArgumentNull(itemCollection, "itemCollection");
56 // If no specific type mapping.
57 if (structuralTypeMappings.Count() == 0)
59 // Initialize with defaults.
60 this.ReturnTypeColumnsRenameMapping = new Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping>();
61 this.NormalizedEntityTypeMappings = new OM.ReadOnlyCollection<FunctionImportNormalizedEntityTypeMapping>(new List<FunctionImportNormalizedEntityTypeMapping>());
62 this.DiscriminatorColumns = new OM.ReadOnlyCollection<string>(new List<string>());
63 this.MappedEntityTypes = new OM.ReadOnlyCollection<EntityType>(new List<EntityType>());
64 return;
67 IEnumerable<FunctionImportEntityTypeMapping> entityTypeMappings = structuralTypeMappings.OfType<FunctionImportEntityTypeMapping>();
69 // FunctionImportEntityTypeMapping
70 if (null != entityTypeMappings && null != entityTypeMappings.FirstOrDefault<FunctionImportEntityTypeMapping>())
72 var isOfTypeEntityTypeColumnsRenameMapping = new Dictionary<EntityType, OM.Collection<FunctionImportReturnTypePropertyMapping>>();
73 var entityTypeColumnsRenameMapping = new Dictionary<EntityType, OM.Collection<FunctionImportReturnTypePropertyMapping>>();
74 var normalizedEntityTypeMappings = new List<FunctionImportNormalizedEntityTypeMapping>();
76 // Collect all mapped entity types.
77 this.MappedEntityTypes = entityTypeMappings
78 .SelectMany(mapping => mapping.GetMappedEntityTypes(m_itemCollection))
79 .Distinct()
80 .ToList()
81 .AsReadOnly();
83 // Collect all discriminator columns.
84 this.DiscriminatorColumns = entityTypeMappings
85 .SelectMany(mapping => mapping.GetDiscriminatorColumns())
86 .Distinct()
87 .ToList()
88 .AsReadOnly();
90 m_entityTypeLineInfos = new KeyToListMap<EntityType, LineInfo>(EqualityComparer<EntityType>.Default);
91 m_isTypeOfLineInfos = new KeyToListMap<EntityType, LineInfo>(EqualityComparer<EntityType>.Default);
93 foreach (var entityTypeMapping in entityTypeMappings)
95 // Remember LineInfos for error reporting.
96 foreach (var entityType in entityTypeMapping.EntityTypes)
98 m_entityTypeLineInfos.Add(entityType, entityTypeMapping.LineInfo);
100 foreach (var isTypeOf in entityTypeMapping.IsOfTypeEntityTypes)
102 m_isTypeOfLineInfos.Add(isTypeOf, entityTypeMapping.LineInfo);
105 // Create map from column name to condition.
106 var columnMap = entityTypeMapping.Conditions.ToDictionary(
107 condition => condition.ColumnName,
108 condition => condition);
110 // Align conditions with discriminator columns.
111 var columnMappings = new List<FunctionImportEntityTypeMappingCondition>(this.DiscriminatorColumns.Count);
112 for (int i = 0; i < this.DiscriminatorColumns.Count; i++)
114 string discriminatorColumn = this.DiscriminatorColumns[i];
115 FunctionImportEntityTypeMappingCondition mappingCondition;
116 if (columnMap.TryGetValue(discriminatorColumn, out mappingCondition))
118 columnMappings.Add(mappingCondition);
120 else
122 // Null indicates the value for this discriminator doesn't matter.
123 columnMappings.Add(null);
127 // Create bit map for implied entity types.
128 bool[] impliedEntityTypesBitMap = new bool[this.MappedEntityTypes.Count];
129 var impliedEntityTypesSet = new Set<EntityType>(entityTypeMapping.GetMappedEntityTypes(m_itemCollection));
130 for (int i = 0; i < this.MappedEntityTypes.Count; i++)
132 impliedEntityTypesBitMap[i] = impliedEntityTypesSet.Contains(this.MappedEntityTypes[i]);
135 // Construct normalized mapping.
136 normalizedEntityTypeMappings.Add(new FunctionImportNormalizedEntityTypeMapping(this, columnMappings, new BitArray(impliedEntityTypesBitMap)));
138 // Construct the rename mappings by adding isTypeOf types and specific entity types to the corresponding lists.
139 foreach (var isOfType in entityTypeMapping.IsOfTypeEntityTypes)
141 if (!isOfTypeEntityTypeColumnsRenameMapping.Keys.Contains(isOfType))
143 isOfTypeEntityTypeColumnsRenameMapping.Add(isOfType, new OM.Collection<FunctionImportReturnTypePropertyMapping>());
145 foreach (var rename in entityTypeMapping.ColumnsRenameList)
147 isOfTypeEntityTypeColumnsRenameMapping[isOfType].Add(rename);
150 foreach (var entityType in entityTypeMapping.EntityTypes)
152 if (!entityTypeColumnsRenameMapping.Keys.Contains(entityType))
154 entityTypeColumnsRenameMapping.Add(entityType, new OM.Collection<FunctionImportReturnTypePropertyMapping>());
156 foreach (var rename in entityTypeMapping.ColumnsRenameList)
158 entityTypeColumnsRenameMapping[entityType].Add(rename);
163 this.ReturnTypeColumnsRenameMapping = new FunctionImportReturnTypeEntityTypeColumnsRenameBuilder(isOfTypeEntityTypeColumnsRenameMapping,
164 entityTypeColumnsRenameMapping)
165 .ColumnRenameMapping;
167 this.NormalizedEntityTypeMappings = new OM.ReadOnlyCollection<FunctionImportNormalizedEntityTypeMapping>(
168 normalizedEntityTypeMappings);
170 else
172 // FunctionImportComplexTypeMapping
173 Debug.Assert(structuralTypeMappings.First() is FunctionImportComplexTypeMapping, "only two types can have renames, complexType and entityType");
174 IEnumerable<FunctionImportComplexTypeMapping> complexTypeMappings = structuralTypeMappings.Cast<FunctionImportComplexTypeMapping>();
176 Debug.Assert(complexTypeMappings.Count() == 1, "how come there are more than 1, complex type cannot derive from other complex type");
178 this.ReturnTypeColumnsRenameMapping = new Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping>();
179 foreach (var rename in complexTypeMappings.First().ColumnsRenameList)
181 FunctionImportReturnTypeStructuralTypeColumnRenameMapping columnRenameMapping = new FunctionImportReturnTypeStructuralTypeColumnRenameMapping(rename.CMember);
182 columnRenameMapping.AddRename(new FunctionImportReturnTypeStructuralTypeColumn(
183 rename.SColumn,
184 complexTypeMappings.First().ReturnType,
185 false,
186 rename.LineInfo));
187 this.ReturnTypeColumnsRenameMapping.Add(rename.CMember, columnRenameMapping);
190 // Initialize the entity mapping data as empty.
191 this.NormalizedEntityTypeMappings = new OM.ReadOnlyCollection<FunctionImportNormalizedEntityTypeMapping>(new List<FunctionImportNormalizedEntityTypeMapping>());
192 this.DiscriminatorColumns = new OM.ReadOnlyCollection<string>(new List<string>() { });
193 this.MappedEntityTypes = new OM.ReadOnlyCollection<EntityType>(new List<EntityType>() { });
197 private readonly ItemCollection m_itemCollection;
198 private readonly KeyToListMap<EntityType, LineInfo> m_entityTypeLineInfos;
199 private readonly KeyToListMap<EntityType, LineInfo> m_isTypeOfLineInfos;
201 /// <summary>
202 /// Gets all types in scope for this mapping.
203 /// </summary>
204 internal readonly OM.ReadOnlyCollection<EntityType> MappedEntityTypes;
206 /// <summary>
207 /// Gets a list of all discriminator columns used in this mapping.
208 /// </summary>
209 internal readonly OM.ReadOnlyCollection<string> DiscriminatorColumns;
211 /// <summary>
212 /// Gets normalized representation of all EntityTypeMapping fragments for this
213 /// function import mapping.
214 /// </summary>
215 internal readonly OM.ReadOnlyCollection<FunctionImportNormalizedEntityTypeMapping> NormalizedEntityTypeMappings;
217 /// <summary>
218 /// Get the columns rename mapping for return type, the first string is the member name
219 /// the second one is column names for different types that mentioned in the mapping.
220 /// </summary>
221 internal readonly Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping> ReturnTypeColumnsRenameMapping;
223 internal bool ValidateTypeConditions(bool validateAmbiguity, IList<EdmSchemaError> errors, string sourceLocation)
225 // Verify that all types can be produced
226 KeyToListMap<EntityType, LineInfo> unreachableEntityTypes;
227 KeyToListMap<EntityType, LineInfo> unreachableIsTypeOfs;
228 GetUnreachableTypes(validateAmbiguity, out unreachableEntityTypes, out unreachableIsTypeOfs);
230 bool valid = true;
231 foreach (var unreachableEntityType in unreachableEntityTypes.KeyValuePairs)
233 var lineInfo = unreachableEntityType.Value.First();
234 string lines = StringUtil.ToCommaSeparatedString(unreachableEntityType.Value.Select(li => li.LineNumber));
235 EdmSchemaError error = new EdmSchemaError(
236 Strings.Mapping_FunctionImport_UnreachableType(unreachableEntityType.Key.FullName, lines),
237 (int)StorageMappingErrorCode.MappingFunctionImportAmbiguousTypeConditions,
238 EdmSchemaErrorSeverity.Error,
239 sourceLocation,
240 lineInfo.LineNumber,
241 lineInfo.LinePosition);
242 errors.Add(error);
243 valid = false;
245 foreach (var unreachableIsTypeOf in unreachableIsTypeOfs.KeyValuePairs)
247 var lineInfo = unreachableIsTypeOf.Value.First();
248 string lines = StringUtil.ToCommaSeparatedString(unreachableIsTypeOf.Value.Select(li => li.LineNumber));
249 string isTypeOfDescription = StorageMslConstructs.IsTypeOf + unreachableIsTypeOf.Key.FullName + StorageMslConstructs.IsTypeOfTerminal;
250 EdmSchemaError error = new EdmSchemaError(
251 Strings.Mapping_FunctionImport_UnreachableIsTypeOf(isTypeOfDescription, lines),
252 (int)StorageMappingErrorCode.MappingFunctionImportAmbiguousTypeConditions,
253 EdmSchemaErrorSeverity.Error,
254 sourceLocation,
255 lineInfo.LineNumber,
256 lineInfo.LinePosition);
257 errors.Add(error);
258 valid = false;
261 return valid;
264 /// <summary>
265 /// Determines which explicitly mapped types in the function import mapping cannot be generated.
266 /// For IsTypeOf declarations, reports if no type in hierarchy can be produced.
267 ///
268 /// Works by:
269 ///
270 /// - Converting type mapping conditions into vertices
271 /// - Checking that some assignment satisfies
272 /// </summary>
273 private void GetUnreachableTypes(
274 bool validateAmbiguity,
275 out KeyToListMap<EntityType, LineInfo> unreachableEntityTypes,
276 out KeyToListMap<EntityType, LineInfo> unreachableIsTypeOfs)
278 // Contains, for each DiscriminatorColumn, a domain variable where the domain values are
279 // integers representing the ordinal within discriminatorDomains.
280 DomainVariable<string, ValueCondition>[] variables = ConstructDomainVariables();
282 // Convert type mapping conditions to decision diagram vertices.
283 var converter = new DomainConstraintConversionContext<string, ValueCondition>();
284 Vertex[] mappingConditions = ConvertMappingConditionsToVertices(converter, variables);
286 // Find reachable types.
287 Set<EntityType> reachableTypes = validateAmbiguity ?
288 FindUnambiguouslyReachableTypes(converter, mappingConditions) :
289 FindReachableTypes(converter, mappingConditions);
291 CollectUnreachableTypes(reachableTypes, out unreachableEntityTypes, out unreachableIsTypeOfs);
294 private DomainVariable<string, ValueCondition>[] ConstructDomainVariables()
296 // Determine domain for each discriminator column, including "other" and "null" placeholders.
297 var discriminatorDomains = new Set<ValueCondition>[this.DiscriminatorColumns.Count];
298 for (int i = 0; i < discriminatorDomains.Length; i++)
300 discriminatorDomains[i] = new Set<ValueCondition>();
301 discriminatorDomains[i].Add(ValueCondition.IsOther);
302 discriminatorDomains[i].Add(ValueCondition.IsNull);
305 // Collect all domain values.
306 foreach (var typeMapping in this.NormalizedEntityTypeMappings)
308 for (int i = 0; i < this.DiscriminatorColumns.Count; i++)
310 var discriminatorValue = typeMapping.ColumnConditions[i];
311 if (null != discriminatorValue &&
312 !discriminatorValue.ConditionValue.IsNotNullCondition) // NotNull is a special range (everything but IsNull)
314 discriminatorDomains[i].Add(discriminatorValue.ConditionValue);
319 var discriminatorVariables = new DomainVariable<string, ValueCondition>[discriminatorDomains.Length];
320 for (int i = 0; i < discriminatorVariables.Length; i++)
322 // domain variable is identified by the column name and takes all collected domain values
323 discriminatorVariables[i] = new DomainVariable<string, ValueCondition>(
324 this.DiscriminatorColumns[i], discriminatorDomains[i].MakeReadOnly());
327 return discriminatorVariables;
330 private Vertex[] ConvertMappingConditionsToVertices(
331 ConversionContext<DomainConstraint<string, ValueCondition>> converter,
332 DomainVariable<string, ValueCondition>[] variables)
334 Vertex[] conditions = new Vertex[this.NormalizedEntityTypeMappings.Count];
335 for (int i = 0; i < conditions.Length; i++)
337 var typeMapping = this.NormalizedEntityTypeMappings[i];
339 // create conjunction representing the condition
340 Vertex condition = Vertex.One;
341 for (int j = 0; j < this.DiscriminatorColumns.Count; j++)
343 var columnCondition = typeMapping.ColumnConditions[j];
344 if (null != columnCondition)
346 var conditionValue = columnCondition.ConditionValue;
347 if (conditionValue.IsNotNullCondition)
349 // the 'not null' condition is not actually part of the domain (since it
350 // covers other elements), so create a Not(value in {null}) condition
351 var isNull = new TermExpr<DomainConstraint<string, ValueCondition>>(
352 new DomainConstraint<string, ValueCondition>(variables[j], ValueCondition.IsNull));
353 Vertex isNullVertex = converter.TranslateTermToVertex(isNull);
354 condition = converter.Solver.And(condition, converter.Solver.Not(isNullVertex));
356 else
358 var hasValue = new TermExpr<DomainConstraint<string, ValueCondition>>(
359 new DomainConstraint<string, ValueCondition>(variables[j], conditionValue));
360 condition = converter.Solver.And(condition, converter.TranslateTermToVertex(hasValue));
364 conditions[i] = condition;
366 return conditions;
369 /// <summary>
370 /// Determines which types are produced by this mapping.
371 /// </summary>
372 private Set<EntityType> FindReachableTypes(DomainConstraintConversionContext<string, ValueCondition> converter, Vertex[] mappingConditions)
374 // For each entity type, create a candidate function that evaluates to true given
375 // discriminator assignments iff. all of that type's conditions evaluate to true
376 // and its negative conditions evaluate to false.
377 Vertex[] candidateFunctions = new Vertex[this.MappedEntityTypes.Count];
378 for (int i = 0; i < candidateFunctions.Length; i++)
380 // Seed the candidate function conjunction with 'true'.
381 Vertex candidateFunction = Vertex.One;
382 for (int j = 0; j < this.NormalizedEntityTypeMappings.Count; j++)
384 var entityTypeMapping = this.NormalizedEntityTypeMappings[j];
386 // Determine if this mapping is a positive or negative case for the current type.
387 if (entityTypeMapping.ImpliedEntityTypes[i])
389 candidateFunction = converter.Solver.And(candidateFunction, mappingConditions[j]);
391 else
393 candidateFunction = converter.Solver.And(candidateFunction, converter.Solver.Not(mappingConditions[j]));
396 candidateFunctions[i] = candidateFunction;
399 // Make sure that for each type there is an assignment that resolves to only that type.
400 var reachableTypes = new Set<EntityType>();
401 for (int i = 0; i < candidateFunctions.Length; i++)
403 // Create a function that evaluates to true iff. the current candidate function is true
404 // and every other candidate function is false.
405 Vertex isExactlyThisTypeCondition = converter.Solver.And(
406 candidateFunctions.Select((typeCondition, ordinal) => ordinal == i ?
407 typeCondition :
408 converter.Solver.Not(typeCondition)));
410 // If the above conjunction is satisfiable, it means some row configuration exists producing the type.
411 if (!isExactlyThisTypeCondition.IsZero())
413 reachableTypes.Add(this.MappedEntityTypes[i]);
417 return reachableTypes;
420 /// <summary>
421 /// Determines which types are produced by this mapping.
422 /// </summary>
423 private Set<EntityType> FindUnambiguouslyReachableTypes(DomainConstraintConversionContext<string, ValueCondition> converter, Vertex[] mappingConditions)
425 // For each entity type, create a candidate function that evaluates to true given
426 // discriminator assignments iff. all of that type's conditions evaluate to true.
427 Vertex[] candidateFunctions = new Vertex[this.MappedEntityTypes.Count];
428 for (int i = 0; i < candidateFunctions.Length; i++)
430 // Seed the candidate function conjunction with 'true'.
431 Vertex candidateFunction = Vertex.One;
432 for (int j = 0; j < this.NormalizedEntityTypeMappings.Count; j++)
434 var entityTypeMapping = this.NormalizedEntityTypeMappings[j];
436 // Determine if this mapping is a positive or negative case for the current type.
437 if (entityTypeMapping.ImpliedEntityTypes[i])
439 candidateFunction = converter.Solver.And(candidateFunction, mappingConditions[j]);
442 candidateFunctions[i] = candidateFunction;
445 // Make sure that for each type with satisfiable candidateFunction all assignments for the type resolve to only that type.
446 var unambigouslyReachableMap = new BitArray(candidateFunctions.Length, true);
447 for (int i = 0; i < candidateFunctions.Length; ++i)
449 if (candidateFunctions[i].IsZero())
451 // The i-th type is unreachable regardless of other types.
452 unambigouslyReachableMap[i] = false;
454 else
456 for (int j = i + 1; j < candidateFunctions.Length; ++j)
458 if (!converter.Solver.And(candidateFunctions[i], candidateFunctions[j]).IsZero())
460 // The i-th and j-th types have common assignments, hence they aren't unambiguously reachable.
461 unambigouslyReachableMap[i] = false;
462 unambigouslyReachableMap[j] = false;
467 var reachableTypes = new Set<EntityType>();
468 for (int i = 0; i < candidateFunctions.Length; ++i)
470 if (unambigouslyReachableMap[i])
472 reachableTypes.Add(this.MappedEntityTypes[i]);
476 return reachableTypes;
479 private void CollectUnreachableTypes(Set<EntityType> reachableTypes, out KeyToListMap<EntityType, LineInfo> entityTypes, out KeyToListMap<EntityType, LineInfo> isTypeOfEntityTypes)
481 // Collect line infos for types in violation
482 entityTypes = new KeyToListMap<EntityType, LineInfo>(EqualityComparer<EntityType>.Default);
483 isTypeOfEntityTypes = new KeyToListMap<EntityType, LineInfo>(EqualityComparer<EntityType>.Default);
485 if (reachableTypes.Count == this.MappedEntityTypes.Count)
487 // All types are reachable; nothing to check
488 return;
491 // Find IsTypeOf mappings where no type in hierarchy can generate a row
492 foreach (var isTypeOf in m_isTypeOfLineInfos.Keys)
494 if (!MetadataHelper.GetTypeAndSubtypesOf(isTypeOf, m_itemCollection, false)
495 .Cast<EntityType>()
496 .Intersect(reachableTypes)
497 .Any())
499 // no type in the hierarchy is reachable...
500 isTypeOfEntityTypes.AddRange(isTypeOf, m_isTypeOfLineInfos.EnumerateValues(isTypeOf));
504 // Find explicit types not generating a value
505 foreach (var entityType in m_entityTypeLineInfos.Keys)
507 if (!reachableTypes.Contains(entityType))
509 entityTypes.AddRange(entityType, m_entityTypeLineInfos.EnumerateValues(entityType));
515 internal sealed class FunctionImportNormalizedEntityTypeMapping
517 internal FunctionImportNormalizedEntityTypeMapping(FunctionImportStructuralTypeMappingKB parent,
518 List<FunctionImportEntityTypeMappingCondition> columnConditions, BitArray impliedEntityTypes)
520 // validate arguments
521 EntityUtil.CheckArgumentNull(parent, "parent");
522 EntityUtil.CheckArgumentNull(columnConditions, "discriminatorValues");
523 EntityUtil.CheckArgumentNull(impliedEntityTypes, "impliedEntityTypes");
525 Debug.Assert(columnConditions.Count == parent.DiscriminatorColumns.Count,
526 "discriminator values must be ordinally aligned with discriminator columns");
527 Debug.Assert(impliedEntityTypes.Count == parent.MappedEntityTypes.Count,
528 "implied entity types must be ordinally aligned with mapped entity types");
530 this.ColumnConditions = new OM.ReadOnlyCollection<FunctionImportEntityTypeMappingCondition>(columnConditions.ToList());
531 this.ImpliedEntityTypes = impliedEntityTypes;
532 this.ComplementImpliedEntityTypes = (new BitArray(this.ImpliedEntityTypes)).Not();
535 /// <summary>
536 /// Gets discriminator values aligned with DiscriminatorColumns of the parent FunctionImportMapping.
537 /// A null ValueCondition indicates 'anything goes'.
538 /// </summary>
539 internal readonly OM.ReadOnlyCollection<FunctionImportEntityTypeMappingCondition> ColumnConditions;
541 /// <summary>
542 /// Gets bit array with 'true' indicating the corresponding MappedEntityType of the parent
543 /// FunctionImportMapping is implied by this fragment.
544 /// </summary>
545 internal readonly BitArray ImpliedEntityTypes;
547 /// <summary>
548 /// Gets the complement of the ImpliedEntityTypes BitArray.
549 /// </summary>
550 internal readonly BitArray ComplementImpliedEntityTypes;
552 public override string ToString()
554 return String.Format(CultureInfo.InvariantCulture, "Values={0}, Types={1}",
555 StringUtil.ToCommaSeparatedString(this.ColumnConditions), StringUtil.ToCommaSeparatedString(this.ImpliedEntityTypes));
559 internal abstract class FunctionImportEntityTypeMappingCondition
561 protected FunctionImportEntityTypeMappingCondition(string columnName, LineInfo lineInfo)
563 this.ColumnName = EntityUtil.CheckArgumentNull(columnName, "columnName");
564 this.LineInfo = lineInfo;
567 internal readonly string ColumnName;
568 internal readonly LineInfo LineInfo;
570 internal abstract ValueCondition ConditionValue { get; }
572 internal abstract bool ColumnValueMatchesCondition(object columnValue);
574 public override string ToString()
576 return this.ConditionValue.ToString();
580 internal sealed class FunctionImportEntityTypeMappingConditionValue : FunctionImportEntityTypeMappingCondition
582 internal FunctionImportEntityTypeMappingConditionValue(string columnName, XPathNavigator columnValue, LineInfo lineInfo)
583 : base(columnName, lineInfo)
585 this._xPathValue = EntityUtil.CheckArgumentNull(columnValue, "columnValue");
586 this._convertedValues = new Memoizer<Type, object>(this.GetConditionValue, null);
589 private readonly XPathNavigator _xPathValue;
590 private readonly Memoizer<Type, object> _convertedValues;
592 internal override ValueCondition ConditionValue
594 get { return new ValueCondition(_xPathValue.Value); }
597 internal override bool ColumnValueMatchesCondition(object columnValue)
599 if (null == columnValue || Convert.IsDBNull(columnValue))
601 // only FunctionImportEntityTypeMappingConditionIsNull can match a null
602 // column value
603 return false;
606 Type columnValueType = columnValue.GetType();
608 // check if we've interpreted this column type yet
609 object conditionValue = _convertedValues.Evaluate(columnValueType);
610 return ByValueEqualityComparer.Default.Equals(columnValue, conditionValue);
613 private object GetConditionValue(Type columnValueType)
615 return GetConditionValue(
616 columnValueType,
617 handleTypeNotComparable: () =>
619 throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_UnsupportedType(this.ColumnName, columnValueType.FullName));
621 handleInvalidConditionValue: () =>
623 throw EntityUtil.CommandExecution(Strings.Mapping_FunctionImport_ConditionValueTypeMismatch(StorageMslConstructs.FunctionImportMappingElement, this.ColumnName, columnValueType.FullName));
627 internal object GetConditionValue(Type columnValueType, Action handleTypeNotComparable, Action handleInvalidConditionValue)
629 // Check that the type is supported and comparable.
630 PrimitiveType primitiveType;
631 if (!ClrProviderManifest.Instance.TryGetPrimitiveType(columnValueType, out primitiveType) ||
632 !StorageMappingItemLoader.IsTypeSupportedForCondition(primitiveType.PrimitiveTypeKind))
634 handleTypeNotComparable();
635 return null;
640 return _xPathValue.ValueAs(columnValueType);
642 catch (FormatException)
644 handleInvalidConditionValue();
645 return null;
650 internal sealed class FunctionImportEntityTypeMappingConditionIsNull : FunctionImportEntityTypeMappingCondition
652 internal FunctionImportEntityTypeMappingConditionIsNull(string columnName, bool isNull, LineInfo lineInfo)
653 : base(columnName, lineInfo)
655 this.IsNull = isNull;
658 internal readonly bool IsNull;
660 internal override ValueCondition ConditionValue
662 get { return IsNull ? ValueCondition.IsNull : ValueCondition.IsNotNull; }
665 internal override bool ColumnValueMatchesCondition(object columnValue)
667 bool valueIsNull = null == columnValue || Convert.IsDBNull(columnValue);
668 return valueIsNull == this.IsNull;
672 /// <summary>
673 /// Represents a simple value condition of the form (value IS NULL), (value IS NOT NULL)
674 /// or (value EQ X). Supports IEquatable(Of ValueCondition) so that equivalent conditions
675 /// can be identified.
676 /// </summary>
677 internal class ValueCondition : IEquatable<ValueCondition>
679 internal readonly string Description;
680 internal readonly bool IsSentinel;
682 internal const string IsNullDescription = "NULL";
683 internal const string IsNotNullDescription = "NOT NULL";
684 internal const string IsOtherDescription = "OTHER";
686 internal readonly static ValueCondition IsNull = new ValueCondition(IsNullDescription, true);
687 internal readonly static ValueCondition IsNotNull = new ValueCondition(IsNotNullDescription, true);
688 internal readonly static ValueCondition IsOther = new ValueCondition(IsOtherDescription, true);
690 private ValueCondition(string description, bool isSentinel)
692 Description = description;
693 IsSentinel = isSentinel;
696 internal ValueCondition(string description)
697 : this(description, false)
701 internal bool IsNotNullCondition { get { return object.ReferenceEquals(this, IsNotNull); } }
703 public bool Equals(ValueCondition other)
705 return other.IsSentinel == this.IsSentinel &&
706 other.Description == this.Description;
709 public override int GetHashCode()
711 return Description.GetHashCode();
714 public override string ToString()
716 return this.Description;
720 internal sealed class LineInfo: IXmlLineInfo
722 private readonly bool m_hasLineInfo;
723 private readonly int m_lineNumber;
724 private readonly int m_linePosition;
726 internal LineInfo(XPathNavigator nav)
727 : this((IXmlLineInfo)nav)
730 internal LineInfo(IXmlLineInfo lineInfo)
732 m_hasLineInfo = lineInfo.HasLineInfo();
733 m_lineNumber = lineInfo.LineNumber;
734 m_linePosition = lineInfo.LinePosition;
737 internal static readonly LineInfo Empty = new LineInfo();
738 private LineInfo()
740 m_hasLineInfo = false;
741 m_lineNumber = default(int);
742 m_linePosition = default(int);
745 public int LineNumber
746 { get { return m_lineNumber; } }
748 public int LinePosition
749 { get { return m_linePosition; } }
751 public bool HasLineInfo()
753 return m_hasLineInfo;