private SimpleCollectionColumnMap BuildSimpleEntitySetColumnMap(Mock<MetadataWorkspace> metadataWorkspaceMock, CodeFirstOSpaceTypeFactory codeFirstOSpaceTypeFactory = null) { var cSpaceEntityType = new EntityType(typeof(SimpleEntity).Name, "N", DataSpace.CSpace); var intTypeUsage = TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32), new FacetValues{Nullable = false}); cSpaceEntityType.AddMember(new EdmProperty("Id", intTypeUsage)); cSpaceEntityType.AddMember(new EdmProperty("Count", intTypeUsage)); var entityTypeUsage = TypeUsage.Create(cSpaceEntityType); var idScalarMap = new ScalarColumnMap(intTypeUsage, "Id", 0, 0); var entityMap = new EntityColumnMap( entityTypeUsage, "E", new[] { idScalarMap, new ScalarColumnMap(intTypeUsage, "Count", 0, 1)}, new SimpleEntityIdentity(null, new SimpleColumnMap[] { idScalarMap })); var collectionMap = new SimpleCollectionColumnMap( entityTypeUsage, "MockCollectionType", entityMap, null, null); codeFirstOSpaceTypeFactory = codeFirstOSpaceTypeFactory ?? new CodeFirstOSpaceTypeFactory(); var oSpaceEntityType = codeFirstOSpaceTypeFactory.TryCreateType(typeof(SimpleEntity), cSpaceEntityType); codeFirstOSpaceTypeFactory.CspaceToOspace.Add(cSpaceEntityType, oSpaceEntityType); metadataWorkspaceMock.Setup(m => m.GetItem<EdmType>("N.SimpleEntity", DataSpace.OSpace)) .Returns(oSpaceEntityType); return collectionMap; }
public void TranslateColumnMap_returns_correct_columntypes_and_nullablecolumns_for_associations() { var metadataWorkspaceMock = new Mock<MetadataWorkspace>(); metadataWorkspaceMock.Setup(m => m.GetQueryCacheManager()).Returns(QueryCacheManager.Create()); var codeFirstOSpaceTypeFactory = new CodeFirstOSpaceTypeFactory(); var refEntityColumnMap = (EntityColumnMap)BuildSimpleEntitySetColumnMap(metadataWorkspaceMock, codeFirstOSpaceTypeFactory).Element; var cSpaceEntityType = new EntityType(typeof(RefEntity).Name, "N", DataSpace.CSpace); cSpaceEntityType.AddMember(new EdmProperty("Id", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)))); var navigationProperty = new NavigationProperty("SimpleEntity", TypeUsage.Create(refEntityColumnMap.Type.EdmType)); var associationType = new AssociationType("A", "N", false, DataSpace.CSpace); associationType.AddMember( new AssociationEndMember("From", new RefType(cSpaceEntityType), RelationshipMultiplicity.One)); associationType.AddMember( new AssociationEndMember("To", new RefType((EntityType)navigationProperty.TypeUsage.EdmType), RelationshipMultiplicity.One)); associationType.SetReadOnly(); navigationProperty.RelationshipType = associationType; navigationProperty.FromEndMember = associationType.RelationshipEndMembers[0]; navigationProperty.ToEndMember = associationType.RelationshipEndMembers[1]; cSpaceEntityType.AddMember(navigationProperty); var entityTypeUsage = TypeUsage.Create(cSpaceEntityType); var oSpaceEntityType = codeFirstOSpaceTypeFactory.TryCreateType(typeof(RefEntity), cSpaceEntityType); codeFirstOSpaceTypeFactory.CspaceToOspace.Add(cSpaceEntityType, oSpaceEntityType); var associations = new EdmItemCollection(); associations.AddInternal(associationType); codeFirstOSpaceTypeFactory.CreateRelationships(associations); foreach (var resolve in codeFirstOSpaceTypeFactory.ReferenceResolutions) { resolve(); } var idScalarMap = new ScalarColumnMap(TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), "Id", 0, 1); var refColumnMap = new RefColumnMap( associationType.RelationshipEndMembers[1].TypeUsage, "E", new SimpleEntityIdentity(null, new SimpleColumnMap[] { idScalarMap })); var collectionMap = new SimpleCollectionColumnMap( entityTypeUsage, "MockCollectionType", refColumnMap, null, null); metadataWorkspaceMock.Setup(m => m.GetItem<EdmType>("N.RefEntity", DataSpace.OSpace)) .Returns(oSpaceEntityType); var factory = new Translator().TranslateColumnMap<object>( collectionMap, metadataWorkspaceMock.Object, new SpanIndex(), MergeOption.NoTracking, streaming: false, valueLayer: false); Assert.NotNull(factory); Assert.Equal(new[] { null, typeof(int) }, factory.ColumnTypes); Assert.Equal(new[] { false, true }, factory.NullableColumns); }
public void TranslateColumnMap_returns_correct_columntypes_and_nullablecolumns_for_associations() { var metadataWorkspaceMock = new Mock <MetadataWorkspace>(); metadataWorkspaceMock.Setup(m => m.GetQueryCacheManager()).Returns(QueryCacheManager.Create()); var codeFirstOSpaceTypeFactory = new CodeFirstOSpaceTypeFactory(); var refEntityColumnMap = (EntityColumnMap)BuildSimpleEntitySetColumnMap(metadataWorkspaceMock, codeFirstOSpaceTypeFactory).Element; var cSpaceEntityType = new EntityType(typeof(RefEntity).Name, "N", DataSpace.CSpace); cSpaceEntityType.AddMember(new EdmProperty("Id", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)))); var navigationProperty = new NavigationProperty("SimpleEntity", TypeUsage.Create(refEntityColumnMap.Type.EdmType)); var associationType = new AssociationType("A", "N", false, DataSpace.CSpace); associationType.AddMember( new AssociationEndMember("From", new RefType(cSpaceEntityType), RelationshipMultiplicity.One)); associationType.AddMember( new AssociationEndMember("To", new RefType((EntityType)navigationProperty.TypeUsage.EdmType), RelationshipMultiplicity.One)); associationType.SetReadOnly(); navigationProperty.RelationshipType = associationType; navigationProperty.FromEndMember = associationType.RelationshipEndMembers[0]; navigationProperty.ToEndMember = associationType.RelationshipEndMembers[1]; cSpaceEntityType.AddMember(navigationProperty); var entityTypeUsage = TypeUsage.Create(cSpaceEntityType); var oSpaceEntityType = codeFirstOSpaceTypeFactory.TryCreateType(typeof(RefEntity), cSpaceEntityType); codeFirstOSpaceTypeFactory.CspaceToOspace.Add(cSpaceEntityType, oSpaceEntityType); var associations = new EdmItemCollection(); associations.AddInternal(associationType); codeFirstOSpaceTypeFactory.CreateRelationships(associations); foreach (var resolve in codeFirstOSpaceTypeFactory.ReferenceResolutions) { resolve(); } var idScalarMap = new ScalarColumnMap(TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), "Id", 0, 1); var refColumnMap = new RefColumnMap( associationType.RelationshipEndMembers[1].TypeUsage, "E", new SimpleEntityIdentity(null, new SimpleColumnMap[] { idScalarMap })); var collectionMap = new SimpleCollectionColumnMap( entityTypeUsage, "MockCollectionType", refColumnMap, null, null); metadataWorkspaceMock.Setup(m => m.GetItem <EdmType>(It.IsAny <string>(), DataSpace.OSpace)) .Returns(oSpaceEntityType); var factory = new Translator().TranslateColumnMap <object>( collectionMap, metadataWorkspaceMock.Object, new SpanIndex(), MergeOption.NoTracking, streaming: false, valueLayer: false); Assert.NotNull(factory); Assert.Equal(new[] { null, typeof(object) }, factory.ColumnTypes); Assert.Equal(new[] { false, true }, factory.NullableColumns); }
// <summary> // Determines the store type for a function import. // </summary> private TypeUsage DetermineStoreResultType( 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; var functionImport = mapping.FunctionImport; // Collection(Entity) or Collection(ComplexType) if (MetadataHelper.TryGetFunctionImportReturnType(functionImport, resultSetIndex, out baseStructuralType)) { ValidateEdmResultType(baseStructuralType, functionImport); //Note: Defensive check for historic reasons, we expect functionImport.EntitySets.Count > resultSetIndex var entitySet = functionImport.EntitySets.Count > resultSetIndex ? functionImport.EntitySets[resultSetIndex] : null; columnMapGenerator = new FunctionColumnMapGenerator( mapping, resultSetIndex, entitySet, baseStructuralType, _columnMapFactory); // 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(resultSetIndex); } // Collection(PrimitiveType) else { var 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"); var 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. var scalarColumnMap = new ScalarColumnMap(elementType, string.Empty, 0, 0); var 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> /// Build the collectionColumnMap from a store datareader, a type and an entitySet. /// </summary> /// <param name="storeDataReader"></param> /// <param name="edmType"></param> /// <param name="entitySet"></param> /// <returns></returns> internal static CollectionColumnMap CreateColumnMapFromReaderAndType(DbDataReader storeDataReader, EdmType edmType, EntitySet entitySet, Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping> renameList) { Debug.Assert(Helper.IsEntityType(edmType) || null == entitySet, "The specified non-null EntitySet is incompatible with the EDM type specified."); // Next, build the ColumnMap directly from the edmType and entitySet provided. ColumnMap[] propertyColumnMaps = GetColumnMapsForType(storeDataReader, edmType, renameList); ColumnMap elementColumnMap = null; // NOTE: We don't have a null sentinel here, because the stored proc won't // return one anyway; we'll just presume the data's always there. if (Helper.IsRowType(edmType)) { elementColumnMap = new RecordColumnMap(TypeUsage.Create(edmType), edmType.Name, propertyColumnMaps, null); } else if (Helper.IsComplexType(edmType)) { elementColumnMap = new ComplexTypeColumnMap(TypeUsage.Create(edmType), edmType.Name, propertyColumnMaps, null); } else if (Helper.IsScalarType(edmType)) { if (storeDataReader.FieldCount != 1) { throw EntityUtil.CommandExecutionDataReaderFieldCountForScalarType(); } elementColumnMap = new ScalarColumnMap(TypeUsage.Create(edmType), edmType.Name, 0, 0); } else if (Helper.IsEntityType(edmType)) { elementColumnMap = CreateEntityTypeElementColumnMap(storeDataReader, edmType, entitySet, propertyColumnMaps, null/*renameList*/); } else { Debug.Assert(false, "unexpected edmType?"); } CollectionColumnMap collection = new SimpleCollectionColumnMap(edmType.GetCollectionType().TypeUsage, edmType.Name, elementColumnMap, null, null); return collection; }
/// <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> /// For a given edmType, build an array of scalarColumnMaps that map to the columns /// in the store datareader provided. Note that we're hooking things up by name, not /// by ordinal position. /// </summary> /// <param name="storeDataReader"></param> /// <param name="edmType"></param> /// <returns></returns> private static ColumnMap[] GetColumnMapsForType(DbDataReader storeDataReader, EdmType edmType, Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping> renameList) { // First get the list of properties; NOTE: we need to hook up the column by name, // not by position. IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(edmType); ColumnMap[] propertyColumnMaps = new ColumnMap[members.Count]; int index = 0; foreach (EdmMember member in members) { if (!Helper.IsScalarType(member.TypeUsage.EdmType)) { throw EntityUtil.InvalidOperation(Strings.ADP_InvalidDataReaderUnableToMaterializeNonScalarType(member.Name, member.TypeUsage.EdmType.FullName)); } int ordinal = GetMemberOrdinalFromReader(storeDataReader, member, edmType, renameList); propertyColumnMaps[index] = new ScalarColumnMap(member.TypeUsage, member.Name, 0, ordinal); index++; } return propertyColumnMaps; }
private static ScalarColumnMap[] CreateDiscriminatorColumnMaps(DbDataReader storeDataReader, FunctionImportMappingNonComposable mapping, int resultIndex) { // choose an arbitrary type for discriminator columns -- the type is not // actually statically known EdmType discriminatorType = MetadataItem.EdmProviderManifest.GetPrimitiveType(PrimitiveTypeKind.String); TypeUsage discriminatorTypeUsage = TypeUsage.Create(discriminatorType); IList<string> discriminatorColumnNames = mapping.GetDiscriminatorColumns(resultIndex); ScalarColumnMap[] discriminatorColumns = new ScalarColumnMap[discriminatorColumnNames.Count]; for (int i = 0; i < discriminatorColumns.Length; i++) { string columnName = discriminatorColumnNames[i]; ScalarColumnMap columnMap = new ScalarColumnMap(discriminatorTypeUsage, columnName, 0, GetDiscriminatorOrdinalFromReader(storeDataReader, columnName, mapping.FunctionImport)); discriminatorColumns[i] = columnMap; } return discriminatorColumns; }
/// <summary> /// Requires: a public type with a public, default constructor. Returns a column map initializing the type /// and all properties of the type with a public setter taking a primitive type and having a corresponding /// column in the reader. /// </summary> internal static CollectionColumnMap CreateColumnMapFromReaderAndClrType(DbDataReader reader, Type type, MetadataWorkspace workspace) { Debug.Assert(null != reader); Debug.Assert(null != type); Debug.Assert(null != workspace); // we require a default constructor ConstructorInfo constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); if (type.IsAbstract || (null == constructor && !type.IsValueType)) { throw EntityUtil.InvalidOperation( Strings.ObjectContext_InvalidTypeForStoreQuery(type)); } // build a LINQ expression used by result assembly to create results var memberInfo = new List<Tuple<MemberAssignment, int, EdmProperty>>(); foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { // for enums unwrap the type if nullable var propertyUnderlyingType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; Type propType = propertyUnderlyingType.IsEnum ? propertyUnderlyingType.GetEnumUnderlyingType() : prop.PropertyType; EdmType modelType; int ordinal; if (TryGetColumnOrdinalFromReader(reader, prop.Name, out ordinal) && MetadataHelper.TryDetermineCSpaceModelType(propType, workspace, out modelType) && (Helper.IsScalarType(modelType)) && prop.CanWrite && prop.GetIndexParameters().Length == 0 && null != prop.GetSetMethod(/* nonPublic */true)) { memberInfo.Add(Tuple.Create( Expression.Bind(prop, Expression.Parameter(prop.PropertyType, "placeholder")), ordinal, new EdmProperty(prop.Name, TypeUsage.Create(modelType)))); } } // initialize members in the order in which they appear in the reader MemberInfo[] members = new MemberInfo[memberInfo.Count]; MemberBinding[] memberBindings = new MemberBinding[memberInfo.Count]; ColumnMap[] propertyMaps = new ColumnMap[memberInfo.Count]; EdmProperty[] modelProperties = new EdmProperty[memberInfo.Count]; int i = 0; foreach (var memberGroup in memberInfo.GroupBy(tuple => tuple.Item2).OrderBy(tuple => tuple.Key)) { // make sure that a single column isn't contributing to multiple properties if (memberGroup.Count() != 1) { throw EntityUtil.InvalidOperation(Strings.ObjectContext_TwoPropertiesMappedToSameColumn( reader.GetName(memberGroup.Key), String.Join(", ", memberGroup.Select(tuple => tuple.Item3.Name).ToArray()))); } var member = memberGroup.Single(); MemberAssignment assignment = member.Item1; int ordinal = member.Item2; EdmProperty modelProp = member.Item3; members[i] = assignment.Member; memberBindings[i] = assignment; propertyMaps[i] = new ScalarColumnMap(modelProp.TypeUsage, modelProp.Name, 0, ordinal); modelProperties[i] = modelProp; i++; } NewExpression newExpr = null == constructor ? Expression.New(type) : Expression.New(constructor); MemberInitExpression init = Expression.MemberInit(newExpr, memberBindings); InitializerMetadata initMetadata = InitializerMetadata.CreateProjectionInitializer( (EdmItemCollection)workspace.GetItemCollection(DataSpace.CSpace), init, members); // column map (a collection of rows with InitializerMetadata markup) RowType rowType = new RowType(modelProperties, initMetadata); RecordColumnMap rowMap = new RecordColumnMap(TypeUsage.Create(rowType), "DefaultTypeProjection", propertyMaps, null); CollectionColumnMap collectionMap = new SimpleCollectionColumnMap(rowType.GetCollectionType().TypeUsage, rowType.Name, rowMap, null, null); return collectionMap; }
internal abstract TResultType Visit(ScalarColumnMap columnMap, TArgType arg);
/// <summary> /// ScalarColumnMap /// </summary> /// <param name="columnMap"></param> /// <param name="translationDelegate"></param> /// <returns></returns> internal override ColumnMap Visit(ScalarColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate) { return(translationDelegate(columnMap)); }
internal virtual void Visit(ScalarColumnMap columnMap, TArgType arg) { }
internal override void Visit(ScalarColumnMap columnMap, int dummy) { this.Append(string.Format((IFormatProvider)CultureInfo.InvariantCulture, "S({0}-{1}:{2})", (object)columnMap.CommandId, (object)columnMap.ColumnPos, (object)columnMap.Type.Identity)); }