Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1 @@
065d41fc19580d3063cdc6b3810c1c3bb12061c5

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}