|
//---------------------------------------------------------------------
// <copyright file="ColumnMap.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System;
using System.Text;
using System.Collections.Generic;
using md = System.Data.Metadata.Edm;
using mp = System.Data.Mapping;
using System.Globalization;
using System.Diagnostics;
using System.Data.Common.Utils;
// A ColumnMap is a data structure that maps columns from the C space to
// the corresponding columns from one or more underlying readers.
//
// ColumnMaps are used by the ResultAssembly phase to assemble results in the
// desired shape (as requested by the caller) from a set of underlying
// (usually) flat readers. ColumnMaps are produced as part of the PlanCompiler
// module of the bridge, and are consumed by the Execution phase of the bridge.
//
// * Simple (scalar) columns (and UDTs) are represented by a SimpleColumnMap
// * Record type columns are represented by a RecordColumnMap
// * A nominal type instance (that supports inheritance) is usually represented
// by a PolymorphicColumnMap - this polymorphicColumnMap contains information
// about the type discriminator (assumed to be a simple column), and a mapping
// from type-discriminator value to the column map for the specific type
// * The specific type for nominal types is represented by ComplexTypeColumnMap
// for complextype columns, and EntityColumnMap for entity type columns.
// EntityColumnMaps additionally have an EntityIdentity that describes
// the entity identity. The entity identity is logically just a set of keys
// (and the column maps), plus a column map that helps to identify the
// the appropriate entity set for the entity instance
// * Refs are represented by a RefColumnMap. The RefColumnMap simply contains an
// EntityIdentity
// * Collections are represented by either a SimpleCollectionColumnMap or a
// DiscriminatedCollectionColumnMap. Both of these contain a column map for the
// element type, and an optional list of simple columns (the keys) that help
// demarcate the elements of a specific collection instance.
// The DiscriminatedCollectionColumnMap is used in scenarios when the containing
// row has multiple collections, and the different collection properties must be
// differentiated. This differentiation is achieved via a Discriminator column
// (a simple column), and a Discriminator value. The value of the Discriminator
// column is read and compared with the DiscriminatorValue stored in this map
// to determine if we're dealing with the current collection.
//
// NOTE:
// * Key columns are assumed to be SimpleColumns. There may be more than one key
// column (applies to EntityColumnMap and *CollectionColumnMap)
// * TypeDiscriminator and Discriminator columns are also considered to be
// SimpleColumns. There are singleton columns.
//
// It is the responsibility of the PlanCompiler phase to produce the right column
// maps.
//
// The result of a query is always assumed to be a collection. The ColumnMap that we
// return as part of plan compilation refers to the element type of this collection
// - the element type is usually a structured type, but may also be a simple type
// or another collection type. How does the DbRecord framework handle these cases?
//
//
namespace System.Data.Query.InternalTrees
{
/// <summary>
/// Represents a column
/// </summary>
internal abstract class ColumnMap
{
private md.TypeUsage m_type; // column datatype
private string m_name; // name of the column
/// <summary>
/// Default Column Name; should not be set until CodeGen once we're done
/// with all our transformations that might give us a good name, but put
/// here for ease of finding it.
/// </summary>
internal const string DefaultColumnName = "Value";
/// <summary>
/// Simple constructor - just needs the name and type of the column
/// </summary>
/// <param name="type">column type</param>
/// <param name="name">column name</param>
internal ColumnMap(md.TypeUsage type, string name)
{
Debug.Assert(type != null, "Unspecified type");
m_type = type;
m_name = name;
}
/// <summary>
/// Get the column's datatype
/// </summary>
internal md.TypeUsage Type { get { return m_type; } }
/// <summary>
/// Get the column name
/// </summary>
internal string Name
{
get { return m_name; }
set
{
Debug.Assert(!String.IsNullOrEmpty(value), "invalid name?");
m_name = value;
}
}
/// <summary>
/// Returns whether the column already has a name;
/// </summary>
internal bool IsNamed
{
get { return m_name != null; }
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
/// <returns></returns>
[DebuggerNonUserCode]
internal abstract void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg);
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
/// <returns></returns>
[DebuggerNonUserCode]
internal abstract TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg);
}
/// <summary>
/// Base class for simple column maps; can be either a VarRefColumnMap or
/// ScalarColumnMap; the former is used pretty much throughout the PlanCompiler,
/// while the latter will only be used once we generate the final Plan.
/// </summary>
internal abstract class SimpleColumnMap : ColumnMap
{
/// <summary>
/// Basic constructor
/// </summary>
/// <param name="type">datatype for this column</param>
/// <param name="name">column name</param>
internal SimpleColumnMap(md.TypeUsage type, string name)
: base(type, name)
{
}
}
/// <summary>
/// Column map for a scalar column - maps 1-1 with a column from a
/// row of the underlying reader
/// </summary>
internal class ScalarColumnMap : SimpleColumnMap
{
private int m_commandId;
private int m_columnPos;
/// <summary>
/// Basic constructor
/// </summary>
/// <param name="type">datatype for this column</param>
/// <param name="name">column name</param>
/// <param name="commandId">Underlying command to locate this column</param>
/// <param name="columnPos">Position in underlying reader</param>
internal ScalarColumnMap(md.TypeUsage type, string name, int commandId, int columnPos)
: base(type, name)
{
Debug.Assert(commandId >= 0, "invalid command id");
Debug.Assert(columnPos >= 0, "invalid column position");
m_commandId = commandId;
m_columnPos = columnPos;
}
/// <summary>
/// The command (reader, really) to get this column value from
/// </summary>
internal int CommandId { get { return m_commandId; } }
/// <summary>
/// Column position within the reader of the command
/// </summary>
internal int ColumnPos { get { return m_columnPos; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "S({0},{1})", this.CommandId, this.ColumnPos);
}
}
#region Structured Columns
/// <summary>
/// Represents a column map for a structured column
/// </summary>
internal abstract class StructuredColumnMap : ColumnMap
{
private readonly ColumnMap[] m_properties;
/// <summary>
/// Structured columnmap constructor
/// </summary>
/// <param name="type">datatype for this column</param>
/// <param name="name">column name</param>
/// <param name="properties">list of properties</param>
internal StructuredColumnMap(md.TypeUsage type, string name, ColumnMap[] properties)
: base(type, name)
{
Debug.Assert(properties != null, "No properties (gasp!) for a structured type");
m_properties = properties;
}
/// <summary>
/// Get the null sentinel column, if any. Virtual so only derived column map
/// types that can have NullSentinel have to provide storage, etc.
/// </summary>
virtual internal SimpleColumnMap NullSentinel { get { return null; } }
/// <summary>
/// Get the list of properties that constitute this structured type
/// </summary>
internal ColumnMap[] Properties { get { return m_properties; } }
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
string separator = String.Empty;
sb.Append("{");
foreach (ColumnMap c in this.Properties)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c);
separator = ",";
}
sb.Append("}");
return sb.ToString();
}
}
/// <summary>
/// Represents a record (an untyped structured column)
/// </summary>
internal class RecordColumnMap : StructuredColumnMap
{
private SimpleColumnMap m_nullSentinel;
/// <summary>
/// Constructor for a record column map
/// </summary>
/// <param name="type">Datatype of this column</param>
/// <param name="name">column name</param>
/// <param name="properties">List of ColumnMaps - one for each property</param>
internal RecordColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, SimpleColumnMap nullSentinel)
: base(type, name, properties)
{
m_nullSentinel = nullSentinel;
}
/// <summary>
/// Get the type Nullability column
/// </summary>
internal override SimpleColumnMap NullSentinel { get { return m_nullSentinel; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
}
/// <summary>
/// Column map for a "typed" column
/// - either an entity type or a complex type
/// </summary>
internal abstract class TypedColumnMap : StructuredColumnMap
{
/// <summary>
/// Typed columnMap constructor
/// </summary>
/// <param name="type">Datatype of column</param>
/// <param name="name">column name</param>
/// <param name="properties">List of column maps - one for each property</param>
internal TypedColumnMap(md.TypeUsage type, string name, ColumnMap[] properties)
: base(type, name, properties) { }
}
/// <summary>
/// Represents a polymorphic typed column - either an entity or
/// a complex type.
/// </summary>
internal class SimplePolymorphicColumnMap : TypedColumnMap
{
private SimpleColumnMap m_typeDiscriminator;
private Dictionary<object, TypedColumnMap> m_typedColumnMap;
/// <summary>
/// Internal constructor
/// </summary>
/// <param name="type">datatype of the column</param>
/// <param name="name">column name</param>
/// <param name="typeDiscriminator">column map for type discriminator column</param>
/// <param name="baseTypeColumns">base list of fields common to all types</param>
/// <param name="typeChoices">map from type discriminator value->columnMap</param>
internal SimplePolymorphicColumnMap(md.TypeUsage type,
string name,
ColumnMap[] baseTypeColumns,
SimpleColumnMap typeDiscriminator,
Dictionary<object, TypedColumnMap> typeChoices)
: base(type, name, baseTypeColumns)
{
Debug.Assert(typeDiscriminator != null, "Must specify a type discriminator column");
Debug.Assert(typeChoices != null, "No type choices for polymorphic column");
m_typedColumnMap = typeChoices;
m_typeDiscriminator = typeDiscriminator;
}
/// <summary>
/// Get the type discriminator column
/// </summary>
internal SimpleColumnMap TypeDiscriminator { get { return m_typeDiscriminator; } }
/// <summary>
/// Get the type mapping
/// </summary>
internal Dictionary<object, TypedColumnMap> TypeChoices
{
get { return m_typedColumnMap; }
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
string separator = String.Empty;
sb.AppendFormat(CultureInfo.InvariantCulture, "P{{TypeId={0}, ", this.TypeDiscriminator);
foreach (KeyValuePair<object, TypedColumnMap> kv in this.TypeChoices)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", separator, kv.Key, kv.Value);
separator = ",";
}
sb.Append("}");
return sb.ToString();
}
}
/// <summary>
/// Represents a function import column map.
/// </summary>
internal class MultipleDiscriminatorPolymorphicColumnMap : TypedColumnMap
{
private readonly SimpleColumnMap[] m_typeDiscriminators;
private readonly Dictionary<md.EntityType, TypedColumnMap> m_typeChoices;
private readonly Func<object[], md.EntityType> m_discriminate;
/// <summary>
/// Internal constructor
/// </summary>
internal MultipleDiscriminatorPolymorphicColumnMap(md.TypeUsage type,
string name,
ColumnMap[] baseTypeColumns,
SimpleColumnMap[] typeDiscriminators,
Dictionary<md.EntityType, TypedColumnMap> typeChoices,
Func<object[], md.EntityType> discriminate)
: base(type, name, baseTypeColumns)
{
Debug.Assert(typeDiscriminators != null, "Must specify type discriminator columns");
Debug.Assert(typeChoices != null, "No type choices for polymorphic column");
Debug.Assert(discriminate != null, "Must specify discriminate");
m_typeDiscriminators = typeDiscriminators;
m_typeChoices = typeChoices;
m_discriminate = discriminate;
}
/// <summary>
/// Get the type discriminator column
/// </summary>
internal SimpleColumnMap[] TypeDiscriminators { get { return m_typeDiscriminators; } }
/// <summary>
/// Get the type mapping
/// </summary>
internal Dictionary<md.EntityType, TypedColumnMap> TypeChoices
{
get { return m_typeChoices; }
}
/// <summary>
/// Gets discriminator delegate
/// </summary>
internal Func<object[], md.EntityType> Discriminate
{
get { return m_discriminate; }
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
string separator = String.Empty;
sb.AppendFormat(CultureInfo.InvariantCulture, "P{{TypeId=<{0}>, ", StringUtil.ToCommaSeparatedString(this.TypeDiscriminators));
foreach (var kv in this.TypeChoices)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}(<{1}>,{2})", separator, kv.Key, kv.Value);
separator = ",";
}
sb.Append("}");
return sb.ToString();
}
}
/// <summary>
/// Represents a column map for a specific complextype
/// </summary>
internal class ComplexTypeColumnMap : TypedColumnMap
{
private SimpleColumnMap m_nullSentinel;
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">column Datatype</param>
/// <param name="name">column name</param>
/// <param name="properties">list of properties</param>
internal ComplexTypeColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, SimpleColumnMap nullSentinel)
: base(type, name, properties)
{
m_nullSentinel = nullSentinel;
}
/// <summary>
/// Get the type Nullability column
/// </summary>
internal override SimpleColumnMap NullSentinel { get { return m_nullSentinel; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
string str = String.Format(CultureInfo.InvariantCulture, "C{0}", base.ToString());
return str;
}
}
/// <summary>
/// Represents a column map for a specific entity type
/// </summary>
internal class EntityColumnMap : TypedColumnMap
{
private EntityIdentity m_entityIdentity;
/// <summary>
/// constructor
/// </summary>
/// <param name="type">column datatype</param>
/// <param name="name">column name</param>
/// <param name="entityIdentity">entity identity information</param>
/// <param name="properties">list of properties</param>
internal EntityColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, EntityIdentity entityIdentity)
: base(type, name, properties)
{
Debug.Assert(entityIdentity != null, "Must specify an entity identity");
m_entityIdentity = entityIdentity;
}
/// <summary>
/// Get the entity identity information
/// </summary>
internal EntityIdentity EntityIdentity { get { return m_entityIdentity; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
string str = String.Format(CultureInfo.InvariantCulture, "E{0}", base.ToString());
return str;
}
}
/// <summary>
/// A column map that represents a ref column.
/// </summary>
internal class RefColumnMap: ColumnMap
{
private EntityIdentity m_entityIdentity;
/// <summary>
/// Constructor for a ref column
/// </summary>
/// <param name="type">column datatype</param>
/// <param name="name">column name</param>
/// <param name="entityIdentity">identity information for this entity</param>
internal RefColumnMap(md.TypeUsage type, string name,
EntityIdentity entityIdentity)
: base(type, name)
{
Debug.Assert(entityIdentity != null, "Must specify entity identity information");
m_entityIdentity = entityIdentity;
}
/// <summary>
/// Get the entity identity information for this ref
/// </summary>
internal EntityIdentity EntityIdentity { get { return m_entityIdentity; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
}
#endregion
#region Collections
/// <summary>
/// Represents a column map for a collection column.
/// The "element" represents the element of the collection - usually a Structured
/// type, but occasionally a collection/simple type as well.
/// The "ForeignKeys" property is optional (but usually necessary) to determine the
/// elements of the collection.
/// </summary>
internal abstract class CollectionColumnMap : ColumnMap
{
private readonly ColumnMap m_element;
private readonly SimpleColumnMap[] m_foreignKeys;
private readonly SimpleColumnMap[] m_keys;
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">datatype of column</param>
/// <param name="name">column name</param>
/// <param name="elementMap">column map for collection element</param>
/// <param name="keys">List of keys</param>
/// <param name="foreignKeys">List of foreign keys</param>
internal CollectionColumnMap(md.TypeUsage type, string name, ColumnMap elementMap, SimpleColumnMap[] keys, SimpleColumnMap[] foreignKeys)
: base(type, name)
{
Debug.Assert(elementMap != null, "Must specify column map for element");
m_element = elementMap;
m_keys = keys ?? new SimpleColumnMap[0];
m_foreignKeys = foreignKeys ?? new SimpleColumnMap[0];
}
/// <summary>
/// Get the list of columns that may comprise the foreign key
/// </summary>
internal SimpleColumnMap[] ForeignKeys
{
get { return m_foreignKeys; }
}
/// <summary>
/// Get the list of columns that may comprise the key
/// </summary>
internal SimpleColumnMap[] Keys
{
get { return m_keys; }
}
/// <summary>
/// Get the column map describing the collection element
/// </summary>
internal ColumnMap Element
{
get { return m_element; }
}
}
/// <summary>
/// Represents a "simple" collection map.
/// </summary>
internal class SimpleCollectionColumnMap : CollectionColumnMap
{
/// <summary>
/// Basic constructor
/// </summary>
/// <param name="type">Column datatype</param>
/// <param name="name">column name</param>
/// <param name="elementMap">column map for the element of the collection</param>
/// <param name="keys">list of key columns</param>
/// <param name="foreignKeys">list of foreign key columns</param>
internal SimpleCollectionColumnMap(md.TypeUsage type, string name,
ColumnMap elementMap,
SimpleColumnMap[] keys,
SimpleColumnMap[] foreignKeys)
: base(type, name, elementMap, keys, foreignKeys) { }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
}
/// <summary>
/// Represents a "discriminated" collection column.
/// This represents a scenario when multiple collections are represented
/// at the same level of the container row, and there is a need to distinguish
/// between these collections
/// </summary>
internal class DiscriminatedCollectionColumnMap : CollectionColumnMap
{
private SimpleColumnMap m_discriminator;
private object m_discriminatorValue;
/// <summary>
/// Internal constructor
/// </summary>
/// <param name="type">Column datatype</param>
/// <param name="name">column name</param>
/// <param name="elementMap">column map for collection element</param>
/// <param name="keys">Keys for the collection</param>
/// <param name="foreignKeys">Foreign keys for the collection</param>
/// <param name="discriminator">Discriminator column map</param>
/// <param name="discriminatorValue">Discriminator value</param>
internal DiscriminatedCollectionColumnMap(md.TypeUsage type, string name,
ColumnMap elementMap,
SimpleColumnMap[] keys,
SimpleColumnMap[] foreignKeys,
SimpleColumnMap discriminator,
object discriminatorValue)
: base(type, name, elementMap, keys, foreignKeys)
{
Debug.Assert(discriminator != null, "Must specify a column map for the collection discriminator");
Debug.Assert(discriminatorValue != null, "Must specify a discriminator value");
m_discriminator = discriminator;
m_discriminatorValue = discriminatorValue;
}
/// <summary>
/// Get the column that describes the discriminator
/// </summary>
internal SimpleColumnMap Discriminator { get { return m_discriminator; } }
/// <summary>
/// Get the discriminator value
/// </summary>
internal object DiscriminatorValue { get { return m_discriminatorValue; } }
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
string str = String.Format(CultureInfo.InvariantCulture, "M{{{0}}}", this.Element.ToString());
return str;
}
}
#endregion
#region EntityIdentity
/// <summary>
/// Abstract base class representing entity identity. Used by both
/// EntityColumnMap and RefColumnMap.
/// An EntityIdentity captures two pieces of information - the list of keys
/// that uniquely identify an entity within an entityset, and the the entityset
/// itself.
/// </summary>
internal abstract class EntityIdentity
{
private readonly SimpleColumnMap[] m_keys; // list of keys
/// <summary>
/// Simple constructor - gets a list of key columns
/// </summary>
/// <param name="keyColumns"></param>
internal EntityIdentity(SimpleColumnMap[] keyColumns)
{
Debug.Assert(keyColumns != null, "Must specify column maps for key columns");
m_keys = keyColumns;
}
/// <summary>
/// Get the key columns
/// </summary>
internal SimpleColumnMap[] Keys { get { return m_keys; } }
}
/// <summary>
/// This class is a "simple" representation of the entity identity, where the
/// entityset containing the entity is known a priori. This may be because
/// there is exactly one entityset for the entity; or because it is inferrable
/// from the query that only one entityset is relevant here
/// </summary>
internal class SimpleEntityIdentity : EntityIdentity
{
private md.EntitySet m_entitySet; // the entity set
/// <summary>
/// Basic constructor.
/// Note: the entitySet may be null - in which case, we are referring to
/// a transient entity
/// </summary>
/// <param name="entitySet">The entityset</param>
/// <param name="keyColumns">key columns of the entity</param>
internal SimpleEntityIdentity(md.EntitySet entitySet, SimpleColumnMap[] keyColumns)
: base(keyColumns)
{
// the entityset may be null
m_entitySet = entitySet;
}
/// <summary>
/// The entityset containing the entity
/// </summary>
internal md.EntitySet EntitySet { get { return m_entitySet; } }
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
string separator = String.Empty;
sb.AppendFormat(CultureInfo.InvariantCulture, "[(ES={0}) (Keys={", this.EntitySet.Name);
foreach (SimpleColumnMap c in this.Keys)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c);
separator = ",";
}
sb.AppendFormat(CultureInfo.InvariantCulture, "})]");
return sb.ToString();
}
}
/// <summary>
/// This class also represents entity identity. However, this class addresses
/// those scenarios where the entityset for the entity is not uniquely known
/// a priori. Instead, the query is annotated with information, and based on
/// the resulting information, the appropriate entityset is identified.
/// Specifically, the specific entityset is represented as a SimpleColumnMap
/// in the query. The value of that column is used to look up a dictionary,
/// and then identify the appropriate entity set.
/// It is entirely possible that no entityset may be located for the entity
/// instance - this represents a transient entity instance
/// </summary>
internal class DiscriminatedEntityIdentity : EntityIdentity
{
private SimpleColumnMap m_entitySetColumn; // (optional) column map representing the entity set
private md.EntitySet[] m_entitySetMap; // optional dictionary that maps values to entitysets
/// <summary>
/// Simple constructor
/// </summary>
/// <param name="entitySetColumn">column map representing the entityset</param>
/// <param name="entitySetMap">Map from value -> the appropriate entityset</param>
/// <param name="keyColumns">list of key columns</param>
internal DiscriminatedEntityIdentity(SimpleColumnMap entitySetColumn, md.EntitySet[] entitySetMap,
SimpleColumnMap[] keyColumns)
: base(keyColumns)
{
Debug.Assert(entitySetColumn != null, "Must specify a column map to identify the entity set");
Debug.Assert(entitySetMap != null, "Must specify a dictionary to look up entitysets");
m_entitySetColumn = entitySetColumn;
m_entitySetMap = entitySetMap;
}
/// <summary>
/// Get the column map representing the entityset
/// </summary>
internal SimpleColumnMap EntitySetColumnMap { get { return m_entitySetColumn; } }
/// <summary>
/// Return the entityset map
/// </summary>
internal md.EntitySet[] EntitySetMap
{
get { return m_entitySetMap; }
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
string separator = String.Empty;
sb.AppendFormat(CultureInfo.InvariantCulture, "[(Keys={");
foreach (SimpleColumnMap c in this.Keys)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c);
separator = ",";
}
sb.AppendFormat(CultureInfo.InvariantCulture, "})]");
return sb.ToString();
}
}
#endregion
#region internal classes
/// <summary>
/// A VarRefColumnMap is our intermediate representation of a ColumnMap.
/// Eventually, this gets translated into a regular ColumnMap - during the CodeGen phase
/// </summary>
internal class VarRefColumnMap : SimpleColumnMap
{
#region Public Methods
/// <summary>
/// Get the Var that produces this column's value
/// </summary>
internal InternalTrees.Var Var
{
get { return m_var; }
}
#endregion
#region Constructors
/// <summary>
/// Simple constructor
/// </summary>
/// <param name="type">datatype of this Var</param>
/// <param name="name">the name of the column</param>
/// <param name="v">the var producing the value for this column</param>
internal VarRefColumnMap(md.TypeUsage type, string name, InternalTrees.Var v)
: base(type, name)
{
m_var = v;
}
internal VarRefColumnMap(InternalTrees.Var v)
: this(v.Type, null, v)
{
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override void Accept<TArgType>(ColumnMapVisitor<TArgType> visitor, TArgType arg)
{
visitor.Visit(this, arg);
}
/// <summary>
/// Visitor Design Pattern
/// </summary>
/// <typeparam name="TResultType"></typeparam>
/// <typeparam name="TArgType"></typeparam>
/// <param name="visitor"></param>
/// <param name="arg"></param>
[DebuggerNonUserCode]
internal override TResultType Accept<TResultType, TArgType>(ColumnMapVisitorWithResults<TResultType, TArgType> visitor, TArgType arg)
{
return visitor.Visit(this, arg);
}
/// <summary>
/// Debugging support
/// </summary>
/// <returns></returns>
public override string ToString()
{
return this.IsNamed ? this.Name : String.Format(CultureInfo.InvariantCulture, "{0}", m_var.Id);
}
#endregion
#region private state
private InternalTrees.Var m_var;
#endregion
}
#endregion
}
|