|
//---------------------------------------------------------------------
// <copyright file="FunctionImportMappingNonComposable.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner willa
//---------------------------------------------------------------------
namespace System.Data.Mapping
{
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Common.Utils;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Linq;
using OM = System.Collections.ObjectModel;
/// <summary>
/// Represents a mapping from a model function import to a store non-composable function.
/// </summary>
internal sealed class FunctionImportMappingNonComposable : FunctionImportMapping
{
internal FunctionImportMappingNonComposable(
EdmFunction functionImport,
EdmFunction targetFunction,
List<List<FunctionImportStructuralTypeMapping>> structuralTypeMappingsList,
ItemCollection itemCollection)
: base(functionImport, targetFunction)
{
EntityUtil.CheckArgumentNull(structuralTypeMappingsList, "structuralTypeMappingsList");
EntityUtil.CheckArgumentNull(itemCollection, "itemCollection");
Debug.Assert(!functionImport.IsComposableAttribute, "!functionImport.IsComposableAttribute");
Debug.Assert(!targetFunction.IsComposableAttribute, "!targetFunction.IsComposableAttribute");
if (structuralTypeMappingsList.Count == 0)
{
this.ResultMappings = new OM.ReadOnlyCollection<FunctionImportStructuralTypeMappingKB>(
new FunctionImportStructuralTypeMappingKB[] {
new FunctionImportStructuralTypeMappingKB(new List<FunctionImportStructuralTypeMapping>(), itemCollection) });
this.noExplicitResultMappings = true;
}
else
{
Debug.Assert(functionImport.ReturnParameters.Count == structuralTypeMappingsList.Count);
this.ResultMappings = new OM.ReadOnlyCollection<FunctionImportStructuralTypeMappingKB>(
EntityUtil.CheckArgumentNull(structuralTypeMappingsList, "structuralTypeMappingsList")
.Select((structuralTypeMappings) => new FunctionImportStructuralTypeMappingKB(
EntityUtil.CheckArgumentNull(structuralTypeMappings, "structuralTypeMappings"),
itemCollection))
.ToArray());
this.noExplicitResultMappings = false;
}
}
private bool noExplicitResultMappings;
/// <summary>
/// Gets function import return type mapping knowledge bases.
/// </summary>
internal readonly OM.ReadOnlyCollection<FunctionImportStructuralTypeMappingKB> ResultMappings;
/// <summary>
/// If no return mappings were specified in the MSL return an empty return type mapping knowledge base.
/// Otherwise return the resultSetIndexth return type mapping knowledge base, or throw if resultSetIndex is out of range
/// </summary>
internal FunctionImportStructuralTypeMappingKB GetResultMapping(int resultSetIndex)
{
Debug.Assert(resultSetIndex >= 0, "resultSetIndex >= 0");
if (this.noExplicitResultMappings)
{
Debug.Assert(this.ResultMappings.Count == 1, "this.ResultMappings.Count == 1");
return this.ResultMappings[0];
}
else
{
if (ResultMappings.Count <= resultSetIndex)
{
EntityUtil.ThrowArgumentOutOfRangeException("resultSetIndex");
}
return this.ResultMappings[resultSetIndex];
}
}
/// <summary>
/// Gets the disctriminator columns resultSetIndexth result set, or an empty array if the index is not in range
/// </summary>
internal IList<string> GetDiscriminatorColumns(int resultSetIndex)
{
FunctionImportStructuralTypeMappingKB resultMapping = this.GetResultMapping(resultSetIndex);
return resultMapping.DiscriminatorColumns;
}
/// <summary>
/// Given discriminator values (ordinally aligned with DiscriminatorColumns), determines
/// the entity type to return. Throws a CommandExecutionException if the type is ambiguous.
/// </summary>
internal EntityType Discriminate(object[] discriminatorValues, int resultSetIndex)
{
FunctionImportStructuralTypeMappingKB resultMapping = this.GetResultMapping(resultSetIndex);
Debug.Assert (resultMapping != null);
// initialize matching types bit map
BitArray typeCandidates = new BitArray(resultMapping.MappedEntityTypes.Count, true);
foreach (var typeMapping in resultMapping.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 = resultMapping.MappedEntityTypes[i];
}
}
// if there is no match, raise an exception
if (null == entityType)
{
throw EntityUtil.CommandExecution(System.Data.Entity.Strings.ADP_InvalidDataReaderUnableToDetermineType);
}
return entityType;
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Requires that the given function import mapping refers to a Collection(Entity) or Collection(ComplexType) CSDL
/// function.
/// </remarks>
/// <returns>Row type.</returns>
internal TypeUsage GetExpectedTargetResultType(MetadataWorkspace workspace, int resultSetIndex)
{
FunctionImportStructuralTypeMappingKB resultMapping = this.GetResultMapping(resultSetIndex);
// Collect all columns as name-type pairs.
Dictionary<string, TypeUsage> columns = new Dictionary<string, TypeUsage>();
// Figure out which entity types we expect to yield from the function.
IEnumerable<StructuralType> structuralTypes;
if (0 == resultMapping.NormalizedEntityTypeMappings.Count)
{
// No explicit type mappings; just use the type specified in the ReturnType attribute on the function.
StructuralType structuralType;
MetadataHelper.TryGetFunctionImportReturnType<StructuralType>(this.FunctionImport, resultSetIndex, out structuralType);
Debug.Assert(null != structuralType, "this method must be called only for entity/complextype reader function imports");
structuralTypes = new StructuralType[] { structuralType };
}
else
{
// Types are explicitly mapped.
structuralTypes = resultMapping.MappedEntityTypes.Cast<StructuralType>();
}
// Gather columns corresponding to all properties.
foreach (StructuralType structuralType in structuralTypes)
{
foreach (EdmProperty property in TypeHelpers.GetAllStructuralMembers(structuralType))
{
// 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 indexer rather than
// .Add.
columns[property.Name] = property.TypeUsage;
}
}
// Gather discriminator columns.
foreach (string discriminatorColumn in this.GetDiscriminatorColumns(resultSetIndex))
{
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;
}
}
}
|