|
//---------------------------------------------------------------------
// <copyright file="Propagator.PlaceholderVisitor.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Mapping.Update.Internal
{
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Common.CommandTrees;
using System.Data.Metadata.Edm;
using System.Data.Spatial;
using System.Diagnostics;
internal partial class Propagator
{
/// <summary>
/// Class generating default records for extents. Has a single external entry point, the
/// <see cref="CreatePlaceholder" /> static method.
/// </summary>
private class ExtentPlaceholderCreator
{
#region Constructors
/// <summary>
/// Constructs a new placeholder creator.
/// </summary>
/// <param name="parent">Context used to generate all elements of the placeholder.</param>
private ExtentPlaceholderCreator(UpdateTranslator parent)
{
EntityUtil.CheckArgumentNull(parent, "parent");
m_parent = parent;
}
#endregion
#region Fields
static private Dictionary<PrimitiveTypeKind, object> s_typeDefaultMap = InitializeTypeDefaultMap();
private UpdateTranslator m_parent;
#endregion
#region Methods
/// <summary>
/// Initializes a map from primitive scalar types in the C-Space to default values
/// used within the placeholder.
/// </summary>
private static Dictionary<PrimitiveTypeKind, object> InitializeTypeDefaultMap()
{
Dictionary<PrimitiveTypeKind, object> typeDefaultMap = new Dictionary<PrimitiveTypeKind, object>(
EqualityComparer<PrimitiveTypeKind>.Default);
// Use CLR defaults for value types, arbitrary constants for reference types
// (since these default to null)
typeDefaultMap[PrimitiveTypeKind.Binary] = new Byte[0];
typeDefaultMap[PrimitiveTypeKind.Boolean] = default(Boolean);
typeDefaultMap[PrimitiveTypeKind.Byte] = default(Byte);
typeDefaultMap[PrimitiveTypeKind.DateTime] = default(DateTime);
typeDefaultMap[PrimitiveTypeKind.Time] = default(TimeSpan);
typeDefaultMap[PrimitiveTypeKind.DateTimeOffset] = default(DateTimeOffset);
typeDefaultMap[PrimitiveTypeKind.Decimal] = default(Decimal);
typeDefaultMap[PrimitiveTypeKind.Double] = default(Double);
typeDefaultMap[PrimitiveTypeKind.Guid] = default(Guid);
typeDefaultMap[PrimitiveTypeKind.Int16] = default(Int16);
typeDefaultMap[PrimitiveTypeKind.Int32] = default(Int32);
typeDefaultMap[PrimitiveTypeKind.Int64] = default(Int64);
typeDefaultMap[PrimitiveTypeKind.Single] = default(Single);
typeDefaultMap[PrimitiveTypeKind.SByte] = default(SByte);
typeDefaultMap[PrimitiveTypeKind.String] = String.Empty;
typeDefaultMap[PrimitiveTypeKind.Geometry] = DbGeometry.FromText("POINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryPoint] = DbGeometry.FromText("POINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryLineString] = DbGeometry.FromText("LINESTRING EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryPolygon] = DbGeometry.FromText("POLYGON EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryMultiPoint] = DbGeometry.FromText("MULTIPOINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryMultiLineString] = DbGeometry.FromText("MULTILINESTRING EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryMultiPolygon] = DbGeometry.FromText("MULTIPOLYGON EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeometryCollection] = DbGeometry.FromText("GEOMETRYCOLLECTION EMPTY");
typeDefaultMap[PrimitiveTypeKind.Geography] = DbGeography.FromText("POINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyPoint] = DbGeography.FromText("POINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyLineString] = DbGeography.FromText("LINESTRING EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyPolygon] = DbGeography.FromText("POLYGON EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyMultiPoint] = DbGeography.FromText("MULTIPOINT EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyMultiLineString] = DbGeography.FromText("MULTILINESTRING EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyMultiPolygon] = DbGeography.FromText("MULTIPOLYGON EMPTY");
typeDefaultMap[PrimitiveTypeKind.GeographyCollection] = DbGeography.FromText("GEOMETRYCOLLECTION EMPTY");
#if DEBUG
foreach (object o in typeDefaultMap.Values)
{
Debug.Assert(null != o, "DbConstantExpression instances do not support null values");
}
#endif
return typeDefaultMap;
}
/// <summary>
/// Creates a record for an extent containing default values. Assumes the extent is either
/// a relationship set or an entity set.
/// </summary>
/// <remarks>
/// Each scalar value appearing in the record is a <see cref="DbConstantExpression" />. A placeholder is created by recursively
/// building a record, so an entity record type will return a new record (<see cref="DbNewInstanceExpression" />)
/// consisting of some recursively built record for each column in the type.
/// </remarks>
/// <param name="extent">Extent</param>
/// <param name="parent">Command tree used to generate portions of the record</param>
/// <returns>A default record for the </returns>
internal static PropagatorResult CreatePlaceholder(EntitySetBase extent, UpdateTranslator parent)
{
EntityUtil.CheckArgumentNull(extent, "extent");
ExtentPlaceholderCreator creator = new ExtentPlaceholderCreator(parent);
AssociationSet associationSet = extent as AssociationSet;
if (null != associationSet)
{
return creator.CreateAssociationSetPlaceholder(associationSet);
}
EntitySet entitySet = extent as EntitySet;
if (null != entitySet)
{
return creator.CreateEntitySetPlaceholder(entitySet);
}
throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_UnsupportedExtentType(
extent.Name, extent.GetType().Name));
}
/// <summary>
/// Specialization of <see cref="CreatePlaceholder" /> for an entity set extent.
/// </summary>
/// <param name="entitySet"></param>
/// <returns></returns>
private PropagatorResult CreateEntitySetPlaceholder(EntitySet entitySet)
{
EntityUtil.CheckArgumentNull(entitySet, "entitySet");
ReadOnlyMetadataCollection<EdmProperty> members = entitySet.ElementType.Properties;
PropagatorResult[] memberValues = new PropagatorResult[members.Count];
for (int ordinal = 0; ordinal < members.Count; ordinal++)
{
PropagatorResult memberValue = CreateMemberPlaceholder(members[ordinal]);
memberValues[ordinal] = memberValue;
}
PropagatorResult result = PropagatorResult.CreateStructuralValue(memberValues, entitySet.ElementType, false);
return result;
}
/// <summary>
/// Specialization of <see cref="CreatePlaceholder" /> for a relationship set extent.
/// </summary>
/// <param name="associationSet"></param>
/// <returns></returns>
private PropagatorResult CreateAssociationSetPlaceholder(AssociationSet associationSet)
{
Debug.Assert(null != associationSet, "Caller must verify parameters are not null");
var endMetadata = associationSet.ElementType.AssociationEndMembers;
PropagatorResult[] endReferenceValues = new PropagatorResult[endMetadata.Count];
// Create a reference expression for each end in the relationship
for (int endOrdinal = 0; endOrdinal < endMetadata.Count; endOrdinal++)
{
var end = endMetadata[endOrdinal];
EntityType entityType = (EntityType)((RefType)end.TypeUsage.EdmType).ElementType;
// Retrieve key values for this end
PropagatorResult[] keyValues = new PropagatorResult[entityType.KeyMembers.Count];
for (int memberOrdinal = 0; memberOrdinal < entityType.KeyMembers.Count; memberOrdinal++)
{
EdmMember keyMember = entityType.KeyMembers[memberOrdinal];
PropagatorResult keyValue = CreateMemberPlaceholder(keyMember);
keyValues[memberOrdinal] = keyValue;
}
RowType endType = entityType.GetKeyRowType(m_parent.MetadataWorkspace);
PropagatorResult refKeys = PropagatorResult.CreateStructuralValue(keyValues, endType, false);
endReferenceValues[endOrdinal] = refKeys;
}
PropagatorResult result = PropagatorResult.CreateStructuralValue(endReferenceValues, associationSet.ElementType, false);
return result;
}
/// <summary>
/// Returns a placeholder for a specific metadata member.
/// </summary>
/// <param name="member">EdmMember for which to produce a placeholder.</param>
/// <returns>Placeholder element for the given member.</returns>
private PropagatorResult CreateMemberPlaceholder(EdmMember member)
{
EntityUtil.CheckArgumentNull(member, "member");
return Visit(member);
}
#region Visitor implementation
/// <summary>
/// Given default values for children members, produces a new default expression for the requested (parent) member.
/// </summary>
/// <param name="node">Parent member</param>
/// <returns>Default value for parent member</returns>
internal PropagatorResult Visit(EdmMember node)
{
PropagatorResult result;
TypeUsage nodeType = Helper.GetModelTypeUsage(node);
if (Helper.IsScalarType(nodeType.EdmType))
{
GetPropagatorResultForPrimitiveType(Helper.AsPrimitive(nodeType.EdmType), out result);
}
else
{
// Construct a new 'complex type' (really any structural type) member.
StructuralType structuralType = (StructuralType)nodeType.EdmType;
IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(structuralType);
PropagatorResult[] args = new PropagatorResult[members.Count];
for (int ordinal = 0; ordinal < members.Count; ordinal++)
// foreach (EdmMember member in members)
{
args[ordinal] = Visit(members[ordinal]);
}
result = PropagatorResult.CreateStructuralValue(args, structuralType, false);
}
return result;
}
// Find "sanctioned" default value
private static void GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result)
{
object value;
PrimitiveTypeKind primitiveTypeKind = primitiveType.PrimitiveTypeKind;
if (!s_typeDefaultMap.TryGetValue(primitiveTypeKind, out value))
{
// If none exists, default to lowest common denominator for constants
value = default(byte);
}
// Return a new constant expression flagged as unknown since the value is only there for
// show. (Not entirely for show, because null constraints may require a value for a record,
// whether that record is a placeholder or not).
result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value);
}
#endregion
#endregion
}
}
}
|