Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Mapping / FunctionImportMapping.cs / 2 / 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,
IEnumerable entityTypeMappings, 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.ReadOnlyCollection MappedEntityTypes;
///
/// Gets a list of all discriminator columns used in this mapping.
///
internal readonly OM.ReadOnlyCollection DiscriminatorColumns;
///
/// Gets normalized representation of all EntityTypeMapping fragments for this
/// function import mapping.
///
internal readonly OM.ReadOnlyCollection NormalizedEntityTypeMappings;
///
/// Gets line infos for entity type mappings.
///
internal readonly KeyToListMap EntityTypeLineInfos;
///
/// Gets line infos for IsTypeOf mappings for entity types.
///
internal readonly KeyToListMap IsTypeOfLineInfos;
///
/// 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 KeyToListMap unreachableEntityTypes,
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.
Dictionary columns = 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 Set FindReachableTypes(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.ReadOnlyCollection ColumnConditions;
///
/// 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(IEnumerable isOfTypeEntityTypes,
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 IEnumerable GetMappedEntityTypes(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,
IEnumerable entityTypeMappings, 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.ReadOnlyCollection MappedEntityTypes;
///
/// Gets a list of all discriminator columns used in this mapping.
///
internal readonly OM.ReadOnlyCollection DiscriminatorColumns;
///
/// Gets normalized representation of all EntityTypeMapping fragments for this
/// function import mapping.
///
internal readonly OM.ReadOnlyCollection NormalizedEntityTypeMappings;
///
/// Gets line infos for entity type mappings.
///
internal readonly KeyToListMap EntityTypeLineInfos;
///
/// Gets line infos for IsTypeOf mappings for entity types.
///
internal readonly KeyToListMap IsTypeOfLineInfos;
///
/// 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 KeyToListMap unreachableEntityTypes,
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.
Dictionary columns = 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 Set FindReachableTypes(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.ReadOnlyCollection ColumnConditions;
///
/// 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(IEnumerable isOfTypeEntityTypes,
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 IEnumerable GetMappedEntityTypes(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
- FeatureManager.cs
- BuilderPropertyEntry.cs
- TextTreeTextElementNode.cs
- NetCodeGroup.cs
- LineSegment.cs
- DrawListViewColumnHeaderEventArgs.cs
- MeasurementDCInfo.cs
- InteropAutomationProvider.cs
- SqlDataSourceFilteringEventArgs.cs
- EnvironmentPermission.cs
- ResXResourceSet.cs
- ResourceProviderFactory.cs
- _NegotiateClient.cs
- PerspectiveCamera.cs
- NullableIntMinMaxAggregationOperator.cs
- SafeFileMapViewHandle.cs
- AdRotator.cs
- ClientConvert.cs
- SystemIPv4InterfaceProperties.cs
- ConnectionManagementSection.cs
- PropertyMapper.cs
- UserControlCodeDomTreeGenerator.cs
- SendActivity.cs
- GroupJoinQueryOperator.cs
- DesignTableCollection.cs
- SessionIDManager.cs
- Size3DValueSerializer.cs
- SecuritySessionSecurityTokenAuthenticator.cs
- TextBoxAutomationPeer.cs
- BaseUriHelper.cs
- VolatileEnlistmentMultiplexing.cs
- GetPageNumberCompletedEventArgs.cs
- LayoutDump.cs
- DataReceivedEventArgs.cs
- SqlDataSourceFilteringEventArgs.cs
- DictionaryBase.cs
- MimeWriter.cs
- ColorAnimationUsingKeyFrames.cs
- ExecutionScope.cs
- RSAOAEPKeyExchangeDeformatter.cs
- TrustLevel.cs
- BitmapEffectDrawing.cs
- SpecialFolderEnumConverter.cs
- ThemeDictionaryExtension.cs
- xdrvalidator.cs
- WindowProviderWrapper.cs
- RSAOAEPKeyExchangeDeformatter.cs
- RegexRunnerFactory.cs
- WindowInteropHelper.cs
- PageAdapter.cs
- UrlMappingCollection.cs
- WebPartEditorApplyVerb.cs
- ConnectionStringSettings.cs
- hwndwrapper.cs
- ServiceNameCollection.cs
- PolyLineSegmentFigureLogic.cs
- PropertyGridCommands.cs
- CultureInfo.cs
- RawKeyboardInputReport.cs
- EtwTrace.cs
- securestring.cs
- AsymmetricKeyExchangeDeformatter.cs
- _ListenerAsyncResult.cs
- MetadataArtifactLoaderFile.cs
- CompilerGeneratedAttribute.cs
- FirstMatchCodeGroup.cs
- DynamicResourceExtensionConverter.cs
- XhtmlConformanceSection.cs
- Validator.cs
- ClientBuildManagerCallback.cs
- Input.cs
- ToolStripPanelRow.cs
- bindurihelper.cs
- SoapTypeAttribute.cs
- TableLayoutCellPaintEventArgs.cs
- __ComObject.cs
- Mappings.cs
- OdbcConnectionFactory.cs
- SqlTypeSystemProvider.cs
- Rijndael.cs
- FontFamily.cs
- Stacktrace.cs
- CanExecuteRoutedEventArgs.cs
- WinFormsSecurity.cs
- RtfToXamlLexer.cs
- COM2FontConverter.cs
- PersonalizationAdministration.cs
- MetabaseServerConfig.cs
- PolicyStatement.cs
- SafeRightsManagementPubHandle.cs
- AbstractExpressions.cs
- CodeGenerator.cs
- Point4DConverter.cs
- IsolationInterop.cs
- TableLayoutPanel.cs
- ExpandCollapseProviderWrapper.cs
- SmtpNegotiateAuthenticationModule.cs
- ToolboxCategory.cs
- SqlNodeTypeOperators.cs
- MasterPage.cs