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;
        }
Beispiel #10
0
 internal abstract TResultType Visit(ScalarColumnMap columnMap, TArgType arg);
Beispiel #11
0
 /// <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));
 }