//--------------------------------------------------------------------- // <copyright file="MemberProjectedSlot.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Data.Common.CommandTrees; using System.Data.Common.CommandTrees.ExpressionBuilder; using System.Data.Common.Utils; using System.Data.Metadata.Edm; using System.Data.Mapping.ViewGeneration.CqlGeneration; using System.Data.Mapping.ViewGeneration.Utils; namespace System.Data.Mapping.ViewGeneration.Structures { /// <summary> /// A wrapper around MemberPath that allows members to be marked as ProjectedSlots. /// </summary> internal sealed class MemberProjectedSlot : ProjectedSlot { #region Constructor /// <summary> /// Creates a projected slot that references the relevant celltree node. /// </summary> internal MemberProjectedSlot(MemberPath node) { m_memberPath = node; } #endregion #region Fields private readonly MemberPath m_memberPath; #endregion #region Properties /// <summary> /// Returns the full metadata path from the root extent to this node, e.g., Person.Adrs.zip /// </summary> internal MemberPath MemberPath { get { return m_memberPath; } } #endregion #region Methods internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel) { TypeUsage outputMemberStoreTypeUsage; if (NeedToCastCqlValue(outputMember, out outputMemberStoreTypeUsage)) { builder.Append("CAST("); m_memberPath.AsEsql(builder, blockAlias); builder.Append(" AS "); CqlWriter.AppendEscapedTypeName(builder, outputMemberStoreTypeUsage.EdmType); builder.Append(')'); } else { m_memberPath.AsEsql(builder, blockAlias); } return builder; } internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember) { DbExpression cqt = m_memberPath.AsCqt(row); TypeUsage outputMemberTypeUsage; if (NeedToCastCqlValue(outputMember, out outputMemberTypeUsage)) { cqt = cqt.CastTo(outputMemberTypeUsage); } return cqt; } /// <summary> /// True iff <see cref=" m_memberPath"/> and <paramref name="outputMember"/> types do not match, /// We assume that the mapping loader has already checked that the casts are ok and emitted warnings. /// </summary> private bool NeedToCastCqlValue(MemberPath outputMember, out TypeUsage outputMemberTypeUsage) { TypeUsage memberPathTypeUsage = Helper.GetModelTypeUsage(m_memberPath.LeafEdmMember); outputMemberTypeUsage = Helper.GetModelTypeUsage(outputMember.LeafEdmMember); return !memberPathTypeUsage.EdmType.Equals(outputMemberTypeUsage.EdmType); } internal override void ToCompactString(StringBuilder builder) { m_memberPath.ToCompactString(builder); } internal string ToUserString() { return m_memberPath.PathToString(false); } protected override bool IsEqualTo(ProjectedSlot right) { MemberProjectedSlot rightSlot = right as MemberProjectedSlot; if (rightSlot == null) { return false; } // We want equality of the paths return MemberPath.EqualityComparer.Equals(m_memberPath, rightSlot.m_memberPath); } protected override int GetHash() { return MemberPath.EqualityComparer.GetHashCode(m_memberPath); } /// <summary> /// Given a slot and the new mapping, returns the corresponding new slot. /// </summary> internal MemberProjectedSlot RemapSlot(Dictionary<MemberPath, MemberPath> remap) { MemberPath remappedNode = null; if (remap.TryGetValue(MemberPath, out remappedNode)) { return new MemberProjectedSlot(remappedNode); } else { return new MemberProjectedSlot(MemberPath); } } #endregion #region Helper methods /// <summary> /// Given the <paramref name="prefix"/>, determines the slots in <paramref name="slots"/> that correspond to the entity key for the entity set or the /// association set end. Returns the list of slots. Returns null if even one of the key slots is not present in slots. /// </summary> /// <param name="prefix">corresponds to an entity set or an association end</param> internal static List<MemberProjectedSlot> GetKeySlots(IEnumerable<MemberProjectedSlot> slots, MemberPath prefix) { // Get the entity type of the hosted end or entity set EntitySet entitySet = prefix.EntitySet; Debug.Assert(entitySet != null, "Prefix must have associated entity set"); List<ExtentKey> keys = ExtentKey.GetKeysForEntityType(prefix, entitySet.ElementType); Debug.Assert(keys.Count > 0, "No keys for entity?"); Debug.Assert(keys.Count == 1, "Currently, we only support primary keys"); // Get the slots for the key List<MemberProjectedSlot> keySlots = GetSlots(slots, keys[0].KeyFields); return keySlots; } /// <summary> /// Searches for members in <paramref name="slots"/> and returns the corresponding slots in the same order as present in /// <paramref name="members"/>. Returns null if even one member is not present in slots. /// </summary> internal static List<MemberProjectedSlot> GetSlots(IEnumerable<MemberProjectedSlot> slots, IEnumerablemembers) { List<MemberProjectedSlot> result = new List<MemberProjectedSlot>(); foreach (MemberPath member in members) { MemberProjectedSlot slot = GetSlotForMember(Helpers.AsSuperTypeList<MemberProjectedSlot, ProjectedSlot>(slots), member); if (slot == null) { return null; } result.Add(slot); } return result; } /// <summary> /// Searches for <paramref name="member"/> in <paramref name="slots"/> and returns the corresponding slot. If none is found, returns null. /// </summary> internal static MemberProjectedSlot GetSlotForMember(IEnumerable<ProjectedSlot> slots, MemberPath member) { foreach (MemberProjectedSlot slot in slots) { if (MemberPath.EqualityComparer.Equals(slot.MemberPath, member)) { return slot; } } return null; } #endregion } }