2015-04-07 09:35:12 +01:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
// <copyright file="MemberProjectedSlot.cs" company="Microsoft">
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// </copyright>
|
|
|
|
//
|
|
|
|
// @owner [....]
|
|
|
|
// @backupOwner [....]
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
|
|
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, IEnumerable<MemberPath> members)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|