private void CreateAssociationType(LoadMethodSessionState session,
            List<RelationshipDetailsRow> columns)
        {
            Debug.Assert(columns.Count != 0, "should have at least one column");

            RelationshipDetailsRow firstRow = columns[0];

            // get the entity types for the ends
            EntityType pkEntityType;
            EntityType fkEntityType;
            if (!TryGetEndEntities(session, firstRow, out pkEntityType, out fkEntityType))
            {
                return;
            }

            if (!AreRelationshipColumnsTheTypesEntireKey(pkEntityType, columns, r => r.PKColumn))
            {
                session.AddErrorsForType(pkEntityType, new EdmSchemaError(Strings.UnsupportedDbRelationship(firstRow.RelationshipName), (int)ModelBuilderErrorCode.UnsupportedDbRelationship, EdmSchemaErrorSeverity.Warning));
                return;
                         
            }
            UniqueIdentifierService usedEndNames = new UniqueIdentifierService(false);
            // figure out the lower bound of the pk end
            bool someFkColmnsAreNullable;
            if (_targetEntityFrameworkVersion == EntityFrameworkVersions.Version1)
            {
                someFkColmnsAreNullable = AreAllFkKeyColumnsNullable(fkEntityType, columns);
            }
            else
            {
                someFkColmnsAreNullable = AreAnyFkKeyColumnsNullable(fkEntityType, columns);
            }

            RelationshipMultiplicity pkMultiplicity = someFkColmnsAreNullable ? RelationshipMultiplicity.ZeroOrOne : RelationshipMultiplicity.One;
            //Get the Delete Action for the end and set it.
            //The only DeleteAction we support is Cascade, ignor all others for now.
            OperationAction onDeleteAction = OperationAction.None;
            if (firstRow.RelationshipIsCascadeDelete)
            {
                onDeleteAction = OperationAction.Cascade;
            }
            
            AssociationEndMember pkEnd = CreateAssociationEnd( session,
                        pkEntityType,
                        pkMultiplicity,
                        usedEndNames, onDeleteAction);

            RelationshipMultiplicity fkMultiplicity = RelationshipMultiplicity.Many;

            if ( !someFkColmnsAreNullable &&
                 AreRelationshipColumnsTheTypesEntireKey(fkEntityType, columns, r => r.FKColumn))
            {
                // both the pk and fk side columns are the keys of their types
                // so this is a 1 to one relationship
                fkMultiplicity = RelationshipMultiplicity.ZeroOrOne;
            }

            AssociationEndMember fkEnd = CreateAssociationEnd(session, 
                        fkEntityType, 
                        fkMultiplicity,
                        usedEndNames, OperationAction.None);


            // create the type
            string typeName = session.UsedTypeNames.AdjustIdentifier(firstRow.RelationshipName);
            AssociationType type = new AssociationType(typeName, 
                _namespaceName, false, DataSpace.SSpace);
            type.AddMember(pkEnd);
            type.AddMember(fkEnd);

            List<EdmSchemaError> errors = new List<EdmSchemaError>();
            bool isValid = CreateReferentialConstraint(session,
                        type,
                        pkEnd,
                        fkEnd,
                        columns,
                        errors);


            string errorMessage;

            // We can skip most validation checks if the FKs are directly surfaced (since we can produce valid mappings in these cases).
            if (!this.GenerateForeignKeyProperties)
            {
                if (IsFkPartiallyContainedInPK(type, out errorMessage))
                {
                    errors.Add(new EdmSchemaError(
                        errorMessage,
                        (int)ModelBuilderErrorCode.UnsupportedForeinKeyPattern,
                        EdmSchemaErrorSeverity.Warning));
                    isValid = false;
                }

                if (isValid)
                {
                    //Now check if any FK (which could also be a PK) is shared among multiple Associations (ie shared via foreign key constraint).
                    // To do this we check if the Association Type being generated has any dependent property which is also a dependent in one of the association typed already added.
                    //If so, we keep one Association and throw the rest away.

                    foreach (var toPropertyOfAddedAssociation in session.AssociationTypes.SelectMany(t => t.ReferentialConstraints.SelectMany(refconst => refconst.ToProperties)))
                    {
                        foreach (var toProperty in type.ReferentialConstraints.SelectMany(refconst => refconst.ToProperties))
                        {
                            if (toProperty.DeclaringType.Equals(toPropertyOfAddedAssociation.DeclaringType) && toProperty.Equals(toPropertyOfAddedAssociation))
                            {
                                errors.Add(new EdmSchemaError(
                                    Strings.SharedForeignKey(type.Name, toProperty, toProperty.DeclaringType),
                                    (int)ModelBuilderErrorCode.SharedForeignKey,
                                    EdmSchemaErrorSeverity.Warning));

                                isValid = false;
                                break;
                            }
                        }

                        if (!isValid)
                        {
                            break;
                        }
                    }
                }
            }

            if (isValid)
            { 
                session.AssociationTypes.Add(type);
            }
            else
            {
                session.InvalidTypes.Add(type);
                session.RelationshipEndTypeLookup.Remove(pkEnd);
                session.RelationshipEndTypeLookup.Remove(fkEnd);
            }


            type.SetReadOnly();
            session.AddErrorsForType(type, errors);
        }
        private void CreateEdmFunction(LoadMethodSessionState session, DbObjectKey functionKey, List<FunctionDetailsReader.Memento> parameters)
        {
            Debug.Assert(parameters.Count != 0, "don't call the method with no data");

            FunctionDetailsReader row = parameters[0].CreateReader();

            FunctionParameter returnParameter = null;
            bool isValid = true;
            List<EdmSchemaError> errors = new List<EdmSchemaError>();
            if (row.ReturnType != null)
            {
                Debug.Assert(!row.IsTvf, "TVF can't have ReturnType (used only for scalars).");
                bool excludedForTarget;
                TypeUsage returnType = GetScalarFunctionTypeUsage(session, row.ReturnType, out excludedForTarget);
                if (returnType != null)
                {
                    returnParameter = new FunctionParameter(EdmConstants.ReturnType, returnType, ParameterMode.ReturnValue);
                }
                else
                {
                    isValid = false;
                    errors.Add(new EdmSchemaError(excludedForTarget ?
                                                  Strings.UnsupportedFunctionReturnDataTypeForTarget(row.ProcedureName, row.ReturnType) :
                                                  Strings.UnsupportedFunctionReturnDataType(row.ProcedureName, row.ReturnType),
                                                  (int)ModelBuilderErrorCode.UnsupportedType,
                                                  EdmSchemaErrorSeverity.Warning));
                }
            }
            else if (row.IsTvf)
            {
                if (_targetEntityFrameworkVersion < EntityFrameworkVersions.Version3)
                {
                    return;
                }
                RowType tvfReturnType;
                if (session.TryGetTvfReturnType(functionKey, out tvfReturnType) && !session.InvalidTypes.Contains(tvfReturnType))
                {
                    var collectionType = tvfReturnType.GetCollectionType();
                    collectionType.SetReadOnly();
                    returnParameter = new FunctionParameter(EdmConstants.ReturnType, TypeUsage.Create(collectionType), ParameterMode.ReturnValue);
                }
                else
                {
                    isValid = false;

                    // If the TVF return type exists, but it is not valid, then reassign all its errors directly to the TVF.
                    // This is needed in order to avoid the following kind of error reporting:
                    // SSDL:
                    // 
                    // <!-- Errors found while generating type:
                    //    column1 type not supported
                    //    column2 type not supported
                    //   <RowType />
                    // -->
                    // ...
                    // ...
                    // <!-- Error found while generating type:
                    //    TableReferencedByTvfWasNotFound
                    //   <Function Name="TVF" .... />
                    // -->
                    // 
                    // Instead we want something like this:
                    // 
                    // <!-- Errors found while generating type:
                    //    column1 type not supported
                    //    column2 type not supported
                    //    TableReferencedByTvfWasNotFound
                    //   <Function Name="TVF" .... />
                    // -->
                    // 

                    List<EdmSchemaError> tvfReturnTypeErrors;
                    if (tvfReturnType != null && session.ItemToErrorsMap.TryGetValue(tvfReturnType, out tvfReturnTypeErrors))
                    {
                        errors.AddRange(tvfReturnTypeErrors);
                        session.ItemToErrorsMap.Remove(tvfReturnType);
                        if (session.InvalidTypes.Contains(tvfReturnType))
                        {
                            session.InvalidTypes.Remove(tvfReturnType);
                        }
                    }
                    
                    errors.Add(new EdmSchemaError(
                        Strings.TableReferencedByTvfWasNotFound(functionKey),
                        (int)ModelBuilderErrorCode.MissingTvfReturnTable,
                        EdmSchemaErrorSeverity.Warning));
                }
            }
            
            bool caseSensitive = false;
            UniqueIdentifierService uniqueIdentifiers = new UniqueIdentifierService(caseSensitive);
            List<FunctionParameter> functionParameters = new List<FunctionParameter>();
            for (int i = 0; i < parameters.Count && !row.IsParameterNameNull; i++)
            {
                row.Attach(parameters[i]);
                TypeUsage parameterType = null;
                bool excludedForTarget = false;
                if (!row.IsParameterTypeNull)
                {
                    parameterType = GetScalarFunctionTypeUsage(session, row.ParameterType, out excludedForTarget);
                }

                if (parameterType != null)
                {
                    ParameterMode mode;
                    if (!row.TryGetParameterMode(out mode))
                    {
                        isValid = false;
                        string modeValue = "null";
                        if (!row.IsParameterModeNull)
                        {
                            modeValue = row.ProcParameterMode;
                        }
                        errors.Add(new EdmSchemaError(
                            Strings.ParameterDirectionNotValid(
                            row.ProcedureName,
                            row.ParameterName,
                            modeValue),
                            (int)ModelBuilderErrorCode.ParameterDirectionNotValid,
                            EdmSchemaErrorSeverity.Warning));
                    }

                    // the mode will get defaulted to something, so it is ok to keep creating after
                    // an error getting the mode value.
                    string parameterName = EntityModelSchemaGenerator.CreateValidEcmaName(row.ParameterName, 'p');
                    parameterName = uniqueIdentifiers.AdjustIdentifier(parameterName);
                    FunctionParameter parameter = new FunctionParameter(parameterName, parameterType, mode);
                    functionParameters.Add(parameter);
                }
                else
                {
                    isValid = false;
                    string typeValue = "null";
                    if (!row.IsParameterTypeNull)
                    {
                        typeValue = row.ParameterType;
                    }
                    errors.Add(new EdmSchemaError(excludedForTarget ?
                                                  Strings.UnsupportedFunctionParameterDataTypeForTarget(row.ProcedureName, row.ParameterName, i, typeValue) :
                                                  Strings.UnsupportedFunctionParameterDataType(row.ProcedureName, row.ParameterName, i, typeValue),
                                                  (int)ModelBuilderErrorCode.UnsupportedType,
                                                  EdmSchemaErrorSeverity.Warning));
                }
            }

            string functionName = EntityModelSchemaGenerator.CreateValidEcmaName(row.ProcedureName, 'f');
            functionName = session.UsedTypeNames.AdjustIdentifier(functionName);
            FunctionParameter[] returnParameters = 
                returnParameter == null ? new FunctionParameter[0] : new FunctionParameter[] {returnParameter};
            EdmFunction function = new EdmFunction(functionName,
                _namespaceName,
                DataSpace.SSpace,
                new EdmFunctionPayload
                {
                    Schema = row.Schema,
                    StoreFunctionName = functionName != row.ProcedureName ? row.ProcedureName : null,
                    IsAggregate = row.IsIsAggregate,
                    IsBuiltIn = row.IsBuiltIn,
                    IsNiladic = row.IsNiladic,
                    IsComposable = row.IsComposable,
                    ReturnParameters = returnParameters,
                    Parameters = functionParameters.ToArray()
                });
            function.SetReadOnly();

            session.AddErrorsForType(function, errors);
            if (isValid)
            {
                session.Functions.Add(function);
            }
            else
            {
                session.InvalidTypes.Add(function);
            }
        }
        private void CreateTvfReturnRowType(
            LoadMethodSessionState session,
            IList<TableDetailsRow> columns,
            ICollection<string> primaryKeys,
            DbObjectType objectType,
            List<EdmSchemaError> errors)
        {
            Debug.Assert(columns.Count != 0, "Trying to create a RowType with 0 properties");
            Debug.Assert(primaryKeys != null, "primaryKeys != null");

            DbObjectKey tableKey = columns[0].CreateDbObjectKey(objectType);
            if (errors == null)
            {
                errors = new List<EdmSchemaError>();
            }

            IList<string> excludedColumns;
            var properties = CreateEdmProperties(session, columns, tableKey, errors, out excludedColumns);

            var rowType = new RowType(properties);
            rowType.SetReadOnly();
            session.AddTvfReturnType(tableKey, rowType);
            if (rowType.Properties.Count == 0)
            {
                session.InvalidTypes.Add(rowType);
            }
            session.AddErrorsForType(rowType, errors);
        }
        private IList<EdmSchemaError> DoGenerateStoreMetadata(IEnumerable<EntityStoreSchemaFilterEntry> filters, Version targetEntityFrameworkVersion)
        {
            if (_entityContainer != null)
            {
                _entityContainer = null;
                _storeItemCollection = null;
                _errorsLookup = null;
                _invalidTypes = null;
            }

            _targetEntityFrameworkVersion = targetEntityFrameworkVersion;
            LoadMethodSessionState session = new LoadMethodSessionState(targetEntityFrameworkVersion);
            try
            {
                _loader.Open();

                DbConnection connection = _loader.InnerConnection;
                DbProviderFactory providerFactory = DbProviderServices.GetProviderFactory(_loader.ProviderInvariantName);
                DbProviderServices providerServices = DbProviderServices.GetProviderServices(providerFactory);
                _providerManifestToken = providerServices.GetProviderManifestToken(connection);
                DbProviderManifest storeManifest = providerServices.GetProviderManifest(_providerManifestToken);
                
                session.Filters = filters;
                Debug.Assert(_namespaceName != null, "_namespaceName should not be null at this point, did you add a new ctor?");

                session.ItemCollection = new StoreItemCollection(providerFactory, providerServices.GetProviderManifest(_providerManifestToken), _providerManifestToken);

                CreateTableEntityTypes(session);
                CreateViewEntityTypes(session);
                string entityContainerName = this._namespaceName.Replace(".", string.Empty) + CONTAINER_SUFFIX;

                Debug.Assert(entityContainerName != null, "We should always have a container name");
                EntityContainer entityContainer = new EntityContainer(entityContainerName, DataSpace.SSpace);

                foreach (EntityType type in session.GetAllEntities())
                {
                    Debug.Assert(type.KeyMembers.Count > 0, "Why do we have Entities without keys in our valid Entities collection");
                    session.ItemCollection.AddInternal(type);
                    EntitySet entitySet = CreateEntitySet(session, type);
                    session.EntityTypeToSet.Add(type, entitySet);
                    entityContainer.AddEntitySetBase(entitySet);
                }

                CreateAssociationTypes(session);
                foreach (AssociationType type in session.AssociationTypes)
                {
                    session.ItemCollection.AddInternal(type);
                    AssociationSet set = CreateAssociationSet(session, type);
                    entityContainer.AddEntitySetBase(set);
                }

                entityContainer.SetReadOnly();
                session.ItemCollection.AddInternal(entityContainer);
                FixupKeylessEntitySets(entityContainer, session);

                if (_targetEntityFrameworkVersion >= EntityFrameworkVersions.Version3 &&
                    _loader.StoreSchemaModelVersion >= EntityFrameworkVersions.Version3)
                {
                    CreateTvfReturnRowTypes(session);
                }
                CreateEdmFunctions(session);
                foreach (EdmFunction function in session.Functions)
                {
                    session.ItemCollection.AddInternal(function);
                }

                if (!HasErrorSeverityErrors(session.Errors))
                {
                    _entityContainer = entityContainer;
                    _storeItemCollection = session.ItemCollection;
                    _errorsLookup = session.ItemToErrorsMap;
                    _invalidTypes = new List<EdmType>(session.InvalidTypes);
                }
            }
            catch (Exception e)
            {
                if (MetadataUtil.IsCatchableExceptionType(e))
                {
                    string message = EDesignUtil.GetMessagesFromEntireExceptionChain(e);
                    session.AddErrorsForType(null,
                        new EdmSchemaError(message,
                                    (int)ModelBuilderErrorCode.UnknownError,
                                    EdmSchemaErrorSeverity.Error,
                                    e));
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                _loader.Close();
            }

            return new List<EdmSchemaError>(session.Errors);
        }
        private void CreateEntityType(
            LoadMethodSessionState session, 
            IList<TableDetailsRow> columns, 
            ICollection<string> primaryKeys,
            DbObjectType objectType,
            List<EdmSchemaError> errors)
        {
            Debug.Assert(columns.Count != 0, "Trying to create an EntityType with 0 properties");
            Debug.Assert(primaryKeys != null, "primaryKeys != null");

            DbObjectKey tableKey = columns[0].CreateDbObjectKey(objectType);
            if (errors == null)
            {
                errors = new List<EdmSchemaError>();
            }

            //
            // Handle Tables without explicit declaration of keys
            //
            EntityCreationStatus status = EntityCreationStatus.Normal;
            if (primaryKeys.Count == 0)
            {
                List<string> pKeys = new List<string>(columns.Count);
                session.AddTableWithoutKey(tableKey);
                if (InferKeyColumns(session, columns, pKeys, tableKey, ref primaryKeys))
                {
                    errors.Add(new EdmSchemaError(
                        Strings.NoPrimaryKeyDefined(tableKey),
                                    (int)ModelBuilderErrorCode.NoPrimaryKeyDefined,
                                     EdmSchemaErrorSeverity.Warning));
                    status = EntityCreationStatus.ReadOnly;
                }
                else
                {
                    errors.Add(new EdmSchemaError(
                        Strings.CannotCreateEntityWithNoPrimaryKeyDefined(tableKey),
                                        (int)ModelBuilderErrorCode.CannotCreateEntityWithoutPrimaryKey,
                                        EdmSchemaErrorSeverity.Warning));
                    status = EntityCreationStatus.Invalid;
                }
            }

            Debug.Assert(primaryKeys == null || primaryKeys.Count > 0,"There must be at least one key columns at this point in time");

            IList<string> excludedColumns;
            var properties = CreateEdmProperties(session, columns, tableKey, errors, out excludedColumns);

            var excludedKeyColumns = (primaryKeys != null ? primaryKeys.Intersect(excludedColumns) : new string[0]).ToArray();

            if (primaryKeys != null && excludedKeyColumns.Length == 0)
            {
                foreach (EdmMember pkColumn in properties.Where(p => primaryKeys.Contains(p.Name)))
                {
                    if (!MetadataUtil.IsValidKeyType(_targetEntityFrameworkVersion, pkColumn.TypeUsage.EdmType))
                    {
                        // make it a read-only table by calling this method recursively with no keys
                        errors = new List<EdmSchemaError>();
                        var tableColumn = columns.Where(c => c.ColumnName == pkColumn.Name).Single();
                        errors.Add(new EdmSchemaError(Strings.InvalidTypeForPrimaryKey(tableColumn.GetMostQualifiedTableName(),
                            tableColumn.ColumnName,
                            tableColumn.DataType),
                            (int)ModelBuilderErrorCode.InvalidKeyTypeFound,
                            EdmSchemaErrorSeverity.Warning));

                        string[] keyColumns = new string[0];
                        CreateEntityType(session, columns, keyColumns, objectType, errors);
                        return;
                    }
                }
            }

            if (excludedKeyColumns.Length > 0)
            {
                // see if we have any keys left
                if (primaryKeys != null && excludedKeyColumns.Length < primaryKeys.Count)
                {
                    primaryKeys = primaryKeys.Except(excludedKeyColumns).ToList();
                    status = EntityCreationStatus.ReadOnly;
                }
                else
                {
                    primaryKeys = null;
                    status = EntityCreationStatus.Invalid;
                }

                foreach (string columnName in excludedKeyColumns)
                {
                    if (status == EntityCreationStatus.ReadOnly)
                    {
                        errors.Add(new EdmSchemaError(
                            Strings.ExcludedColumnWasAKeyColumnEntityIsReadOnly(columnName, columns[0].GetMostQualifiedTableName()),
                            (int)ModelBuilderErrorCode.ExcludedColumnWasAKeyColumn,
                            EdmSchemaErrorSeverity.Warning));
                    }
                    else
                    {
                        Debug.Assert(status == EntityCreationStatus.Invalid, "Did we change some code above to make it possible to be something different?");
                        errors.Add(new EdmSchemaError(
                            Strings.ExcludedColumnWasAKeyColumnEntityIsInvalid(columnName, columns[0].GetMostQualifiedTableName()),
                            (int)ModelBuilderErrorCode.ExcludedColumnWasAKeyColumn,
                            EdmSchemaErrorSeverity.Warning));
                    }
                }
            }

            string typeName = session.UsedTypeNames.AdjustIdentifier(columns[0].TableName);
            var entityType = new EntityType(typeName, _namespaceName, DataSpace.SSpace, primaryKeys, properties);
            entityType.SetReadOnly();

            switch (status)
            {
                case EntityCreationStatus.Normal:
                    session.AddEntity(tableKey, entityType);
                    break;
                case EntityCreationStatus.ReadOnly:
                    session.AddEntity(tableKey, entityType);
                    session.ReadOnlyEntities.Add(entityType);
                    break;
                default:
                    Debug.Assert(status == EntityCreationStatus.Invalid, "did you add a new value?");
                    session.InvalidTypes.Add(entityType);
                    break;
            }

            session.AddErrorsForType(entityType, errors);
        }