You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,169 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="DbTypeMap.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
//---------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Common.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides singleton model TypeUsages for each DbType that can be expressed using a supported EDM type and appropriate facet values.
|
||||
/// Used by EntityParameter.GetTypeUsage - if you add additional TypeUsage fields here, review the impact on that method.
|
||||
/// </summary>
|
||||
internal static class DbTypeMap
|
||||
{
|
||||
internal static readonly TypeUsage AnsiString = CreateType(PrimitiveTypeKind.String, new FacetValues { Unicode = false, FixedLength = false, MaxLength = (int?)null });
|
||||
internal static readonly TypeUsage AnsiStringFixedLength = CreateType(PrimitiveTypeKind.String, new FacetValues { Unicode = false, FixedLength = true, MaxLength = (int?)null });
|
||||
internal static readonly TypeUsage String = CreateType(PrimitiveTypeKind.String, new FacetValues { Unicode = true, FixedLength = false, MaxLength = (int?)null });
|
||||
internal static readonly TypeUsage StringFixedLength = CreateType(PrimitiveTypeKind.String, new FacetValues { Unicode = true, FixedLength = true, MaxLength = (int?)null });
|
||||
// SQLBUDT #514204 - EntityCommand: XML parameter size must be ignored
|
||||
/* XML parameters must not have a explicit size */
|
||||
internal static readonly TypeUsage Xml = CreateType(PrimitiveTypeKind.String, new FacetValues { Unicode = true, FixedLength = false, MaxLength = (int?)null });
|
||||
internal static readonly TypeUsage Binary = CreateType(PrimitiveTypeKind.Binary , new FacetValues { MaxLength = (int?)null });
|
||||
internal static readonly TypeUsage Boolean = CreateType(PrimitiveTypeKind.Boolean);
|
||||
internal static readonly TypeUsage Byte = CreateType(PrimitiveTypeKind.Byte);
|
||||
internal static readonly TypeUsage DateTime = CreateType(PrimitiveTypeKind.DateTime);
|
||||
internal static readonly TypeUsage Date = CreateType(PrimitiveTypeKind.DateTime);
|
||||
internal static readonly TypeUsage DateTime2 = CreateType(PrimitiveTypeKind.DateTime, new FacetValues { Precision = (byte?)null });
|
||||
internal static readonly TypeUsage Time = CreateType(PrimitiveTypeKind.Time, new FacetValues { Precision = (byte?)null });
|
||||
internal static readonly TypeUsage DateTimeOffset = CreateType(PrimitiveTypeKind.DateTimeOffset, new FacetValues { Precision = (byte?)null });
|
||||
// For decimal and money, in the case of precision == 0, we don't want any facets when picking the type so the
|
||||
// default type should be picked
|
||||
internal static readonly TypeUsage Decimal = CreateType(PrimitiveTypeKind.Decimal, new FacetValues { Precision = (byte?)null, Scale = (byte?)null });
|
||||
// SQLBU 480928: Need to make currency a separate case once we enable money type
|
||||
internal static readonly TypeUsage Currency = CreateType(PrimitiveTypeKind.Decimal, new FacetValues { Precision = (byte?)null, Scale = (byte?)null });
|
||||
internal static readonly TypeUsage Double = CreateType(PrimitiveTypeKind.Double);
|
||||
internal static readonly TypeUsage Guid = CreateType(PrimitiveTypeKind.Guid);
|
||||
internal static readonly TypeUsage Int16 = CreateType(PrimitiveTypeKind.Int16);
|
||||
internal static readonly TypeUsage Int32 = CreateType(PrimitiveTypeKind.Int32);
|
||||
internal static readonly TypeUsage Int64 = CreateType(PrimitiveTypeKind.Int64);
|
||||
internal static readonly TypeUsage Single = CreateType(PrimitiveTypeKind.Single);
|
||||
internal static readonly TypeUsage SByte = CreateType(PrimitiveTypeKind.SByte);
|
||||
|
||||
internal static bool TryGetModelTypeUsage(DbType dbType, out TypeUsage modelType)
|
||||
{
|
||||
switch(dbType)
|
||||
{
|
||||
case DbType.AnsiString:
|
||||
modelType = DbTypeMap.AnsiString;
|
||||
break;
|
||||
|
||||
case DbType.AnsiStringFixedLength:
|
||||
modelType = DbTypeMap.AnsiStringFixedLength;
|
||||
break;
|
||||
|
||||
case DbType.String:
|
||||
modelType = DbTypeMap.String;
|
||||
break;
|
||||
|
||||
case DbType.StringFixedLength:
|
||||
modelType = DbTypeMap.StringFixedLength;
|
||||
break;
|
||||
|
||||
case DbType.Xml:
|
||||
modelType = DbTypeMap.Xml;
|
||||
break;
|
||||
|
||||
case DbType.Binary:
|
||||
modelType = DbTypeMap.Binary;
|
||||
break;
|
||||
|
||||
case DbType.Boolean:
|
||||
modelType = DbTypeMap.Boolean;
|
||||
break;
|
||||
|
||||
case DbType.Byte:
|
||||
modelType = DbTypeMap.Byte;
|
||||
break;
|
||||
|
||||
case DbType.DateTime:
|
||||
modelType = DbTypeMap.DateTime;
|
||||
break;
|
||||
|
||||
case DbType.Date:
|
||||
modelType = DbTypeMap.Date;
|
||||
break;
|
||||
|
||||
case DbType.DateTime2:
|
||||
modelType = DbTypeMap.DateTime2;
|
||||
break;
|
||||
|
||||
case DbType.Time:
|
||||
modelType = DbTypeMap.Time;
|
||||
break;
|
||||
|
||||
case DbType.DateTimeOffset:
|
||||
modelType = DbTypeMap.DateTimeOffset;
|
||||
break;
|
||||
|
||||
case DbType.Decimal:
|
||||
modelType = DbTypeMap.Decimal;
|
||||
break;
|
||||
|
||||
case DbType.Currency:
|
||||
modelType = DbTypeMap.Currency;
|
||||
break;
|
||||
|
||||
case DbType.Double:
|
||||
modelType = DbTypeMap.Double;
|
||||
break;
|
||||
|
||||
case DbType.Guid:
|
||||
modelType = DbTypeMap.Guid;
|
||||
break;
|
||||
|
||||
case DbType.Int16:
|
||||
modelType = DbTypeMap.Int16;
|
||||
break;
|
||||
|
||||
case DbType.Int32:
|
||||
modelType = DbTypeMap.Int32;
|
||||
break;
|
||||
|
||||
case DbType.Int64:
|
||||
modelType = DbTypeMap.Int64;
|
||||
break;
|
||||
|
||||
case DbType.Single:
|
||||
modelType = DbTypeMap.Single;
|
||||
break;
|
||||
|
||||
case DbType.SByte:
|
||||
modelType = DbTypeMap.SByte;
|
||||
break;
|
||||
|
||||
case DbType.VarNumeric:
|
||||
modelType = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
modelType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return (modelType != null);
|
||||
}
|
||||
|
||||
private static TypeUsage CreateType(PrimitiveTypeKind type)
|
||||
{
|
||||
return CreateType(type, new FacetValues());
|
||||
}
|
||||
|
||||
private static TypeUsage CreateType(PrimitiveTypeKind type, FacetValues facets)
|
||||
{
|
||||
PrimitiveType primitiveType = EdmProviderManifest.Instance.GetPrimitiveType(type);
|
||||
TypeUsage typeUsage = TypeUsage.Create(primitiveType, facets);
|
||||
return typeUsage;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,285 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ColumnMapKeyBuilder.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Objects.ELinq;
|
||||
using System.Data.Objects.Internal;
|
||||
using System.Data.Query.InternalTrees;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports building a unique key for a column map so that compiled delegates (<see cref="ShaperFactory"/>)
|
||||
/// can be cached. The general rule: if the <see cref="Translator"/> cares about some property of
|
||||
/// the column map, the generated key must include that property value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// IMPORTANT:
|
||||
/// The "X-" prefixes introduced in the different column map types should be unique. This avoids
|
||||
/// conflicts for different column maps with similar properties (e.g. ComplexType and EntityType)
|
||||
/// </remarks>
|
||||
internal class ColumnMapKeyBuilder : ColumnMapVisitor<int>
|
||||
{
|
||||
#region private state
|
||||
|
||||
private readonly StringBuilder _builder = new StringBuilder();
|
||||
private readonly SpanIndex _spanIndex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
private ColumnMapKeyBuilder(SpanIndex spanIndex)
|
||||
{
|
||||
_spanIndex = spanIndex;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string uniquely identifying the given ColumnMap.
|
||||
/// </summary>
|
||||
internal static string GetColumnMapKey(ColumnMap columnMap, SpanIndex spanIndex)
|
||||
{
|
||||
ColumnMapKeyBuilder builder = new ColumnMapKeyBuilder(spanIndex);
|
||||
columnMap.Accept(builder, 0);
|
||||
return builder._builder.ToString();
|
||||
}
|
||||
|
||||
internal void Append(string value)
|
||||
{
|
||||
_builder.Append(value);
|
||||
}
|
||||
|
||||
internal void Append(string prefix, Type type)
|
||||
{
|
||||
Append(prefix, type.AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
internal void Append(string prefix, TypeUsage type)
|
||||
{
|
||||
if (null != type)
|
||||
{
|
||||
// LINQ has anonymous types that aren't going to show up in our
|
||||
// metadata workspace, and we don't want to hydrate a record when
|
||||
// we need an anonymous type. LINQ solves this by annotating the
|
||||
// edmType with some additional information, which we'll pick up
|
||||
// here.
|
||||
InitializerMetadata initializer;
|
||||
if (InitializerMetadata.TryGetInitializerMetadata(type, out initializer))
|
||||
{
|
||||
initializer.AppendColumnMapKey(this);
|
||||
}
|
||||
Append(prefix, type.EdmType);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Append(string prefix, EdmType type)
|
||||
{
|
||||
if (null != type)
|
||||
{
|
||||
Append(prefix, type.NamespaceName);
|
||||
Append(".", type.Name);
|
||||
|
||||
if (type.BuiltInTypeKind == BuiltInTypeKind.RowType)
|
||||
{
|
||||
if (_spanIndex != null)
|
||||
{
|
||||
Append("<<");
|
||||
Dictionary<int, AssociationEndMember> spanMap = _spanIndex.GetSpanMap((RowType)type);
|
||||
if (null != spanMap)
|
||||
{
|
||||
string separator = string.Empty;
|
||||
foreach (var pair in spanMap)
|
||||
{
|
||||
Append(separator);
|
||||
AppendValue("C", pair.Key);
|
||||
Append(":", pair.Value.DeclaringType);
|
||||
Append(".", pair.Value.Name);
|
||||
separator = ",";
|
||||
}
|
||||
}
|
||||
Append(">>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region helper methods
|
||||
|
||||
private void Append(string prefix, string value)
|
||||
{
|
||||
Append(prefix);
|
||||
Append("'");
|
||||
Append(value);
|
||||
Append("'");
|
||||
}
|
||||
|
||||
private void Append(string prefix, ColumnMap columnMap)
|
||||
{
|
||||
Append(prefix);
|
||||
Append("[");
|
||||
if (null != columnMap)
|
||||
{
|
||||
columnMap.Accept(this, 0);
|
||||
}
|
||||
Append("]");
|
||||
}
|
||||
|
||||
private void Append(string prefix, IEnumerable<ColumnMap> elements)
|
||||
{
|
||||
Append(prefix);
|
||||
Append("{");
|
||||
if (null != elements)
|
||||
{
|
||||
string separator = string.Empty;
|
||||
foreach (ColumnMap element in elements)
|
||||
{
|
||||
Append(separator, element);
|
||||
separator = ",";
|
||||
}
|
||||
}
|
||||
Append("}");
|
||||
}
|
||||
|
||||
private void Append(string prefix, EntityIdentity entityIdentity)
|
||||
{
|
||||
Append(prefix);
|
||||
Append("[");
|
||||
|
||||
Append(",K", entityIdentity.Keys);
|
||||
|
||||
SimpleEntityIdentity simple = entityIdentity as SimpleEntityIdentity;
|
||||
if (null != simple)
|
||||
{
|
||||
Append(",", simple.EntitySet);
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscriminatedEntityIdentity discriminated = (DiscriminatedEntityIdentity)entityIdentity;
|
||||
Append("CM", discriminated.EntitySetColumnMap);
|
||||
foreach (EntitySet entitySet in discriminated.EntitySetMap)
|
||||
{
|
||||
Append(",E", entitySet);
|
||||
}
|
||||
}
|
||||
|
||||
Append("]");
|
||||
}
|
||||
|
||||
private void Append(string prefix, EntitySet entitySet)
|
||||
{
|
||||
if (null != entitySet)
|
||||
{
|
||||
Append(prefix, entitySet.EntityContainer.Name);
|
||||
Append(".", entitySet.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendValue(string prefix, object value)
|
||||
{
|
||||
Append(prefix, String.Format(CultureInfo.InvariantCulture, "{0}", value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region visitor methods
|
||||
|
||||
internal override void Visit(ComplexTypeColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("C-", columnMap.Type);
|
||||
Append(",N", columnMap.NullSentinel);
|
||||
Append(",P", columnMap.Properties);
|
||||
}
|
||||
|
||||
internal override void Visit(DiscriminatedCollectionColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("DC-D", columnMap.Discriminator);
|
||||
AppendValue(",DV", columnMap.DiscriminatorValue);
|
||||
Append(",FK", columnMap.ForeignKeys);
|
||||
Append(",K", columnMap.Keys);
|
||||
Append(",E", columnMap.Element);
|
||||
}
|
||||
|
||||
internal override void Visit(EntityColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("E-", columnMap.Type);
|
||||
Append(",N", columnMap.NullSentinel);
|
||||
Append(",P", columnMap.Properties);
|
||||
Append(",I", columnMap.EntityIdentity);
|
||||
}
|
||||
|
||||
internal override void Visit(SimplePolymorphicColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("SP-", columnMap.Type);
|
||||
Append(",D", columnMap.TypeDiscriminator);
|
||||
Append(",N", columnMap.NullSentinel);
|
||||
Append(",P", columnMap.Properties);
|
||||
foreach (var typeChoice in columnMap.TypeChoices)
|
||||
{
|
||||
AppendValue(",K", typeChoice.Key);
|
||||
Append(":", typeChoice.Value);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Visit(RecordColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("R-", columnMap.Type);
|
||||
Append(",N", columnMap.NullSentinel);
|
||||
Append(",P", columnMap.Properties);
|
||||
}
|
||||
|
||||
internal override void Visit(RefColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("Ref-", columnMap.EntityIdentity);
|
||||
|
||||
EntityType referencedEntityType;
|
||||
bool isRefType = TypeHelpers.TryGetRefEntityType(columnMap.Type, out referencedEntityType);
|
||||
Debug.Assert(isRefType, "RefColumnMap is not of RefType?");
|
||||
Append(",T", referencedEntityType);
|
||||
}
|
||||
|
||||
internal override void Visit(ScalarColumnMap columnMap, int dummy)
|
||||
{
|
||||
String description = String.Format(CultureInfo.InvariantCulture,
|
||||
"S({0}-{1}:{2})", columnMap.CommandId, columnMap.ColumnPos, columnMap.Type.Identity);
|
||||
Append(description);
|
||||
}
|
||||
|
||||
internal override void Visit(SimpleCollectionColumnMap columnMap, int dummy)
|
||||
{
|
||||
Append("DC-FK", columnMap.ForeignKeys);
|
||||
Append(",K", columnMap.Keys);
|
||||
Append(",E", columnMap.Element);
|
||||
}
|
||||
|
||||
internal override void Visit(VarRefColumnMap columnMap, int dummy)
|
||||
{
|
||||
Debug.Fail("must not encounter VarRef in ColumnMap for key (eliminated in final ColumnMap)");
|
||||
}
|
||||
|
||||
internal override void Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, int dummy)
|
||||
{
|
||||
// MultipleDiscriminator maps contain an opaque discriminator delegate, so recompilation
|
||||
// is always required. Generate a unique key for the discriminator.
|
||||
//
|
||||
Append(String.Format(CultureInfo.InvariantCulture, "MD-{0}", Guid.NewGuid()));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CompensatingCollection.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
/// <summary>
|
||||
/// What we return from our materialization of a collection column must be
|
||||
/// exactly the type that the compilers expected when they generated the
|
||||
/// code that asked for it. This class wraps our enumerators and derives
|
||||
/// from all the possible options, covering all the bases.
|
||||
/// </summary>
|
||||
internal class CompensatingCollection<TElement> : IOrderedQueryable<TElement>, IOrderedEnumerable<TElement>
|
||||
{
|
||||
#region private state
|
||||
|
||||
/// <summary>
|
||||
/// The thing we're compensating for
|
||||
/// </summary>
|
||||
private readonly IEnumerable<TElement> _source;
|
||||
|
||||
/// <summary>
|
||||
/// An expression that returns the source as a constant
|
||||
/// </summary>
|
||||
private readonly Expression _expression;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructors
|
||||
|
||||
public CompensatingCollection(IEnumerable<TElement> source)
|
||||
{
|
||||
_source = EntityUtil.CheckArgumentNull(source, "source");
|
||||
_expression = Expression.Constant(source);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _source.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<TElement> Members
|
||||
|
||||
IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
|
||||
{
|
||||
return _source.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IOrderedEnumerable<TElement> Members
|
||||
|
||||
IOrderedEnumerable<TElement> IOrderedEnumerable<TElement>.CreateOrderedEnumerable<K>(Func<TElement, K> keySelector, IComparer<K> comparer, bool descending)
|
||||
{
|
||||
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_CreateOrderedEnumerableNotSupported);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IQueryable Members
|
||||
|
||||
Type IQueryable.ElementType
|
||||
{
|
||||
get { return typeof(TElement); }
|
||||
}
|
||||
|
||||
Expression IQueryable.Expression
|
||||
{
|
||||
get { return _expression; }
|
||||
}
|
||||
|
||||
IQueryProvider IQueryable.Provider
|
||||
{
|
||||
get
|
||||
{
|
||||
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedQueryableMethod);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IQueryable<TElement> Members
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,345 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Coordinator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Data.Objects.Internal;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A coordinator is responsible for tracking state and processing result in a root or nested query
|
||||
/// result collection. The coordinator exists within a graph, and knows its Parent, (First)Child,
|
||||
/// and Next sibling. This allows the Shaper to use the coordinator as a simple state machine when
|
||||
/// consuming store reader results.
|
||||
/// </summary>
|
||||
internal abstract class Coordinator
|
||||
{
|
||||
#region state
|
||||
|
||||
/// <summary>
|
||||
/// The factory used to generate this coordinator instance. Contains delegates used
|
||||
/// by the Shaper during result enumeration.
|
||||
/// </summary>
|
||||
internal readonly CoordinatorFactory CoordinatorFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Parent coordinator (the coordinator producing rows containing this collection).
|
||||
/// If this is the root, null.
|
||||
/// </summary>
|
||||
internal readonly Coordinator Parent;
|
||||
|
||||
/// <summary>
|
||||
/// First coordinator for nested results below this collection. When reading a new row
|
||||
/// for this coordinator, we walk down to the Child.
|
||||
///
|
||||
/// NOTE:: this cannot be readonly because we can't know both the parent and the child
|
||||
/// at initialization time; we set the Child in the parent's constructor.
|
||||
/// </summary>
|
||||
public Coordinator Child
|
||||
{
|
||||
get { return _child; }
|
||||
protected set { _child = value; }
|
||||
}
|
||||
private Coordinator _child;
|
||||
|
||||
/// <summary>
|
||||
/// Next coordinator at this depth. Once we're done consuming results for this reader,
|
||||
/// we move on to this.Next.
|
||||
/// </summary>
|
||||
internal readonly Coordinator Next;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether data has been read for the collection being aggregated or yielded
|
||||
/// by this coordinator.
|
||||
/// </summary>
|
||||
public bool IsEntered
|
||||
{
|
||||
get { return _isEntered; }
|
||||
protected set { _isEntered = value; }
|
||||
}
|
||||
private bool _isEntered;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is the top level coordinator for a query.
|
||||
/// </summary>
|
||||
internal bool IsRoot
|
||||
{
|
||||
get { return null == Parent; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
protected Coordinator(CoordinatorFactory coordinatorFactory, Coordinator parent, Coordinator next)
|
||||
{
|
||||
this.CoordinatorFactory = coordinatorFactory;
|
||||
this.Parent = parent;
|
||||
this.Next = next;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// Registers this hierarchy of coordinators in the given shaper.
|
||||
/// </summary>
|
||||
internal void Initialize(Shaper shaper)
|
||||
{
|
||||
ResetCollection(shaper);
|
||||
|
||||
// Add this coordinator to the appropriate state slot in the
|
||||
// shaper so that it is available to materialization delegates.
|
||||
shaper.State[this.CoordinatorFactory.StateSlot] = this;
|
||||
|
||||
if (null != this.Child)
|
||||
{
|
||||
this.Child.Initialize(shaper);
|
||||
}
|
||||
if (null != this.Next)
|
||||
{
|
||||
this.Next.Initialize(shaper);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the maximum depth of this subtree.
|
||||
/// </summary>
|
||||
internal int MaxDistanceToLeaf()
|
||||
{
|
||||
int maxDistance = 0;
|
||||
Coordinator child = this.Child;
|
||||
while (null != child)
|
||||
{
|
||||
maxDistance = Math.Max(maxDistance, child.MaxDistanceToLeaf() + 1);
|
||||
child = child.Next;
|
||||
}
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when the current collection is finished and it's time to move to the next collection.
|
||||
/// Recursively initializes children and siblings as well.
|
||||
/// </summary>
|
||||
internal abstract void ResetCollection(Shaper shaper);
|
||||
|
||||
/// <summary>
|
||||
/// Precondition: the current row has data for the coordinator.
|
||||
/// Side-effects: updates keys currently stored in state and updates IsEntered if a new value is encountered.
|
||||
/// Determines whether the row contains the next element in this collection.
|
||||
/// </summary>
|
||||
internal bool HasNextElement(Shaper shaper)
|
||||
{
|
||||
// check if this row contains a new element for this coordinator
|
||||
bool result = false;
|
||||
|
||||
if (!this.IsEntered || !this.CoordinatorFactory.CheckKeys(shaper))
|
||||
{
|
||||
// remember initial keys values
|
||||
this.CoordinatorFactory.SetKeys(shaper);
|
||||
this.IsEntered = true;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Precondition: the current row has data and contains a new element for the coordinator.
|
||||
/// Reads the next element in this collection.
|
||||
/// </summary>
|
||||
internal abstract void ReadNextElement(Shaper shaper);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed <see cref="Coordinator"/>
|
||||
/// </summary>
|
||||
internal class Coordinator<T> : Coordinator
|
||||
{
|
||||
#region state
|
||||
|
||||
internal readonly CoordinatorFactory<T> TypedCoordinatorFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes the Current element that has been materialized (and is being populated) by this coordinator.
|
||||
/// </summary>
|
||||
internal T Current
|
||||
{
|
||||
get { return _current; }
|
||||
}
|
||||
private T _current;
|
||||
|
||||
/// <summary>
|
||||
/// For ObjectResult, aggregates all elements for in the nested collection handled by this coordinator.
|
||||
/// </summary>
|
||||
private ICollection<T> _elements;
|
||||
|
||||
/// <summary>
|
||||
/// For ObjectResult, aggregates all elements as wrapped entities for in the nested collection handled by this coordinator.
|
||||
/// </summary>
|
||||
private List<IEntityWrapper> _wrappedElements;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate called when the current nested collection has been consumed. This is necessary in Span
|
||||
/// scenarios where an EntityCollection RelatedEnd is populated only when all related entities have
|
||||
/// been materialized. This version of the close handler works with wrapped entities.
|
||||
/// </summary>
|
||||
private Action<Shaper, List<IEntityWrapper>> _handleClose;
|
||||
|
||||
/// <summary>
|
||||
/// For nested, object-layer coordinators we want to collect all the elements we find and handle them
|
||||
/// when the root coordinator advances. Otherwise we just want to return them as we find them.
|
||||
/// </summary>
|
||||
private readonly bool IsUsingElementCollection;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructors
|
||||
|
||||
internal Coordinator(CoordinatorFactory<T> coordinator, Coordinator parent, Coordinator next)
|
||||
: base(coordinator, parent, next)
|
||||
{
|
||||
this.TypedCoordinatorFactory = coordinator;
|
||||
|
||||
// generate all children
|
||||
Coordinator nextChild = null;
|
||||
foreach (var nestedCoordinator in coordinator.NestedCoordinators.Reverse())
|
||||
{
|
||||
// last child processed is first child...
|
||||
this.Child = nestedCoordinator.CreateCoordinator(this, nextChild);
|
||||
nextChild = this.Child;
|
||||
}
|
||||
|
||||
this.IsUsingElementCollection = (!this.IsRoot && typeof(T) != typeof(RecordState));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
internal override void ResetCollection(Shaper shaper)
|
||||
{
|
||||
// Check to see if anyone has registered for notification when the current coordinator
|
||||
// is reset.
|
||||
if (null != _handleClose)
|
||||
{
|
||||
_handleClose(shaper, _wrappedElements);
|
||||
_handleClose = null;
|
||||
}
|
||||
|
||||
// Reset is entered for this collection.
|
||||
this.IsEntered = false;
|
||||
|
||||
if (IsUsingElementCollection)
|
||||
{
|
||||
_elements = this.TypedCoordinatorFactory.InitializeCollection(shaper);
|
||||
_wrappedElements = new List<IEntityWrapper>();
|
||||
}
|
||||
|
||||
if (null != this.Child)
|
||||
{
|
||||
this.Child.ResetCollection(shaper);
|
||||
}
|
||||
if (null != this.Next)
|
||||
{
|
||||
this.Next.ResetCollection(shaper);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ReadNextElement(Shaper shaper)
|
||||
{
|
||||
T element;
|
||||
IEntityWrapper wrappedElement = null;
|
||||
try
|
||||
{
|
||||
if (this.TypedCoordinatorFactory.WrappedElement == null)
|
||||
{
|
||||
element = this.TypedCoordinatorFactory.Element(shaper);
|
||||
}
|
||||
else
|
||||
{
|
||||
wrappedElement = this.TypedCoordinatorFactory.WrappedElement(shaper);
|
||||
// This cast may throw, in which case it will be immediately caught
|
||||
// and the error handling expression will be used to get the appropriate error message.
|
||||
element = (T)wrappedElement.Entity;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (EntityUtil.IsCatchableExceptionType(e))
|
||||
{
|
||||
// Some errors can occur while a close handler is registered. This clears
|
||||
// out the handler so that ElementWithErrorHandling will report the correct
|
||||
// error rather than asserting on the missing close handler.
|
||||
ResetCollection(shaper);
|
||||
// call a variation of the "Element" delegate with more detailed
|
||||
// error handling (to produce a better exception message)
|
||||
element = this.TypedCoordinatorFactory.ElementWithErrorHandling(shaper);
|
||||
}
|
||||
|
||||
// rethrow
|
||||
throw;
|
||||
}
|
||||
if (IsUsingElementCollection)
|
||||
{
|
||||
_elements.Add(element);
|
||||
if (wrappedElement != null)
|
||||
{
|
||||
_wrappedElements.Add(wrappedElement);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_current = element;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the delegate called when this collection is closed. This close handler works on
|
||||
/// a collection of wrapped entities, rather than on the raw entity objects.
|
||||
/// </summary>
|
||||
internal void RegisterCloseHandler(Action<Shaper, List<IEntityWrapper>> closeHandler)
|
||||
{
|
||||
Debug.Assert(null == _handleClose, "more than one handler for a collection close 'event'");
|
||||
_handleClose = closeHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when we're disposing the enumerator;
|
||||
/// </summary>
|
||||
internal void SetCurrentToDefault()
|
||||
{
|
||||
_current = default(T);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region runtime callable code
|
||||
|
||||
// Code in this section is called from the delegates produced by the Translator. It may
|
||||
// not show up if you search using Find All References
|
||||
|
||||
/// <summary>
|
||||
/// Returns a handle to the element aggregator for this nested collection.
|
||||
/// </summary>
|
||||
private IEnumerable<T> GetElements()
|
||||
{
|
||||
return _elements;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,314 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CoordinatorFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Data.Objects.Internal;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// An immutable class used to generate new coordinators. These coordinators are used
|
||||
/// at runtime to materialize results.
|
||||
/// </summary>
|
||||
internal abstract class CoordinatorFactory
|
||||
{
|
||||
#region statics
|
||||
|
||||
/// <summary>
|
||||
/// Function of shaper that returns true; one default case when there is no explicit predicate.
|
||||
/// </summary>
|
||||
private static readonly Func<Shaper, bool> AlwaysTrue = s => true;
|
||||
|
||||
/// <summary>
|
||||
/// Function of shaper that returns false; one default case used when there is no explicit predicate.
|
||||
/// </summary>
|
||||
private static readonly Func<Shaper, bool> AlwaysFalse = s => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region state
|
||||
|
||||
/// <summary>
|
||||
/// Gets depth of the reader (0 is top-level -- which incidentally doesn't
|
||||
/// require a coordinator...
|
||||
/// </summary>
|
||||
internal readonly int Depth;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates which state slot in the Shaper.State is expected to hold the
|
||||
/// value for this nested reader result.
|
||||
/// </summary>
|
||||
internal readonly int StateSlot;
|
||||
|
||||
/// <summary>
|
||||
/// A function determining whether the current row has data for this nested result.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, bool> HasData;
|
||||
|
||||
/// <summary>
|
||||
/// A function setting key values. (the return value is irrelevant)
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, bool> SetKeys;
|
||||
|
||||
/// <summary>
|
||||
/// A function returning true if key values match the previously set values.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, bool> CheckKeys;
|
||||
|
||||
/// <summary>
|
||||
/// Nested results below this (at depth + 1)
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<CoordinatorFactory> NestedCoordinators;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is a leaf reader.
|
||||
/// </summary>
|
||||
internal readonly bool IsLeafResult;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this coordinator can be managed by a simple enumerator. A simple enumerator
|
||||
/// returns a single element per row, so the following conditions disqualify the enumerator:
|
||||
/// nested collections, data discriminators (not all rows have data), keys (not all rows have new data).
|
||||
/// </summary>
|
||||
internal readonly bool IsSimple;
|
||||
|
||||
/// <summary>
|
||||
/// For value-layer queries, the factories for all the records that we can potentially process
|
||||
/// at this level in the query result.
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory> RecordStateFactories;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
protected CoordinatorFactory(int depth, int stateSlot, Func<Shaper, bool> hasData, Func<Shaper, bool> setKeys, Func<Shaper, bool> checkKeys, CoordinatorFactory[] nestedCoordinators, RecordStateFactory[] recordStateFactories)
|
||||
{
|
||||
this.Depth = depth;
|
||||
this.StateSlot = stateSlot;
|
||||
|
||||
// figure out if there are any nested coordinators
|
||||
this.IsLeafResult = 0 == nestedCoordinators.Length;
|
||||
|
||||
// if there is no explicit 'has data' discriminator, it means all rows contain data for the coordinator
|
||||
if (hasData == null)
|
||||
{
|
||||
this.HasData = AlwaysTrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.HasData = hasData;
|
||||
}
|
||||
|
||||
// if there is no explicit set key delegate, just return true (the value is not used anyways)
|
||||
if (setKeys == null)
|
||||
{
|
||||
this.SetKeys = AlwaysTrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SetKeys = setKeys;
|
||||
}
|
||||
|
||||
// If there are no keys, it means different things depending on whether we are a leaf
|
||||
// coordinator or an inner (or 'driving') coordinator. For a leaf coordinator, it means
|
||||
// that every row is a new result. For an inner coordinator, it means that there is no
|
||||
// key to check. This should only occur where there is a SingleRowTable (in other words,
|
||||
// all rows are elements of a single child collection).
|
||||
if (checkKeys == null)
|
||||
{
|
||||
if (this.IsLeafResult)
|
||||
{
|
||||
this.CheckKeys = AlwaysFalse; // every row is a new result (the keys don't match)
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CheckKeys = AlwaysTrue; // every row belongs to a single child collection
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CheckKeys = checkKeys;
|
||||
}
|
||||
this.NestedCoordinators = new System.Collections.ObjectModel.ReadOnlyCollection<CoordinatorFactory>(nestedCoordinators);
|
||||
this.RecordStateFactories = new System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory>(recordStateFactories);
|
||||
|
||||
// Determines whether this coordinator can be handled by a 'simple' enumerator. See IsSimple for details.
|
||||
this.IsSimple = IsLeafResult && null == checkKeys && null == hasData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer handling state needed by this coordinator.
|
||||
/// </summary>
|
||||
internal abstract Coordinator CreateCoordinator(Coordinator parent, Coordinator next);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed <see cref="CoordinatorFactory"/>
|
||||
/// </summary>
|
||||
internal sealed class CoordinatorFactory<TElement> : CoordinatorFactory
|
||||
{
|
||||
#region state
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single element of the result from the given reader state object, returning the
|
||||
/// result as a wrapped entity. May be null if the element is not available as a wrapped entity.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, IEntityWrapper> WrappedElement;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single element of the result from the given reader state object.
|
||||
/// May be null if the element is available as a wrapped entity instead.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, TElement> Element;
|
||||
|
||||
/// <summary>
|
||||
/// Same as Element but uses slower patterns to provide better exception messages (e.g.
|
||||
/// using reader.GetValue + type check rather than reader.GetInt32)
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, TElement> ElementWithErrorHandling;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the collection storing results from this coordinator.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, ICollection<TElement>> InitializeCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Description of this CoordinatorFactory, used for debugging only; while this is not
|
||||
/// needed in retail code, it is pretty important because it's the only description we'll
|
||||
/// have once we compile the Expressions; debugging a problem with retail bits would be
|
||||
/// pretty hard without this.
|
||||
/// </summary>
|
||||
private readonly string Description;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
public CoordinatorFactory(int depth, int stateSlot, Expression hasData, Expression setKeys, Expression checkKeys, CoordinatorFactory[] nestedCoordinators, Expression element, Expression elementWithErrorHandling, Expression initializeCollection, RecordStateFactory[] recordStateFactories)
|
||||
: base(depth, stateSlot, CompilePredicate(hasData), CompilePredicate(setKeys), CompilePredicate(checkKeys), nestedCoordinators, recordStateFactories)
|
||||
{
|
||||
// If we are in a case where a wrapped entity is available, then use it; otherwise use the raw element.
|
||||
// However, in both cases, use the raw element for the error handling case where what we care about is
|
||||
// getting the appropriate exception message.
|
||||
if (typeof(IEntityWrapper).IsAssignableFrom(element.Type))
|
||||
{
|
||||
this.WrappedElement = Translator.Compile<IEntityWrapper>(element);
|
||||
elementWithErrorHandling = Translator.Emit_UnwrapAndEnsureType(elementWithErrorHandling, typeof(TElement));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Element = Translator.Compile<TElement>(element);
|
||||
}
|
||||
this.ElementWithErrorHandling = Translator.Compile<TElement>(elementWithErrorHandling);
|
||||
this.InitializeCollection = null == initializeCollection
|
||||
? s => new List<TElement>()
|
||||
: Translator.Compile<ICollection<TElement>>(initializeCollection);
|
||||
|
||||
this.Description = new StringBuilder()
|
||||
.Append("HasData: ")
|
||||
.AppendLine(DescribeExpression(hasData))
|
||||
.Append("SetKeys: ")
|
||||
.AppendLine(DescribeExpression(setKeys))
|
||||
.Append("CheckKeys: ")
|
||||
.AppendLine(DescribeExpression(checkKeys))
|
||||
.Append("Element: ")
|
||||
.AppendLine(DescribeExpression(element))
|
||||
.Append("ElementWithExceptionHandling: ")
|
||||
.AppendLine(DescribeExpression(elementWithErrorHandling))
|
||||
.Append("InitializeCollection: ")
|
||||
.AppendLine(DescribeExpression(initializeCollection))
|
||||
.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region expression helpers
|
||||
|
||||
/// <summary>
|
||||
/// Return the compiled expression for the predicate
|
||||
/// </summary>
|
||||
private static Func<Shaper, bool> CompilePredicate(Expression predicate)
|
||||
{
|
||||
Func<Shaper, bool> result;
|
||||
if (null == predicate)
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Translator.Compile<bool>(predicate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the expression
|
||||
/// </summary>
|
||||
private static string DescribeExpression(Expression expression)
|
||||
{
|
||||
string result;
|
||||
if (null == expression)
|
||||
{
|
||||
result = "undefined";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = expression.ToString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// Create a coordinator used for materialization of collections. Unlike the CoordinatorFactory,
|
||||
/// the Coordinator contains mutable state.
|
||||
/// </summary>
|
||||
internal override Coordinator CreateCoordinator(Coordinator parent, Coordinator next)
|
||||
{
|
||||
return new Coordinator<TElement>(this, parent, next);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the "default" record state (that is, the one we use for PreRead/PastEnd reader states
|
||||
/// </summary>
|
||||
internal RecordState GetDefaultRecordState(Shaper<RecordState> shaper)
|
||||
{
|
||||
RecordState result = null;
|
||||
if (this.RecordStateFactories.Count > 0)
|
||||
{
|
||||
//
|
||||
|
||||
result = (RecordState)shaper.State[this.RecordStateFactories[0].StateSlotNumber];
|
||||
Debug.Assert(null != result, "did you initialize the record states?");
|
||||
result.ResetToDefaultState();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="RecordState.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// The RecordState class is responsible for tracking state about a record
|
||||
/// that should be returned from a data reader.
|
||||
/// </summary>
|
||||
internal class RecordState
|
||||
{
|
||||
#region state
|
||||
|
||||
/// <summary>
|
||||
/// Where to find the static information about this record
|
||||
/// </summary>
|
||||
private readonly RecordStateFactory RecordStateFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The coordinator factory (essentially, the reader) that we're a part of.
|
||||
/// </summary>
|
||||
internal readonly CoordinatorFactory CoordinatorFactory;
|
||||
|
||||
/// <summary>
|
||||
/// True when the record is supposed to be null. (Null Structured Types...)
|
||||
/// </summary>
|
||||
private bool _pendingIsNull;
|
||||
private bool _currentIsNull;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An EntityRecordInfo, with EntityKey and EntitySet populated; set
|
||||
/// by the GatherData expression.
|
||||
/// </summary>
|
||||
private EntityRecordInfo _currentEntityRecordInfo;
|
||||
private EntityRecordInfo _pendingEntityRecordInfo;
|
||||
|
||||
/// <summary>
|
||||
/// The column values; set by the GatherData expression. Really ought
|
||||
/// to be in the Shaper.State.
|
||||
/// </summary>
|
||||
internal object[] CurrentColumnValues;
|
||||
internal object[] PendingColumnValues;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
internal RecordState(RecordStateFactory recordStateFactory, CoordinatorFactory coordinatorFactory)
|
||||
{
|
||||
this.RecordStateFactory = recordStateFactory;
|
||||
this.CoordinatorFactory = coordinatorFactory;
|
||||
this.CurrentColumnValues = new object[RecordStateFactory.ColumnCount];
|
||||
this.PendingColumnValues = new object[RecordStateFactory.ColumnCount];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// Move the PendingValues to the CurrentValues for this record and all nested
|
||||
/// records. We keep the pending values separate from the current ones because
|
||||
/// we may have a nested reader in the middle, and while we're reading forward
|
||||
/// on the nested reader we we'll blast over the pending values.
|
||||
///
|
||||
/// This should be called as part of the data reader's Read() method.
|
||||
/// </summary>
|
||||
internal void AcceptPendingValues()
|
||||
{
|
||||
object[] temp = CurrentColumnValues;
|
||||
CurrentColumnValues = PendingColumnValues;
|
||||
PendingColumnValues = temp;
|
||||
|
||||
_currentEntityRecordInfo = _pendingEntityRecordInfo;
|
||||
_pendingEntityRecordInfo = null;
|
||||
|
||||
_currentIsNull = _pendingIsNull;
|
||||
|
||||
//
|
||||
|
||||
if (RecordStateFactory.HasNestedColumns)
|
||||
{
|
||||
for (int ordinal = 0; ordinal < CurrentColumnValues.Length; ordinal++)
|
||||
{
|
||||
if (RecordStateFactory.IsColumnNested[ordinal])
|
||||
{
|
||||
RecordState recordState = CurrentColumnValues[ordinal] as RecordState;
|
||||
if (null != recordState)
|
||||
{
|
||||
recordState.AcceptPendingValues();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of columns
|
||||
/// </summary>
|
||||
internal int ColumnCount
|
||||
{
|
||||
get { return RecordStateFactory.ColumnCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the DataRecordInfo for this record; if we had an EntityRecordInfo
|
||||
/// set, then return it otherwise return the static one from the factory.
|
||||
/// </summary>
|
||||
internal DataRecordInfo DataRecordInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
DataRecordInfo result = _currentEntityRecordInfo;
|
||||
if (null == result)
|
||||
{
|
||||
result = RecordStateFactory.DataRecordInfo;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the record NULL?
|
||||
/// </summary>
|
||||
internal bool IsNull
|
||||
{
|
||||
get { return _currentIsNull; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of DataReader's GetBytes method
|
||||
/// </summary>
|
||||
internal long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
|
||||
{
|
||||
byte[] byteValue = (byte[])CurrentColumnValues[ordinal];
|
||||
int valueLength = byteValue.Length;
|
||||
int sourceOffset = (int)dataOffset;
|
||||
int byteCount = valueLength - sourceOffset;
|
||||
|
||||
if (null != buffer)
|
||||
{
|
||||
byteCount = Math.Min(byteCount, length);
|
||||
|
||||
if (0 < byteCount)
|
||||
{
|
||||
Buffer.BlockCopy(byteValue, sourceOffset, buffer, bufferOffset, byteCount);
|
||||
}
|
||||
}
|
||||
return Math.Max(0, byteCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of DataReader's GetChars method
|
||||
/// </summary>
|
||||
internal long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
|
||||
{
|
||||
string stringValue = CurrentColumnValues[ordinal] as string;
|
||||
char[] charValue;
|
||||
|
||||
if (stringValue != null)
|
||||
{
|
||||
charValue = stringValue.ToCharArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
charValue = (char[])CurrentColumnValues[ordinal];
|
||||
}
|
||||
|
||||
int valueLength = charValue.Length;
|
||||
int sourceOffset = (int)dataOffset;
|
||||
int charCount = valueLength - sourceOffset;
|
||||
|
||||
if (null != buffer)
|
||||
{
|
||||
charCount = Math.Min(charCount, length);
|
||||
|
||||
if (0 < charCount)
|
||||
{
|
||||
Buffer.BlockCopy(charValue, sourceOffset * System.Text.UnicodeEncoding.CharSize,
|
||||
buffer, bufferOffset * System.Text.UnicodeEncoding.CharSize,
|
||||
charCount * System.Text.UnicodeEncoding.CharSize);
|
||||
}
|
||||
}
|
||||
return Math.Max(0, charCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the name of the column at the ordinal specified.
|
||||
/// </summary>
|
||||
internal string GetName(int ordinal)
|
||||
{
|
||||
// Some folks are picky about the exception we throw
|
||||
if (ordinal < 0 || ordinal >= RecordStateFactory.ColumnCount)
|
||||
{
|
||||
throw EntityUtil.ArgumentOutOfRange("ordinal");
|
||||
}
|
||||
return RecordStateFactory.ColumnNames[ordinal];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is where the GetOrdinal method for DbDataReader/DbDataRecord end up.
|
||||
/// </summary>
|
||||
internal int GetOrdinal(string name)
|
||||
{
|
||||
return RecordStateFactory.FieldNameLookup.GetOrdinal(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the type of the column at the ordinal specified.
|
||||
/// </summary>
|
||||
internal TypeUsage GetTypeUsage(int ordinal)
|
||||
{
|
||||
return RecordStateFactory.TypeUsages[ordinal];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the column at the ordinal specified is
|
||||
/// a record or reader column that requires special handling.
|
||||
/// </summary>
|
||||
internal bool IsNestedObject(int ordinal)
|
||||
{
|
||||
return RecordStateFactory.IsColumnNested[ordinal];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever we hand this record state out as the default state for
|
||||
/// a data reader; we will have already handled any existing data back to
|
||||
/// the previous group of records (that is, we couldn't be using it from two
|
||||
/// distinct readers at the same time).
|
||||
/// </summary>
|
||||
internal void ResetToDefaultState()
|
||||
{
|
||||
_currentEntityRecordInfo = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region called from Shaper's Element Expression
|
||||
|
||||
/// <summary>
|
||||
/// Called from the Element expression on the Coordinator to gather all
|
||||
/// the data for the record; we just turn around and call the expression
|
||||
/// we build on the RecordStateFactory.
|
||||
/// </summary>
|
||||
internal RecordState GatherData(Shaper shaper)
|
||||
{
|
||||
RecordStateFactory.GatherData(shaper);
|
||||
_pendingIsNull = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the GatherData expression to set the data for the
|
||||
/// specified column value
|
||||
/// </summary>
|
||||
internal bool SetColumnValue(int ordinal, object value)
|
||||
{
|
||||
PendingColumnValues[ordinal] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the GatherData expression to set the data for the
|
||||
/// EntityRecordInfo
|
||||
/// </summary>
|
||||
internal bool SetEntityRecordInfo(EntityKey entityKey, EntitySet entitySet)
|
||||
{
|
||||
_pendingEntityRecordInfo = new EntityRecordInfo(this.RecordStateFactory.DataRecordInfo, entityKey, entitySet);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the Element expression on the Coordinator to indicate that
|
||||
/// the record should be NULL.
|
||||
/// </summary>
|
||||
internal RecordState SetNullRecord(Shaper shaper)
|
||||
{
|
||||
//
|
||||
|
||||
|
||||
for (int i = 0; i < PendingColumnValues.Length; i++)
|
||||
{
|
||||
PendingColumnValues[i] = DBNull.Value;
|
||||
}
|
||||
_pendingEntityRecordInfo = null; // the default is already setup correctly on the record state factory
|
||||
_pendingIsNull = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="recordstatefactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// An immutable class used to generate new RecordStates, which are used
|
||||
/// at runtime to produce value-layer (aka DataReader) results.
|
||||
///
|
||||
/// Contains static information collected by the Translator visitor. The
|
||||
/// expressions produced by the Translator are compiled. The RecordStates
|
||||
/// will refer to this object for all static information.
|
||||
///
|
||||
/// This class is cached in the query cache as part of the CoordinatorFactory.
|
||||
/// </summary>
|
||||
internal class RecordStateFactory
|
||||
{
|
||||
#region state
|
||||
|
||||
/// <summary>
|
||||
/// Indicates which state slot in the Shaper.State is expected to hold the
|
||||
/// value for this record state. Each unique record shape has it's own state
|
||||
/// slot.
|
||||
/// </summary>
|
||||
internal readonly int StateSlotNumber;
|
||||
|
||||
/// <summary>
|
||||
/// How many column values we have to reserve space for in this record.
|
||||
/// </summary>
|
||||
internal readonly int ColumnCount;
|
||||
|
||||
/// <summary>
|
||||
/// The DataRecordInfo we must return for this record. If the record represents
|
||||
/// an entity, this will be used to construct a unique EntityRecordInfo with the
|
||||
/// EntityKey and EntitySet for the entity.
|
||||
/// </summary>
|
||||
internal readonly DataRecordInfo DataRecordInfo;
|
||||
|
||||
/// <summary>
|
||||
/// A function that will gather the data for the row and store it on the record state.
|
||||
/// </summary>
|
||||
internal readonly Func<Shaper, bool> GatherData;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of nested records for this record, such as a complex type that is
|
||||
/// part of an entity. This does not include records that are part of a nested
|
||||
/// collection, however.
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory> NestedRecordStateFactories;
|
||||
|
||||
/// <summary>
|
||||
/// The name for each column.
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<string> ColumnNames;
|
||||
|
||||
/// <summary>
|
||||
/// The type usage information for each column.
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<TypeUsage> TypeUsages;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks which columns might need special handling (nested readers/records)
|
||||
/// </summary>
|
||||
internal readonly System.Collections.ObjectModel.ReadOnlyCollection<bool> IsColumnNested;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks whether there are ANY columns that need special handling.
|
||||
/// </summary>
|
||||
internal readonly bool HasNestedColumns;
|
||||
|
||||
/// <summary>
|
||||
/// A helper class to make the translation from name->ordinal.
|
||||
/// </summary>
|
||||
internal readonly FieldNameLookup FieldNameLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Description of this RecordStateFactory, used for debugging only; while this
|
||||
/// is not needed in retail code, it is pretty important because it's the only
|
||||
/// description we'll have once we compile the Expressions; debugging a problem
|
||||
/// with retail bits would be pretty hard without this.
|
||||
/// </summary>
|
||||
private readonly string Description;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
public RecordStateFactory(int stateSlotNumber, int columnCount, RecordStateFactory[] nestedRecordStateFactories, DataRecordInfo dataRecordInfo, Expression gatherData, string[] propertyNames, TypeUsage[] typeUsages)
|
||||
{
|
||||
this.StateSlotNumber = stateSlotNumber;
|
||||
this.ColumnCount = columnCount;
|
||||
this.NestedRecordStateFactories = new System.Collections.ObjectModel.ReadOnlyCollection<RecordStateFactory>(nestedRecordStateFactories);
|
||||
this.DataRecordInfo = dataRecordInfo;
|
||||
this.GatherData = Translator.Compile<bool>(gatherData);
|
||||
this.Description = gatherData.ToString();
|
||||
this.ColumnNames = new System.Collections.ObjectModel.ReadOnlyCollection<string>(propertyNames);
|
||||
this.TypeUsages = new System.Collections.ObjectModel.ReadOnlyCollection<TypeUsage>(typeUsages);
|
||||
|
||||
this.FieldNameLookup = new FieldNameLookup(this.ColumnNames, -1);
|
||||
|
||||
// pre-compute the nested objects from typeUsage, for performance
|
||||
bool[] isColumnNested = new bool[columnCount];
|
||||
|
||||
for (int ordinal = 0; ordinal < columnCount; ordinal++)
|
||||
{
|
||||
switch (typeUsages[ordinal].EdmType.BuiltInTypeKind)
|
||||
{
|
||||
case BuiltInTypeKind.EntityType:
|
||||
case BuiltInTypeKind.ComplexType:
|
||||
case BuiltInTypeKind.RowType:
|
||||
case BuiltInTypeKind.CollectionType:
|
||||
isColumnNested[ordinal] = true;
|
||||
this.HasNestedColumns = true;
|
||||
break;
|
||||
default:
|
||||
isColumnNested[ordinal] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.IsColumnNested = new System.Collections.ObjectModel.ReadOnlyCollection<bool>(isColumnNested);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "public" surface area
|
||||
|
||||
/// <summary>
|
||||
/// It's GO time, create the record state.
|
||||
/// </summary>
|
||||
internal RecordState Create(CoordinatorFactory coordinatorFactory)
|
||||
{
|
||||
return new RecordState(this, coordinatorFactory);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="RecordStateScratchpad.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in the Translator to aggregate information about a (nested) record
|
||||
/// state. After the translator visits the columnMaps, it will compile
|
||||
/// the recordState(s) which produces an immutable RecordStateFactory that
|
||||
/// can be shared amongst many query instances.
|
||||
/// </summary>
|
||||
internal class RecordStateScratchpad
|
||||
{
|
||||
private int _stateSlotNumber;
|
||||
internal int StateSlotNumber
|
||||
{
|
||||
get { return _stateSlotNumber; }
|
||||
set { _stateSlotNumber = value; }
|
||||
}
|
||||
|
||||
private int _columnCount;
|
||||
internal int ColumnCount
|
||||
{
|
||||
get { return _columnCount; }
|
||||
set { _columnCount = value; }
|
||||
}
|
||||
|
||||
private DataRecordInfo _dataRecordInfo;
|
||||
internal DataRecordInfo DataRecordInfo
|
||||
{
|
||||
get { return _dataRecordInfo; }
|
||||
set { _dataRecordInfo = value; }
|
||||
}
|
||||
|
||||
private Expression _gatherData;
|
||||
internal Expression GatherData
|
||||
{
|
||||
get { return _gatherData; }
|
||||
set { _gatherData = value; }
|
||||
}
|
||||
|
||||
private string[] _propertyNames;
|
||||
internal string[] PropertyNames
|
||||
{
|
||||
get { return _propertyNames; }
|
||||
set { _propertyNames = value; }
|
||||
}
|
||||
private TypeUsage[] _typeUsages;
|
||||
internal TypeUsage[] TypeUsages
|
||||
{
|
||||
get { return _typeUsages; }
|
||||
set { _typeUsages = value; }
|
||||
}
|
||||
|
||||
private List<RecordStateScratchpad> _nestedRecordStateScratchpads = new List<RecordStateScratchpad>();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
internal RecordStateFactory Compile()
|
||||
{
|
||||
RecordStateFactory[] nestedRecordStateFactories = new RecordStateFactory[_nestedRecordStateScratchpads.Count];
|
||||
for (int i = 0; i < nestedRecordStateFactories.Length; i++)
|
||||
{
|
||||
nestedRecordStateFactories[i] = _nestedRecordStateScratchpads[i].Compile();
|
||||
}
|
||||
|
||||
RecordStateFactory result = (RecordStateFactory)Activator.CreateInstance(typeof(RecordStateFactory), new object[] {
|
||||
this.StateSlotNumber,
|
||||
this.ColumnCount,
|
||||
nestedRecordStateFactories,
|
||||
this.DataRecordInfo,
|
||||
this.GatherData,
|
||||
this.PropertyNames,
|
||||
this.TypeUsages
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ShaperFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.QueryCache;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Objects;
|
||||
using System.Data.Objects.Internal;
|
||||
using System.Data.Query.InternalTrees;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
/// <summary>
|
||||
/// An immutable type used to generate Shaper instances.
|
||||
/// </summary>
|
||||
internal abstract class ShaperFactory
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
internal static ShaperFactory Create(Type elementType, QueryCacheManager cacheManager, ColumnMap columnMap, MetadataWorkspace metadata, SpanIndex spanInfo, MergeOption mergeOption, bool valueLayer)
|
||||
{
|
||||
ShaperFactoryCreator creator = (ShaperFactoryCreator)Activator.CreateInstance(typeof(TypedShaperFactoryCreator<>).MakeGenericType(elementType));
|
||||
return creator.TypedCreate(cacheManager, columnMap, metadata, spanInfo, mergeOption, valueLayer);
|
||||
}
|
||||
|
||||
private abstract class ShaperFactoryCreator
|
||||
{
|
||||
internal abstract ShaperFactory TypedCreate(QueryCacheManager cacheManager, ColumnMap columnMap, MetadataWorkspace metadata, SpanIndex spanInfo, MergeOption mergeOption, bool valueLayer);
|
||||
}
|
||||
|
||||
private sealed class TypedShaperFactoryCreator<T> : ShaperFactoryCreator
|
||||
{
|
||||
public TypedShaperFactoryCreator() {}
|
||||
internal override ShaperFactory TypedCreate(QueryCacheManager cacheManager, ColumnMap columnMap, MetadataWorkspace metadata, SpanIndex spanInfo, MergeOption mergeOption, bool valueLayer)
|
||||
{
|
||||
return Translator.TranslateColumnMap<T>(cacheManager, columnMap, metadata, spanInfo, mergeOption, valueLayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed ShaperFactory
|
||||
/// </summary>
|
||||
internal class ShaperFactory<T> : ShaperFactory
|
||||
{
|
||||
private readonly int _stateCount;
|
||||
private readonly CoordinatorFactory<T> _rootCoordinatorFactory;
|
||||
private readonly Action _checkPermissions;
|
||||
private readonly MergeOption _mergeOption;
|
||||
|
||||
internal ShaperFactory(int stateCount, CoordinatorFactory<T> rootCoordinatorFactory, Action checkPermissions, MergeOption mergeOption)
|
||||
{
|
||||
_stateCount = stateCount;
|
||||
_rootCoordinatorFactory = rootCoordinatorFactory;
|
||||
_checkPermissions = checkPermissions;
|
||||
_mergeOption = mergeOption;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory method to create the Shaper for Object Layer queries.
|
||||
/// </summary>
|
||||
internal Shaper<T> Create(DbDataReader reader, ObjectContext context, MetadataWorkspace workspace, MergeOption mergeOption, bool readerOwned)
|
||||
{
|
||||
Debug.Assert(mergeOption == _mergeOption, "executing a query with a different mergeOption than was used to compile the delegate");
|
||||
return new Shaper<T>(reader, context, workspace, mergeOption, _stateCount, _rootCoordinatorFactory, _checkPermissions, readerOwned);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
065d41fc19580d3063cdc6b3810c1c3bb12061c5
|
@@ -0,0 +1,78 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Util.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Mapping;
|
||||
namespace System.Data.Common.Internal.Materialization
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a mapping to CLR type for the given EDM type. Assumes the MetadataWorkspace has no
|
||||
/// </summary>
|
||||
internal static ObjectTypeMapping GetObjectMapping(EdmType type, MetadataWorkspace workspace)
|
||||
{
|
||||
// Check if the workspace has cspace item collection registered with it. If not, then its a case
|
||||
// of public materializer trying to create objects from PODR or EntityDataReader with no context.
|
||||
ItemCollection collection;
|
||||
if (workspace.TryGetItemCollection(DataSpace.CSpace, out collection))
|
||||
{
|
||||
return (ObjectTypeMapping)workspace.GetMap(type, DataSpace.OCSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
EdmType ospaceType;
|
||||
EdmType cspaceType;
|
||||
// If its a case of EntityDataReader with no context, the typeUsage which is passed in must contain
|
||||
// a cspace type. We need to look up an OSpace type in the ospace item collection and then create
|
||||
// ocMapping
|
||||
if (type.DataSpace == DataSpace.CSpace)
|
||||
{
|
||||
// if its a primitive type, then the names will be different for CSpace type and OSpace type
|
||||
if (Helper.IsPrimitiveType(type))
|
||||
{
|
||||
ospaceType = workspace.GetMappedPrimitiveType(((PrimitiveType)type).PrimitiveTypeKind, DataSpace.OSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Metadata will throw if there is no item with this identity present.
|
||||
// Is this exception fine or does object materializer code wants to wrap and throw a new exception
|
||||
ospaceType = workspace.GetItem<EdmType>(type.FullName, DataSpace.OSpace);
|
||||
}
|
||||
cspaceType = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In case of PODR, there is no cspace at all. We must create a fake ocmapping, with ospace types
|
||||
// on both the ends
|
||||
ospaceType = type;
|
||||
cspaceType = type;
|
||||
}
|
||||
|
||||
// This condition must be hit only when someone is trying to materialize a legacy data reader and we
|
||||
// don't have the CSpace metadata.
|
||||
if (!Helper.IsPrimitiveType(ospaceType) && !Helper.IsEntityType(ospaceType) && !Helper.IsComplexType(ospaceType))
|
||||
{
|
||||
throw EntityUtil.MaterializerUnsupportedType();
|
||||
}
|
||||
|
||||
ObjectTypeMapping typeMapping;
|
||||
|
||||
if (Helper.IsPrimitiveType(ospaceType))
|
||||
{
|
||||
typeMapping = new ObjectTypeMapping(ospaceType, cspaceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeMapping = DefaultObjectMappingItemCollection.LoadObjectMapping(cspaceType, ospaceType, null);
|
||||
}
|
||||
|
||||
return typeMapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,253 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="MultipartIdentifier.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Data.Common.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Copied from System.Data.dll
|
||||
/// </summary>
|
||||
internal static class MultipartIdentifier
|
||||
{
|
||||
private const int MaxParts = 4;
|
||||
internal const int ServerIndex = 0;
|
||||
internal const int CatalogIndex = 1;
|
||||
internal const int SchemaIndex = 2;
|
||||
internal const int TableIndex = 3;
|
||||
|
||||
private enum MPIState
|
||||
{
|
||||
MPI_Value,
|
||||
MPI_ParseNonQuote,
|
||||
MPI_LookForSeparator,
|
||||
MPI_LookForNextCharOrSeparator,
|
||||
MPI_ParseQuote,
|
||||
MPI_RightQuote,
|
||||
}
|
||||
|
||||
private static void IncrementStringCount(List<string> ary, ref int position)
|
||||
{
|
||||
++position;
|
||||
ary.Add(string.Empty);
|
||||
}
|
||||
|
||||
private static bool IsWhitespace(char ch)
|
||||
{
|
||||
return Char.IsWhiteSpace(ch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core function for parsing the multipart identifer string.
|
||||
/// Note: Left quote strings need to correspond 1 to 1 with the right quote strings
|
||||
/// example: "ab" "cd", passed in for the left and the right quote
|
||||
/// would set a or b as a starting quote character.
|
||||
/// If a is the starting quote char then c would be the ending quote char
|
||||
/// otherwise if b is the starting quote char then d would be the ending quote character.
|
||||
/// </summary>
|
||||
/// <param name="name">string to parse</param>
|
||||
/// <param name="leftQuote">set of characters which are valid quoteing characters to initiate a quote</param>
|
||||
/// <param name="rightQuote">set of characters which are valid to stop a quote, array index's correspond to the the leftquote array.</param>
|
||||
/// <param name="separator">separator to use</param>
|
||||
/// <returns></returns>
|
||||
internal static List<string> ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, char separator)
|
||||
{
|
||||
Debug.Assert(-1 == leftQuote.IndexOf(separator) && -1 == rightQuote.IndexOf(separator) && leftQuote.Length == rightQuote.Length, "Incorrect usage of quotes");
|
||||
|
||||
List<string> parsedNames = new List<string>();
|
||||
parsedNames.Add(null);
|
||||
int stringCount = 0; // index of current string in the list
|
||||
MPIState state = MPIState.MPI_Value; // Initalize the starting state
|
||||
|
||||
StringBuilder sb = new StringBuilder(name.Length); // String buffer to hold the string being currently built, init the string builder so it will never be resized
|
||||
StringBuilder whitespaceSB = null; // String buffer to hold white space used when parsing nonquoted strings 'a b . c d' = 'a b' and 'c d'
|
||||
char rightQuoteChar = ' '; // Right quote character to use given the left quote character found.
|
||||
for (int index = 0; index < name.Length; ++index)
|
||||
{
|
||||
char testchar = name[index];
|
||||
switch (state)
|
||||
{
|
||||
case MPIState.MPI_Value:
|
||||
{
|
||||
int quoteIndex;
|
||||
if (IsWhitespace(testchar))
|
||||
{ // Is White Space then skip the whitespace
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (testchar == separator)
|
||||
{ // If we found a separator, no string was found, initalize the string we are parsing to Empty and the next one to Empty.
|
||||
// This is NOT a redundent setting of string.Empty it solves the case where we are parsing ".foo" and we should be returning null, null, empty, foo
|
||||
parsedNames[stringCount] = string.Empty;
|
||||
IncrementStringCount(parsedNames, ref stringCount);
|
||||
}
|
||||
else
|
||||
if (-1 != (quoteIndex = leftQuote.IndexOf(testchar)))
|
||||
{ // If we are a left quote
|
||||
rightQuoteChar = rightQuote[quoteIndex]; // record the corresponding right quote for the left quote
|
||||
sb.Length = 0;
|
||||
state = MPIState.MPI_ParseQuote;
|
||||
}
|
||||
else
|
||||
if (-1 != rightQuote.IndexOf(testchar))
|
||||
{ // If we shouldn't see a right quote
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Length = 0;
|
||||
sb.Append(testchar);
|
||||
state = MPIState.MPI_ParseNonQuote;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPIState.MPI_ParseNonQuote:
|
||||
{
|
||||
if (testchar == separator)
|
||||
{
|
||||
parsedNames[stringCount] = sb.ToString(); // set the currently parsed string
|
||||
IncrementStringCount(parsedNames, ref stringCount);
|
||||
state = MPIState.MPI_Value;
|
||||
}
|
||||
else // Quotes are not valid inside a non-quoted name
|
||||
if (-1 != rightQuote.IndexOf(testchar))
|
||||
{
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
else
|
||||
if (-1 != leftQuote.IndexOf(testchar))
|
||||
{
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
else
|
||||
if (IsWhitespace(testchar))
|
||||
{ // If it is Whitespace
|
||||
parsedNames[stringCount] = sb.ToString(); // Set the currently parsed string
|
||||
if (null == whitespaceSB)
|
||||
{
|
||||
whitespaceSB = new StringBuilder();
|
||||
}
|
||||
whitespaceSB.Length = 0;
|
||||
whitespaceSB.Append(testchar); // start to record the white space, if we are parsing a name like "name with space" we should return "name with space"
|
||||
state = MPIState.MPI_LookForNextCharOrSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(testchar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPIState.MPI_LookForNextCharOrSeparator:
|
||||
{
|
||||
if (!IsWhitespace(testchar))
|
||||
{ // If it is not whitespace
|
||||
if (testchar == separator)
|
||||
{
|
||||
IncrementStringCount(parsedNames, ref stringCount);
|
||||
state = MPIState.MPI_Value;
|
||||
}
|
||||
else
|
||||
{ // If its not a separator and not whitespace
|
||||
sb.Append(whitespaceSB);
|
||||
sb.Append(testchar);
|
||||
parsedNames[stringCount] = sb.ToString(); // Need to set the name here in case the string ends here.
|
||||
state = MPIState.MPI_ParseNonQuote;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
whitespaceSB.Append(testchar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPIState.MPI_ParseQuote:
|
||||
{
|
||||
if (testchar == rightQuoteChar)
|
||||
{ // if se are on a right quote see if we are escapeing the right quote or ending the quoted string
|
||||
state = MPIState.MPI_RightQuote;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(testchar); // Append what we are currently parsing
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPIState.MPI_RightQuote:
|
||||
{
|
||||
if (testchar == rightQuoteChar)
|
||||
{ // If the next char is a another right quote then we were escapeing the right quote
|
||||
sb.Append(testchar);
|
||||
state = MPIState.MPI_ParseQuote;
|
||||
}
|
||||
else
|
||||
if (testchar == separator)
|
||||
{ // If its a separator then record what we've parsed
|
||||
parsedNames[stringCount] = sb.ToString();
|
||||
IncrementStringCount(parsedNames, ref stringCount);
|
||||
state = MPIState.MPI_Value;
|
||||
}
|
||||
else
|
||||
if (!IsWhitespace(testchar))
|
||||
{ // If it is not white space we got problems
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
else
|
||||
{ // It is a whitespace character so the following char should be whitespace, separator, or end of string anything else is bad
|
||||
parsedNames[stringCount] = sb.ToString();
|
||||
state = MPIState.MPI_LookForSeparator;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPIState.MPI_LookForSeparator:
|
||||
{
|
||||
if (!IsWhitespace(testchar))
|
||||
{ // If it is not whitespace
|
||||
if (testchar == separator)
|
||||
{ // If it is a separator
|
||||
IncrementStringCount(parsedNames, ref stringCount);
|
||||
state = MPIState.MPI_Value;
|
||||
}
|
||||
else
|
||||
{ // Othewise not a separator
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve final states after parsing the string
|
||||
switch (state)
|
||||
{
|
||||
case MPIState.MPI_Value: // These states require no extra action
|
||||
case MPIState.MPI_LookForSeparator:
|
||||
case MPIState.MPI_LookForNextCharOrSeparator:
|
||||
break;
|
||||
|
||||
case MPIState.MPI_ParseNonQuote: // Dump what ever was parsed
|
||||
case MPIState.MPI_RightQuote:
|
||||
parsedNames[stringCount] = sb.ToString();
|
||||
break;
|
||||
|
||||
case MPIState.MPI_ParseQuote: // Invalid Ending States
|
||||
default:
|
||||
throw EntityUtil.ADP_InvalidMultipartNameDelimiterUsage();
|
||||
}
|
||||
return parsedNames;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user