2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="EntityCommandDefinition.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
2017-08-21 15:34:15 +00:00
// @owner Microsoft
// @backupOwner Microsoft
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
namespace System.Data.EntityClient {
using System.Collections.Generic ;
using System.Data.Common ;
using System.Data.Common.CommandTrees ;
using System.Data.Common.Utils ;
using System.Data.Mapping ;
using System.Data.Metadata.Edm ;
using System.Data.Query.InternalTrees ;
using System.Data.Query.PlanCompiler ;
using System.Data.Query.ResultAssembly ;
using System.Diagnostics ;
using System.Linq ;
using System.Text ;
/// <summary>
/// An aggregate Command Definition used by the EntityClient layers. This is an aggregator
/// object that represent information from multiple underlying provider commands.
/// </summary>
sealed internal class EntityCommandDefinition : DbCommandDefinition {
#region internal state
/// <summary>
/// nested store command definitions
/// </summary>
private readonly List < DbCommandDefinition > _mappedCommandDefinitions ;
/// <summary>
/// generates column map for the store result reader
/// </summary>
private readonly IColumnMapGenerator [ ] _columnMapGenerators ;
/// <summary>
/// list of the parameters that the resulting command should have
/// </summary>
private readonly System . Collections . ObjectModel . ReadOnlyCollection < EntityParameter > _parameters ;
/// <summary>
/// Set of entity sets exposed in the command.
/// </summary>
private readonly Set < EntitySet > _entitySets ;
#endregion
#region constructors
/// <summary>
/// don't let this be constructed publicly;
/// </summary>
/// <exception cref="EntityCommandCompilationException">Cannot prepare the command definition for execution; consult the InnerException for more information.</exception>
/// <exception cref="NotSupportedException">The ADO.NET Data Provider you are using does not support CommandTrees.</exception>
internal EntityCommandDefinition ( DbProviderFactory storeProviderFactory , DbCommandTree commandTree ) {
EntityUtil . CheckArgumentNull ( storeProviderFactory , "storeProviderFactory" ) ;
EntityUtil . CheckArgumentNull ( commandTree , "commandTree" ) ;
DbProviderServices storeProviderServices = DbProviderServices . GetProviderServices ( storeProviderFactory ) ;
try {
if ( DbCommandTreeKind . Query = = commandTree . CommandTreeKind ) {
// Next compile the plan for the command tree
List < ProviderCommandInfo > mappedCommandList = new List < ProviderCommandInfo > ( ) ;
ColumnMap columnMap ;
int columnCount ;
PlanCompiler . Compile ( commandTree , out mappedCommandList , out columnMap , out columnCount , out _entitySets ) ;
_columnMapGenerators = new IColumnMapGenerator [ ] { new ConstantColumnMapGenerator ( columnMap , columnCount ) } ;
// Note: we presume that the first item in the ProviderCommandInfo is the root node;
Debug . Assert ( mappedCommandList . Count > 0 , "empty providerCommandInfo collection and no exception?" ) ; // this shouldn't ever happen.
// Then, generate the store commands from the resulting command tree(s)
_mappedCommandDefinitions = new List < DbCommandDefinition > ( mappedCommandList . Count ) ;
foreach ( ProviderCommandInfo providerCommandInfo in mappedCommandList ) {
DbCommandDefinition providerCommandDefinition = storeProviderServices . CreateCommandDefinition ( providerCommandInfo . CommandTree ) ;
if ( null = = providerCommandDefinition ) {
throw EntityUtil . ProviderIncompatible ( System . Data . Entity . Strings . ProviderReturnedNullForCreateCommandDefinition ) ;
}
_mappedCommandDefinitions . Add ( providerCommandDefinition ) ;
}
}
else {
Debug . Assert ( DbCommandTreeKind . Function = = commandTree . CommandTreeKind , "only query and function command trees are supported" ) ;
DbFunctionCommandTree entityCommandTree = ( DbFunctionCommandTree ) commandTree ;
// Retrieve mapping and metadata information for the function import.
FunctionImportMappingNonComposable mapping = GetTargetFunctionMapping ( entityCommandTree ) ;
IList < FunctionParameter > returnParameters = entityCommandTree . EdmFunction . ReturnParameters ;
int resultSetCount = returnParameters . Count > 1 ? returnParameters . Count : 1 ;
_columnMapGenerators = new IColumnMapGenerator [ resultSetCount ] ;
TypeUsage storeResultType = DetermineStoreResultType ( entityCommandTree . MetadataWorkspace , mapping , 0 , out _columnMapGenerators [ 0 ] ) ;
for ( int i = 1 ; i < resultSetCount ; i + + )
{
DetermineStoreResultType ( entityCommandTree . MetadataWorkspace , mapping , i , out _columnMapGenerators [ i ] ) ;
}
// Copy over parameters (this happens through a more indirect route in the plan compiler, but
// it happens nonetheless)
List < KeyValuePair < string , TypeUsage > > providerParameters = new List < KeyValuePair < string , TypeUsage > > ( ) ;
foreach ( KeyValuePair < string , TypeUsage > parameter in entityCommandTree . Parameters )
{
providerParameters . Add ( parameter ) ;
}
// Construct store command tree usage.
DbFunctionCommandTree providerCommandTree = new DbFunctionCommandTree ( entityCommandTree . MetadataWorkspace , DataSpace . SSpace ,
mapping . TargetFunction , storeResultType , providerParameters ) ;
DbCommandDefinition storeCommandDefinition = storeProviderServices . CreateCommandDefinition ( providerCommandTree ) ;
_mappedCommandDefinitions = new List < DbCommandDefinition > ( 1 ) { storeCommandDefinition } ;
EntitySet firstResultEntitySet = mapping . FunctionImport . EntitySets . FirstOrDefault ( ) ;
if ( firstResultEntitySet ! = null )
{
_entitySets = new Set < EntitySet > ( ) ;
_entitySets . Add ( mapping . FunctionImport . EntitySets . FirstOrDefault ( ) ) ;
_entitySets . MakeReadOnly ( ) ;
}
}
// Finally, build a list of the parameters that the resulting command should have;
List < EntityParameter > parameterList = new List < EntityParameter > ( ) ;
foreach ( KeyValuePair < string , TypeUsage > queryParameter in commandTree . Parameters ) {
EntityParameter parameter = CreateEntityParameterFromQueryParameter ( queryParameter ) ;
parameterList . Add ( parameter ) ;
}
_parameters = new System . Collections . ObjectModel . ReadOnlyCollection < EntityParameter > ( parameterList ) ;
}
catch ( EntityCommandCompilationException ) {
// No need to re-wrap EntityCommandCompilationException
throw ;
}
catch ( Exception e ) {
// we should not be wrapping all exceptions
if ( EntityUtil . IsCatchableExceptionType ( e ) ) {
// we don't wan't folks to have to know all the various types of exceptions that can
// occur, so we just rethrow a CommandDefinitionException and make whatever we caught
// the inner exception of it.
throw EntityUtil . CommandCompilation ( System . Data . Entity . Strings . EntityClient_CommandDefinitionPreparationFailed , e ) ;
}
throw ;
}
}
/// <summary>
/// Determines the store type for a function import.
/// </summary>
private TypeUsage DetermineStoreResultType ( MetadataWorkspace workspace , FunctionImportMappingNonComposable mapping , int resultSetIndex , out IColumnMapGenerator columnMapGenerator ) {
// Determine column maps and infer result types for the mapped function. There are four varieties:
// Collection(Entity)
// Collection(PrimitiveType)
// Collection(ComplexType)
// No result type
TypeUsage storeResultType ;
{
StructuralType baseStructuralType ;
EdmFunction functionImport = mapping . FunctionImport ;
// Collection(Entity) or Collection(ComplexType)
if ( MetadataHelper . TryGetFunctionImportReturnType < StructuralType > ( functionImport , resultSetIndex , out baseStructuralType ) )
{
ValidateEdmResultType ( baseStructuralType , functionImport ) ;
//Note: Defensive check for historic reasons, we expect functionImport.EntitySets.Count > resultSetIndex
EntitySet entitySet = functionImport . EntitySets . Count > resultSetIndex ? functionImport . EntitySets [ resultSetIndex ] : null ;
columnMapGenerator = new FunctionColumnMapGenerator ( mapping , resultSetIndex , entitySet , baseStructuralType ) ;
// We don't actually know the return type for the stored procedure, but we can infer
// one based on the mapping (i.e.: a column for every property of the mapped types
// and for all discriminator columns)
storeResultType = mapping . GetExpectedTargetResultType ( workspace , resultSetIndex ) ;
}
// Collection(PrimitiveType)
else
{
FunctionParameter returnParameter = MetadataHelper . GetReturnParameter ( functionImport , resultSetIndex ) ;
if ( returnParameter ! = null & & returnParameter . TypeUsage ! = null )
{
// Get metadata description of the return type
storeResultType = returnParameter . TypeUsage ;
Debug . Assert ( storeResultType . EdmType . BuiltInTypeKind = = BuiltInTypeKind . CollectionType , "FunctionImport currently supports only collection result type" ) ;
TypeUsage elementType = ( ( CollectionType ) storeResultType . EdmType ) . TypeUsage ;
Debug . Assert ( Helper . IsScalarType ( elementType . EdmType )
, "FunctionImport supports only Collection(Entity), Collection(Enum) and Collection(Primitive)" ) ;
// Build collection column map where the first column of the store result is assumed
// to contain the primitive type values.
ScalarColumnMap scalarColumnMap = new ScalarColumnMap ( elementType , string . Empty , 0 , 0 ) ;
SimpleCollectionColumnMap collectionColumnMap = new SimpleCollectionColumnMap ( storeResultType ,
string . Empty , scalarColumnMap , null , null ) ;
columnMapGenerator = new ConstantColumnMapGenerator ( collectionColumnMap , 1 ) ;
}
// No result type
else
{
storeResultType = null ;
columnMapGenerator = new ConstantColumnMapGenerator ( null , 0 ) ;
}
}
}
return storeResultType ;
}
/// <summary>
/// Handles the following negative scenarios
/// Nested ComplexType Property in ComplexType
/// </summary>
/// <param name="resultType"></param>
private void ValidateEdmResultType ( EdmType resultType , EdmFunction functionImport )
{
if ( Helper . IsComplexType ( resultType ) )
{
ComplexType complexType = resultType as ComplexType ;
Debug . Assert ( null ! = complexType , "we should have a complex type here" ) ;
foreach ( var property in complexType . Properties )
{
if ( property . TypeUsage . EdmType . BuiltInTypeKind = = BuiltInTypeKind . ComplexType )
{
throw new NotSupportedException ( System . Data . Entity . Strings . ComplexTypeAsReturnTypeAndNestedComplexProperty ( property . Name , complexType . Name , functionImport . FullName ) ) ;
}
}
}
}
/// <summary>
/// Retrieves mapping for the given C-Space functionCommandTree
/// </summary>
private static FunctionImportMappingNonComposable GetTargetFunctionMapping ( DbFunctionCommandTree functionCommandTree )
{
Debug . Assert ( functionCommandTree . DataSpace = = DataSpace . CSpace , "map from CSpace->SSpace function" ) ;
Debug . Assert ( functionCommandTree ! = null , "null functionCommandTree" ) ;
Debug . Assert ( ! functionCommandTree . EdmFunction . IsComposableAttribute , "functionCommandTree.EdmFunction must be non-composable." ) ;
// Find mapped store function.
FunctionImportMapping targetFunctionMapping ;
if ( ! functionCommandTree . MetadataWorkspace . TryGetFunctionImportMapping ( functionCommandTree . EdmFunction , out targetFunctionMapping ) )
{
throw EntityUtil . InvalidOperation ( System . Data . Entity . Strings . EntityClient_UnmappedFunctionImport ( functionCommandTree . EdmFunction . FullName ) ) ;
}
return ( FunctionImportMappingNonComposable ) targetFunctionMapping ;
}
#endregion
#region public API
/// <summary>
/// Create a DbCommand object from the definition, that can be executed
/// </summary>
/// <returns></returns>
public override DbCommand CreateCommand ( ) {
return new EntityCommand ( this ) ;
}
#endregion
#region internal methods
/// <summary>
/// Get a list of commands to be executed by the provider
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal IEnumerable < string > MappedCommands {
get {
// Build up the list of command texts, if we haven't done so yet
List < string > mappedCommandTexts = new List < string > ( ) ;
foreach ( DbCommandDefinition commandDefinition in _mappedCommandDefinitions ) {
DbCommand mappedCommand = commandDefinition . CreateCommand ( ) ;
mappedCommandTexts . Add ( mappedCommand . CommandText ) ;
}
return mappedCommandTexts ;
}
}
/// <summary>
/// Creates ColumnMap for result assembly using the given reader.
/// </summary>
internal ColumnMap CreateColumnMap ( DbDataReader storeDataReader )
{
return CreateColumnMap ( storeDataReader , 0 ) ;
}
/// <summary>
/// Creates ColumnMap for result assembly using the given reader's resultSetIndexth result set.
/// </summary>
internal ColumnMap CreateColumnMap ( DbDataReader storeDataReader , int resultSetIndex )
{
return _columnMapGenerators [ resultSetIndex ] . CreateColumnMap ( storeDataReader ) ;
}
/// <summary>
/// Property to expose the known parameters for the query, so the Command objects
/// constructor can poplulate it's parameter collection from.
/// </summary>
internal IEnumerable < EntityParameter > Parameters {
get {
return _parameters ;
}
}
/// <summary>
/// Set of entity sets exposed in the command.
/// </summary>
internal Set < EntitySet > EntitySets {
get {
return _entitySets ;
}
}
/// <summary>
/// Constructs a EntityParameter from a CQT parameter.
/// </summary>
/// <param name="queryParameter"></param>
/// <returns></returns>
private static EntityParameter CreateEntityParameterFromQueryParameter ( KeyValuePair < string , TypeUsage > queryParameter ) {
// We really can't have a parameter here that isn't a scalar type...
Debug . Assert ( TypeSemantics . IsScalarType ( queryParameter . Value ) , "Non-scalar type used as query parameter type" ) ;
EntityParameter result = new EntityParameter ( ) ;
result . ParameterName = queryParameter . Key ;
EntityCommandDefinition . PopulateParameterFromTypeUsage ( result , queryParameter . Value , isOutParam : false ) ;
return result ;
}
internal static void PopulateParameterFromTypeUsage ( EntityParameter parameter , TypeUsage type , bool isOutParam )
{
// type can be null here if the type provided by the user is not a known model type
if ( type ! = null )
{
PrimitiveTypeKind primitiveTypeKind ;
if ( Helper . IsEnumType ( type . EdmType ) )
{
type = TypeUsage . Create ( Helper . GetUnderlyingEdmTypeForEnumType ( type . EdmType ) ) ;
}
else if ( Helper . IsSpatialType ( type , out primitiveTypeKind ) )
{
parameter . EdmType = EdmProviderManifest . Instance . GetPrimitiveType ( primitiveTypeKind ) ;
}
}
DbCommandDefinition . PopulateParameterFromTypeUsage ( parameter , type , isOutParam ) ;
}
/// <summary>
/// Internal execute method -- copies command information from the map command
/// to the command objects, executes them, and builds the result assembly
/// structures needed to return the data reader
/// </summary>
/// <param name="entityCommand"></param>
/// <param name="behavior"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">behavior must specify CommandBehavior.SequentialAccess</exception>
/// <exception cref="InvalidOperationException">input parameters in the entityCommand.Parameters collection must have non-null values.</exception>
internal DbDataReader Execute ( EntityCommand entityCommand , CommandBehavior behavior ) {
if ( CommandBehavior . SequentialAccess ! = ( behavior & CommandBehavior . SequentialAccess ) ) {
throw EntityUtil . MustUseSequentialAccess ( ) ;
}
DbDataReader storeDataReader = ExecuteStoreCommands ( entityCommand , behavior ) ;
DbDataReader result = null ;
// If we actually executed something, then go ahead and construct a bridge
// data reader for it.
if ( null ! = storeDataReader ) {
try {
ColumnMap columnMap = this . CreateColumnMap ( storeDataReader , 0 ) ;
if ( null = = columnMap ) {
// For a query with no result type (and therefore no column map), consume the reader.
// When the user requests Metadata for this reader, we return nothing.
CommandHelper . ConsumeReader ( storeDataReader ) ;
result = storeDataReader ;
}
else {
result = BridgeDataReader . Create ( storeDataReader , columnMap , entityCommand . Connection . GetMetadataWorkspace ( ) , GetNextResultColumnMaps ( storeDataReader ) ) ;
}
}
catch {
// dispose of store reader if there is an error creating the BridgeDataReader
storeDataReader . Dispose ( ) ;
throw ;
}
}
return result ;
}
private IEnumerable < ColumnMap > GetNextResultColumnMaps ( DbDataReader storeDataReader )
{
for ( int i = 1 ; i < _columnMapGenerators . Length ; + + i )
{
yield return this . CreateColumnMap ( storeDataReader , i ) ;
}
}
/// <summary>
/// Execute the store commands, and return IteratorSources for each one
/// </summary>
/// <param name="entityCommand"></param>
/// <param name="behavior"></param>
internal DbDataReader ExecuteStoreCommands ( EntityCommand entityCommand , CommandBehavior behavior )
{
// SQLPT #120007433 is the work item to implement MARS support, which we
// need to do here, but since the PlanCompiler doesn't
// have it yet, neither do we...
if ( 1 ! = _mappedCommandDefinitions . Count ) {
throw EntityUtil . NotSupported ( "MARS" ) ;
}
EntityTransaction entityTransaction = CommandHelper . GetEntityTransaction ( entityCommand ) ;
DbCommandDefinition definition = _mappedCommandDefinitions [ 0 ] ;
DbCommand storeProviderCommand = definition . CreateCommand ( ) ;
CommandHelper . SetStoreProviderCommandState ( entityCommand , entityTransaction , storeProviderCommand ) ;
// Copy over the values from the map command to the store command; we
// assume that they were not renamed by either the plan compiler or SQL
// Generation.
//
// Note that this pretty much presumes that named parameters are supported
// by the store provider, but it might work if we don't reorder/reuse
// parameters.
//
// Note also that the store provider may choose to add parameters to thier
// command object for some things; we'll only copy over the values for
// parameters that we find in the EntityCommands parameters collection, so
// we won't damage anything the store provider did.
bool hasOutputParameters = false ;
if ( storeProviderCommand . Parameters ! = null ) // SQLBUDT 519066
{
DbProviderServices storeProviderServices = DbProviderServices . GetProviderServices ( entityCommand . Connection . StoreProviderFactory ) ;
foreach ( DbParameter storeParameter in storeProviderCommand . Parameters ) {
// I could just use the string indexer, but then if I didn't find it the
// consumer would get some ParameterNotFound exeception message and that
// wouldn't be very meaningful. Instead, I use the IndexOf method and
// if I don't find it, it's not a big deal (The store provider must
// have added it).
int parameterOrdinal = entityCommand . Parameters . IndexOf ( storeParameter . ParameterName ) ;
if ( - 1 ! = parameterOrdinal ) {
EntityParameter entityParameter = entityCommand . Parameters [ parameterOrdinal ] ;
SyncParameterProperties ( entityParameter , storeParameter , storeProviderServices ) ;
if ( storeParameter . Direction ! = ParameterDirection . Input ) {
hasOutputParameters = true ;
}
}
}
}
// If the EntityCommand has output parameters, we must synchronize parameter values when
// the reader is closed. Tell the EntityCommand about the store command so that it knows
// where to pull those values from.
if ( hasOutputParameters ) {
entityCommand . SetStoreProviderCommand ( storeProviderCommand ) ;
}
DbDataReader reader = null ;
try {
reader = storeProviderCommand . ExecuteReader ( behavior & ~ CommandBehavior . SequentialAccess ) ;
}
catch ( Exception e ) {
// we should not be wrapping all exceptions
if ( EntityUtil . IsCatchableExceptionType ( e ) ) {
// we don't wan't folks to have to know all the various types of exceptions that can
// occur, so we just rethrow a CommandDefinitionException and make whatever we caught
// the inner exception of it.
throw EntityUtil . CommandExecution ( System . Data . Entity . Strings . EntityClient_CommandDefinitionExecutionFailed , e ) ;
}
throw ;
}
return reader ;
}
/// <summary>
/// Updates storeParameter size, precision and scale properties from user provided parameter properties.
/// </summary>
/// <param name="entityParameter"></param>
/// <param name="storeParameter"></param>
private static void SyncParameterProperties ( EntityParameter entityParameter , DbParameter storeParameter , DbProviderServices storeProviderServices ) {
IDbDataParameter dbDataParameter = ( IDbDataParameter ) storeParameter ;
// DBType is not currently syncable; it's part of the cache key anyway; this is because we can't guarantee
// that the store provider will honor it -- (SqlClient doesn't...)
//if (entityParameter.IsDbTypeSpecified)
//{
// storeParameter.DbType = entityParameter.DbType;
//}
// Give the store provider the opportunity to set the value before any parameter state has been copied from
// the EntityParameter.
TypeUsage parameterTypeUsage = TypeHelpers . GetPrimitiveTypeUsageForScalar ( entityParameter . GetTypeUsage ( ) ) ;
storeProviderServices . SetParameterValue ( storeParameter , parameterTypeUsage , entityParameter . Value ) ;
// Override the store provider parameter state with any explicitly specified values from the EntityParameter.
if ( entityParameter . IsDirectionSpecified )
{
storeParameter . Direction = entityParameter . Direction ;
}
if ( entityParameter . IsIsNullableSpecified )
{
storeParameter . IsNullable = entityParameter . IsNullable ;
}
if ( entityParameter . IsSizeSpecified )
{
storeParameter . Size = entityParameter . Size ;
}
if ( entityParameter . IsPrecisionSpecified )
{
dbDataParameter . Precision = entityParameter . Precision ;
}
if ( entityParameter . IsScaleSpecified )
{
dbDataParameter . Scale = entityParameter . Scale ;
}
}
/// <summary>
/// Return the string used by EntityCommand and ObjectQuery<T> ToTraceString"/>
/// </summary>
/// <returns></returns>
internal string ToTraceString ( ) {
if ( _mappedCommandDefinitions ! = null ) {
if ( _mappedCommandDefinitions . Count = = 1 ) {
// Gosh it sure would be nice if I could just get the inner commandText, but
// that would require more public surface area on DbCommandDefinition, or
// me to know about the inner object...
return _mappedCommandDefinitions [ 0 ] . CreateCommand ( ) . CommandText ;
}
else {
StringBuilder sb = new StringBuilder ( ) ;
foreach ( DbCommandDefinition commandDefinition in _mappedCommandDefinitions ) {
DbCommand mappedCommand = commandDefinition . CreateCommand ( ) ;
sb . Append ( mappedCommand . CommandText ) ;
}
return sb . ToString ( ) ;
}
}
return string . Empty ;
}
#endregion
#region nested types
/// <summary>
/// Generates a column map given a data reader.
/// </summary>
private interface IColumnMapGenerator {
/// <summary>
/// Given a data reader, returns column map.
/// </summary>
/// <param name="reader">Data reader.</param>
/// <returns>Column map.</returns>
ColumnMap CreateColumnMap ( DbDataReader reader ) ;
}
/// <summary>
/// IColumnMapGenerator wrapping a constant instance of a column map (invariant with respect
/// to the given DbDataReader)
/// </summary>
private sealed class ConstantColumnMapGenerator : IColumnMapGenerator {
private readonly ColumnMap _columnMap ;
private readonly int _fieldsRequired ;
internal ConstantColumnMapGenerator ( ColumnMap columnMap , int fieldsRequired ) {
_columnMap = columnMap ;
_fieldsRequired = fieldsRequired ;
}
ColumnMap IColumnMapGenerator . CreateColumnMap ( DbDataReader reader ) {
if ( null ! = reader & & reader . FieldCount < _fieldsRequired ) {
throw EntityUtil . CommandExecution ( System . Data . Entity . Strings . EntityClient_TooFewColumns ) ;
}
return _columnMap ;
}
}
/// <summary>
/// Generates column maps for a non-composable function mapping.
/// </summary>
private sealed class FunctionColumnMapGenerator : IColumnMapGenerator {
private readonly FunctionImportMappingNonComposable _mapping ;
private readonly EntitySet _entitySet ;
private readonly StructuralType _baseStructuralType ;
private readonly int _resultSetIndex ;
internal FunctionColumnMapGenerator ( FunctionImportMappingNonComposable mapping , int resultSetIndex , EntitySet entitySet , StructuralType baseStructuralType )
{
_mapping = mapping ;
_entitySet = entitySet ;
_baseStructuralType = baseStructuralType ;
_resultSetIndex = resultSetIndex ;
}
ColumnMap IColumnMapGenerator . CreateColumnMap ( DbDataReader reader )
{
return ColumnMapFactory . CreateFunctionImportStructuralTypeColumnMap ( reader , _mapping , _resultSetIndex , _entitySet , _baseStructuralType ) ;
}
}
#endregion
}
}