Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntityDesign / Design / System / Data / Entity / Design / EntityStoreSchemaGenerator.cs / 1 / EntityStoreSchemaGenerator.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections.Generic; using System.Diagnostics; using System.Xml; using SOM=System.Data.EntityModel; using System.Globalization; using System.Data.Metadata.Edm; using System.Data.Entity.Design.Common; using System.Data.Entity.Design.SsdlGenerator; using System.Data.Common; using System.Data.EntityClient; using System.IO; using System.Data.Mapping; using System.Data.Common.Utils; using System.Collections.ObjectModel; using System.Data.Common.CommandTrees; using System.Text; using System.Linq; namespace System.Data.Entity.Design { ////// Responsible for Loading Database Schema Information /// public sealed partial class EntityStoreSchemaGenerator { private const string CONTAINER_SUFFIX = "Container"; private EntityStoreSchemaGeneratorDatabaseSchemaLoader _loader; private readonly string _provider; private string _providerManifestToken = string.Empty; private EntityContainer _entityContainer = null; private StoreItemCollection _storeItemCollection; private string _namespaceName; private MetadataItemSerializer.ErrorsLookup _errorsLookup; private List_invalidTypes; /// /// Creates a new EntityStoreGenerator /// /// The name of the provider to use to load the schema information. /// A connection string to the DB that should be loaded from. /// The namespace name to use for the store metadata that is generated. public EntityStoreSchemaGenerator(string providerInvariantName, string connectionString, string namespaceName) { EDesignUtil.CheckStringArgument(providerInvariantName, "providerInvariantName"); EDesignUtil.CheckArgumentNull(connectionString, "connectionString"); // check for NULL string and support empty connection string EDesignUtil.CheckStringArgument(namespaceName, "namespaceName"); _namespaceName = EntityModelSchemaGenerator.CreateValidNamespaceName(namespaceName, 'z'); if (_namespaceName != namespaceName) { throw EDesignUtil.InvalidNamespaceNameArgument(namespaceName); } _provider = providerInvariantName; _loader = new EntityStoreSchemaGeneratorDatabaseSchemaLoader(providerInvariantName, connectionString); } ////// Gets the EntityContainer that was created /// public EntityContainer EntityContainer { get { return _entityContainer; } } ////// Gets the StoreItemCollection that was created /// public StoreItemCollection StoreItemCollection { get { return _storeItemCollection; } } ////// Creates a Metadata schema from the DbSchemaLoader that was passed in /// ///The new metadata for the schema that was loaded public IListGenerateStoreMetadata() { List filters = new List (); return GenerateStoreMetadata(filters); } /// /// Creates a Metadata schema from the DbSchemaLoader that was passed in /// /// The filters to be applied during generation. ///The new metadata for the schema that was loaded public IListGenerateStoreMetadata(IEnumerable filters) { EDesignUtil.CheckArgumentNull(filters, "filters"); if (_entityContainer != null) { _entityContainer = null; _storeItemCollection = null; _errorsLookup = null; _invalidTypes = null; } LoadMethodSessionState session = new LoadMethodSessionState(); 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)); 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); CreateEdmFunctions(session); foreach (EdmFunction function in session.Functions) { function.SetReadOnly(); session.ItemCollection.AddInternal(function); } if (!HasErrorSeverityErrors(session.Errors)) { _entityContainer = entityContainer; _storeItemCollection = session.ItemCollection; _errorsLookup = session.ItemToErrorsMap; _invalidTypes = new List (session.InvalidTypes); } } catch (Exception e) { if (EntityUtil.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 (session.Errors); } /// /// Writes the Schema to xml /// /// The name of the file to write the xml to. public void WriteStoreSchema(string outputFileName) { EDesignUtil.CheckStringArgument(outputFileName, "outputFileName"); CheckValidItemCollection(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter writer = XmlWriter.Create(outputFileName, settings)) { WriteStoreSchema(writer); } } ////// Writes the Schema to xml. /// /// The XmlWriter to write the xml to. public void WriteStoreSchema(XmlWriter writer) { EDesignUtil.CheckArgumentNull(writer, "writer"); CheckValidItemCollection(); // we are going to add this EntityStoreSchemaGenerator namespace at the top of // the file so that when we mark the entitysets with where they came from // we don't have to repeat the namespace on each node. The VS tools use // the source information to give better messages when refreshing the .ssdl from the db // e.g. //// // ... // var xmlPrefixToNamespace = new KeyValuePair ("store", DesignXmlConstants.EntityStoreSchemaGeneratorNamespace); MetadataItemSerializer.WriteXml(writer, StoreItemCollection, _namespaceName, _errorsLookup, _invalidTypes, _provider, _providerManifestToken, xmlPrefixToNamespace); } /// /// Creates an EntityConnection loaded with the providers metadata for the /// /// The provider invariant name. /// The connection for the providers connection. ///An EntityConnection that can query the ConceptualSchemaDefinition for the provider. public static EntityConnection CreateStoreSchemaConnection(string providerInvariantName, string connectionString) { EDesignUtil.CheckArgumentNull(providerInvariantName, "providerInvariantName"); EDesignUtil.CheckArgumentNull(connectionString, "connectionString"); DbProviderFactory factory; try { factory = DbProviderFactories.GetFactory(providerInvariantName); } catch (ArgumentException e) { throw EDesignUtil.Argument(Strings.EntityClient_InvalidStoreProvider(providerInvariantName), e); } DbProviderServices providerServices = DbProviderServices.GetProviderServices(factory); DbConnection providerConnection = factory.CreateConnection(); if (providerConnection == null) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderFactoryReturnedNullFactory(providerInvariantName)); } providerConnection.ConnectionString = connectionString; MetadataWorkspace workspace = GetProviderSchemaMetadataWorkspace(providerServices, providerConnection); // create the connection with the information we have return new EntityConnection(workspace, providerConnection); } private static MetadataWorkspace GetProviderSchemaMetadataWorkspace(DbProviderServices providerServices, DbConnection providerConnection) { XmlReader csdl = null; XmlReader ssdl = null; XmlReader msl = null; try { // create the metadata workspace MetadataWorkspace workspace = new MetadataWorkspace(); string manifestToken = providerServices.GetProviderManifestToken(providerConnection); DbProviderManifest providerManifest = providerServices.GetProviderManifest(manifestToken); // create the EdmItemCollection IListerrors; ssdl = providerManifest.GetInformation(DbProviderManifest.StoreSchemaDefinition); string location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.StoreSchemaDefinition); List ssdlLocations = new List (1); ssdlLocations.Add(location); StoreItemCollection storeItemCollection = new StoreItemCollection(new XmlReader[] { ssdl }, ssdlLocations.AsReadOnly(), out errors); ThrowOnError(errors); workspace.RegisterItemCollection(storeItemCollection); csdl = DbProviderServices.GetConceptualSchemaDescription(); location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.ConceptualSchemaDefinition); List csdlLocations = new List (1); csdlLocations.Add(location); EdmItemCollection edmItemCollection = new EdmItemCollection(new XmlReader[] { csdl }, csdlLocations.AsReadOnly(), out errors); ThrowOnError(errors); workspace.RegisterItemCollection(edmItemCollection); msl = providerManifest.GetInformation(DbProviderManifest.StoreSchemaMapping); location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.StoreSchemaMapping); List mslLocations = new List (1); mslLocations.Add(location); StorageMappingItemCollection mappingItemCollection = new StorageMappingItemCollection(edmItemCollection, storeItemCollection, new XmlReader[] { msl }, mslLocations, out errors); ThrowOnError(errors); workspace.RegisterItemCollection(mappingItemCollection); // make the views generate here so we can wrap the provider schema problems // in a ProviderIncompatibleException ForceViewGeneration(workspace); return workspace; } catch (ProviderIncompatibleException) { // we don't really want to catch this one, just rethrow it throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderSchemaErrors, e); } throw; } finally { if (csdl != null) ((IDisposable)csdl).Dispose(); if (ssdl != null) ((IDisposable)ssdl).Dispose(); if (msl != null) ((IDisposable)msl).Dispose(); } } private static void ForceViewGeneration(MetadataWorkspace workspace) { ReadOnlyCollection containers = workspace.GetItems (DataSpace.SSpace); Debug.Assert(containers.Count != 0, "no s space containers found"); Debug.Assert(containers[0].BaseEntitySets.Count != 0, "no entity sets in the sspace container"); workspace.GetCqtView(containers[0].BaseEntitySets[0]); } private static void ThrowOnError(IList // ... // var xmlPrefixToNamespace = new KeyValuePairerrors) { if (errors.Count != 0) { if (!MetadataHelper.CheckIfAllErrorsAreWarnings(errors)) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderSchemaErrors, EntityUtil.InvalidSchemaEncountered(Helper.CombineErrorMessage(errors))); } } } private void CheckValidItemCollection() { if (_entityContainer == null) { throw EDesignUtil.EntityStoreGeneratorSchemaNotLoaded(); } } internal static bool HasErrorSeverityErrors(IEnumerable errors) { foreach (EdmSchemaError error in errors) { if (error.Severity == EdmSchemaErrorSeverity.Error) { return true; } } return false; } private AssociationSet CreateAssociationSet(LoadMethodSessionState session, AssociationType type) { AssociationSet set = new AssociationSet(type.Name, type); foreach(AssociationEndMember end in type.RelationshipEndMembers) { EntitySet entitySet = session.GetEntitySet(end); DbObjectKey key = session.GetKey(entitySet.ElementType); AssociationSetEnd setEnd = new AssociationSetEnd(entitySet, set, end); set.AddAssociationSetEnd(setEnd); } set.SetReadOnly(); return set; } private EntitySet CreateEntitySet( LoadMethodSessionState session, EntityType type ) { DbObjectKey key = session.GetKey(type); string schema = key.Schema; string table = null; if (key.TableName != type.Name) { table = key.TableName; } EntitySet entitySet = new EntitySet(type.Name, schema, table, null, type); MetadataProperty property = System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorTypeAttributeName, GetSourceNameFromObjectType(key.ObjectType)); List properties = new List (); properties.Add(property); entitySet.AddMetadataProperties(properties); entitySet.SetReadOnly(); return entitySet; } private string GetSourceNameFromObjectType(DbObjectType dbObjectType) { switch(dbObjectType) { case DbObjectType.Table: return DesignXmlConstants.TypeValueTables; default: Debug.Assert(dbObjectType == DbObjectType.View, "did you change to a call that could have different types?"); return DesignXmlConstants.TypeValueViews; } } private void CreateEdmFunctions(LoadMethodSessionState session) { using(FunctionDetailsReader reader = _loader.LoadStoredProcedureDetails(session.Filters)) { DbObjectKey currentFunction = new DbObjectKey(); List parameters = new List (); while(reader.Read()) { DbObjectKey rowFunction = reader.CreateDbObjectKey(); if (rowFunction != currentFunction) { if (!currentFunction.IsEmpty) { CreateEdmFunction(session, parameters); parameters.Clear(); } currentFunction = rowFunction; } parameters.Add(reader.CreateMemento()); } if (parameters.Count != 0) { CreateEdmFunction(session, parameters); } } } private void CreateEdmFunction(LoadMethodSessionState session, List 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 errors = new List (); if (!row.IsReturnTypeNull) { TypeUsage returnType = GetFunctionTypeUsage(session, row.ReturnType); if (returnType != null) { returnParameter = new FunctionParameter(EdmConstants.ReturnType, returnType, ParameterMode.ReturnValue); } else { isValid = false; errors.Add(new EdmSchemaError( Strings.UnsupportedFunctionReturnDataType( row.ProcedureName, row.ReturnType), (int)ModelBuilderErrorCode.UnsupportedType, EdmSchemaErrorSeverity.Warning)); } } bool caseSensitive = false; UniqueIdentifierService uniqueIdentifiers = new UniqueIdentifierService(caseSensitive); List functionParameters = new List (); for (int i = 0; i < parameters.Count && !row.IsParameterNameNull; i++) { row.Attach(parameters[i]); TypeUsage parameterType = null; if(!row.IsParameterTypeNull) { parameterType = GetFunctionTypeUsage(session, row.ParameterType); } 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( 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); EdmFunction function = new EdmFunction(functionName, _namespaceName, DataSpace.SSpace, new EdmFunctionPayload { Schema = row.IsSchemaNull ? null : row.Schema, StoreFunctionName = functionName != row.ProcedureName ? row.ProcedureName : null, IsAggregate = row.IsIsAggregate, IsBuiltIn = row.IsBuiltIn, IsNiladic = row.IsNiladic, IsComposable = row.IsComposable, ReturnParameter = returnParameter, Parameters = functionParameters.ToArray() }); session.AddErrorsForType(function, errors); if (isValid) { session.Functions.Add(function); } else { session.InvalidTypes.Add(function); } } private TypeUsage GetFunctionTypeUsage(LoadMethodSessionState session, string dataType) { PrimitiveType primitiveType; if (session.TryGetStorePrimitiveType(dataType, out primitiveType)) { TypeUsage usage = TypeUsage.Create(primitiveType, FacetValues.NullFacetValues); return usage; } return null; } private void CreateAssociationTypes(LoadMethodSessionState session) { RelationshipDetailsCollection relationships = _loader.LoadRelationships(session.Filters); string currentRelationshipId = string.Empty; List columns = new List (); foreach (RelationshipDetailsRow row in relationships.Rows) { string rowRelationshipId = row.RelationshipId; if (rowRelationshipId != currentRelationshipId) { if (!string.IsNullOrEmpty(currentRelationshipId)) { CreateAssociationType(session, columns); columns.Clear(); } currentRelationshipId = rowRelationshipId; } columns.Add(row); } if (!string.IsNullOrEmpty(currentRelationshipId)) { CreateAssociationType(session, columns); } } private void CreateAssociationType(LoadMethodSessionState session, List 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 allFkColumnsAreNullable = AreAllFkKeyColumnsNullable(fkEntityType, columns); RelationshipMultiplicity pkMultiplicity = allFkColumnsAreNullable ? 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 ( !allFkColumnsAreNullable && 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, DataSpace.SSpace); type.AddMember(pkEnd); type.AddMember(fkEnd); List errors = new List (); bool isValid = CreateReferentialConstraint(session, type, pkEnd, fkEnd, columns, errors); string errorMessage; 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 toPropertyOfAddedAssc 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(toPropertyOfAddedAssc.DeclaringType) && toProperty.Equals(toPropertyOfAddedAssc)) { 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 bool TryGetEndEntities( LoadMethodSessionState session, RelationshipDetailsRow row, out EntityType pkEntityType, out EntityType fkEntityType) { RelationshipDetailsCollection table = row.Table; DbObjectKey pkKey = new DbObjectKey(row[table.PKCatalogColumn], row[table.PKSchemaColumn], row[table.PKTableColumn], DbObjectType.Unknown); DbObjectKey fkKey = new DbObjectKey(row[table.FKCatalogColumn], row[table.FKSchemaColumn], row[table.FKTableColumn], DbObjectType.Unknown); bool worked = session.TryGetEntity(pkKey, out pkEntityType); worked &= session.TryGetEntity(fkKey, out fkEntityType); return worked; } private static bool AreRelationshipColumnsTheTypesEntireKey( EntityType entity, List columns, Func getColumnName) { if (entity.KeyMembers.Count != columns.Count) { // to be the entire key, // must have the same number of columns return false; } foreach (RelationshipDetailsRow row in columns) { if (!entity.KeyMembers.Contains(getColumnName(row))) { // not a key return false; } } return true; } private static bool AreAllFkKeyColumnsNullable( EntityType entity, List columns) { foreach (RelationshipDetailsRow row in columns) { EdmProperty property; if (entity.Properties.TryGetValue(row.FKColumn, false, out property)) { if (!property.Nullable) { return false; } } else { Debug.Fail("Why didn't we find the column?"); return false; } } return true; } private AssociationEndMember CreateAssociationEnd(LoadMethodSessionState session, EntityType type, RelationshipMultiplicity multiplicity, UniqueIdentifierService usedEndNames, OperationAction deleteAction ) { string role = usedEndNames.AdjustIdentifier(type.Name); RefType refType = type.GetReferenceType(); AssociationEndMember end = new AssociationEndMember(role, refType, multiplicity); end.DeleteBehavior = deleteAction; session.RelationshipEndTypeLookup.Add(end, type); return end; } private bool CreateReferentialConstraint(LoadMethodSessionState session, AssociationType association, AssociationEndMember pkEnd, AssociationEndMember fkEnd, List columns, List errors) { EdmProperty[] fromProperties = new EdmProperty[columns.Count]; EdmProperty[] toProperties = new EdmProperty[columns.Count]; EntityType pkEntityType = session.RelationshipEndTypeLookup[pkEnd]; EntityType fkEntityType = session.RelationshipEndTypeLookup[fkEnd]; for (int index = 0; index < columns.Count; index++) { EdmProperty property; if(!pkEntityType.Properties.TryGetValue(columns[index].PKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, pkEntityType.Name + "." + columns[index].PKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } fromProperties[index] = property; if(!fkEntityType.Properties.TryGetValue(columns[index].FKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, fkEntityType.Name + "." + columns[index].FKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } toProperties[index] = property; } ReferentialConstraint constraint = new ReferentialConstraint(pkEnd, fkEnd, fromProperties, toProperties); association.AddReferentialConstraint(constraint); return true; } static internal bool IsFkPartiallyContainedInPK(AssociationType association, out string errorMessage) { ReferentialConstraint constraint = association.ReferentialConstraints[0]; EntityType toType = (EntityType)constraint.ToProperties[0].DeclaringType; bool toPropertiesAreFullyContainedInPk = true; bool toPropertiesContainedAtLeastOnePK = false; foreach (EdmProperty edmProperty in constraint.ToProperties) { // check if there is at least one to property is not primary key toPropertiesAreFullyContainedInPk &= toType.KeyMembers.Contains(edmProperty); // check if there is one to property is primary key toPropertiesContainedAtLeastOnePK |= toType.KeyMembers.Contains(edmProperty); } if (!toPropertiesAreFullyContainedInPk && toPropertiesContainedAtLeastOnePK) { string foreignKeys = MembersToCommaSeparatedString((System.Collections.IEnumerable)constraint.ToProperties); string primaryKeys = MembersToCommaSeparatedString((System.Collections.IEnumerable)toType.KeyMembers); errorMessage = Strings.UnsupportedForeignKeyPattern(association.Name, foreignKeys, primaryKeys, toType.Name); return true; } errorMessage = ""; return false; } private static string MembersToCommaSeparatedString(System.Collections.IEnumerable members) { StringBuilder builder = new StringBuilder(); builder.Append("{"); StringUtil.ToCommaSeparatedString(builder, members); builder.Append("}"); return builder.ToString(); } private void CreateViewEntityTypes(LoadMethodSessionState session) { TableDetailsCollection viewDetails = _loader.LoadViewDetails(session.Filters); CreateEntityTypes(session, viewDetails, DbObjectType.View); } private void CreateTableEntityTypes(LoadMethodSessionState session) { TableDetailsCollection tableDetails = _loader.LoadTableDetails(session.Filters); CreateEntityTypes(session, tableDetails, DbObjectType.Table); } private void CreateEntityTypes(LoadMethodSessionState session, TableDetailsCollection details, DbObjectType objectType) { DbObjectKey currentKey = new DbObjectKey(); List singleTableColumns = new List (); List primaryKeys = new List (); foreach (TableDetailsRow row in details.Rows) { DbObjectKey rowKey = CreateDbObjectKey(row, objectType); if (rowKey != currentKey) { if (singleTableColumns.Count != 0) { CreateEntityType( session, singleTableColumns, primaryKeys, objectType, null); singleTableColumns.Clear(); primaryKeys.Clear(); } currentKey = rowKey; } singleTableColumns.Add(row); if (row.IsPrimaryKey) { primaryKeys.Add(row.ColumnName); } } // pick up the last one if (singleTableColumns.Count != 0) { CreateEntityType( session, singleTableColumns, primaryKeys, objectType, null); } } private void CreateEntityType(LoadMethodSessionState session, IList columns, ICollection primaryKeys, DbObjectType objectType, List errors) { Debug.Assert(columns.Count != 0, "Trying to create an EntityType with 0 properties"); // // Handle Tables without explicit declaration of keys // DbObjectKey tableKey = CreateDbObjectKey(columns[0], objectType); EntityCreationStatus status = EntityCreationStatus.Normal; if (errors == null) { errors = new List (); } if (primaryKeys.Count == 0) { List pKeys = new List (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"); List members = new List (); List excludedKeyColumns = new List (); foreach (TableDetailsRow row in columns) { bool isPartOfKey = primaryKeys != null && primaryKeys.Contains(row.ColumnName); PrimitiveType primitiveType; if ( row.IsDataTypeNull() || !session.TryGetStorePrimitiveType(row.DataType, out primitiveType)) { string message; if(!row.IsDataTypeNull()) { message = Strings.UnsupportedDataType(row.DataType, row.GetMostQualifiedTableName(), row.ColumnName); } else { message = Strings.UnsupportedDataTypeUnknownType(row.ColumnName, row.GetMostQualifiedTableName()); } errors.Add(new EdmSchemaError(message, (int)ModelBuilderErrorCode.UnsupportedType, EdmSchemaErrorSeverity.Warning)); if(isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } if (isPartOfKey && !Helper.IsValidKeyType(primitiveType.PrimitiveTypeKind)) { // make it a read-only table by calling this method recursivly with no keys List priorErrors = new List (); priorErrors.Add(new EdmSchemaError(Strings.InvalidTypeForPrimaryKey(row.GetMostQualifiedTableName(), row.ColumnName, row.DataType), (int)ModelBuilderErrorCode.InvalidKeyTypeFound, EdmSchemaErrorSeverity.Warning)); string[] keyColumns = new string[0]; CreateEntityType(session, columns, keyColumns, objectType, priorErrors); return; } Dictionary facets = primitiveType.GetAssociatedFacetDescriptions().ToDictionary(fd => fd.FacetName, fd => fd.DefaultValueFacet); facets[DbProviderManifest.NullableFacetName] = Facet.Create(facets[DbProviderManifest.NullableFacetName].Description, row.IsNullable); if (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Decimal) { Facet precision; if (facets.TryGetValue(DbProviderManifest.PrecisionFacetName, out precision)) { if (!row.IsPrecisionNull() && !precision.Description.IsConstant) { if (row.Precision < precision.Description.MinValue || row.Precision > precision.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.PrecisionFacetName, row.Precision, precision.Description.MinValue, precision.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[precision.Name] = Facet.Create(precision.Description, (byte)row.Precision); } } Facet scale; if (facets.TryGetValue(DbProviderManifest.ScaleFacetName, out scale)) { if (!row.IsScaleNull() && !scale.Description.IsConstant) { if (row.Scale < scale.Description.MinValue || row.Scale > scale.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.ScaleFacetName, row.Scale, scale.Description.MinValue, scale.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[scale.Name] = Facet.Create(scale.Description, (byte)row.Scale); } } } else if ((primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.DateTime) || (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Time) || (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.DateTimeOffset)) { Facet datetimePrecision; if (facets.TryGetValue(DbProviderManifest.PrecisionFacetName, out datetimePrecision)) { if (!row.IsDateTimePrecisionNull() && !datetimePrecision.Description.IsConstant) { if (row.DateTimePrecision < datetimePrecision.Description.MinValue || row.DateTimePrecision > datetimePrecision.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.PrecisionFacetName, row.DateTimePrecision, datetimePrecision.Description.MinValue, datetimePrecision.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[datetimePrecision.Name] = Facet.Create(datetimePrecision.Description, (byte)row.DateTimePrecision); } } } else if (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String || primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Binary) { Facet maxLength; if (facets.TryGetValue(DbProviderManifest.MaxLengthFacetName, out maxLength)) { if (!row.IsMaximumLengthNull() && !maxLength.Description.IsConstant) { if (row.MaximumLength < maxLength.Description.MinValue || row.MaximumLength > maxLength.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.MaxLengthFacetName, row.MaximumLength, maxLength.Description.MinValue, maxLength.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[maxLength.Name] = Facet.Create(maxLength.Description, row.MaximumLength); } } } if (!row.IsIsIdentityNull() && row.IsIdentity) { Facet facet = Facet.Create(System.Data.Metadata.Edm.Converter.StoreGeneratedPatternFacet, StoreGeneratedPattern.Identity); facets.Add(facet.Name, facet); } else if (!row.IsIsServerGeneratedNull() && row.IsServerGenerated) { Facet facet = Facet.Create(System.Data.Metadata.Edm.Converter.StoreGeneratedPatternFacet, StoreGeneratedPattern.Computed); facets.Add(facet.Name, facet); } members.Add(new EdmProperty(row.ColumnName, TypeUsage.Create(primitiveType, facets.Values))); } if (excludedKeyColumns.Count > 0) { // see if we have any keys left if (primaryKeys != null && excludedKeyColumns.Count < primaryKeys.Count) { List newKeys = new List (); foreach (string key in primaryKeys) { if (!excludedKeyColumns.Contains(key)) { newKeys.Add(key); } } primaryKeys = newKeys; 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); EntityType entityType = new EntityType(typeName, _namespaceName, DataSpace.SSpace, primaryKeys, members); 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; } entityType.SetReadOnly(); session.AddErrorsForType(entityType, errors); } private static bool InferKeyColumns(LoadMethodSessionState session, IList columns, List pKeys, DbObjectKey tableKey, ref ICollection primaryKeys) { for (int i = 0; i < columns.Count; i++) { if (!columns[i].IsNullable) { PrimitiveType primitiveType; if (session.TryGetStorePrimitiveType(columns[i].DataType, out primitiveType) && Helper.IsValidKeyType(primitiveType.PrimitiveTypeKind) ) { pKeys.Add(columns[i].ColumnName); } } } // if there are valid key column candidates, make them the new key columns if (pKeys.Count > 0) { primaryKeys = pKeys; } else { primaryKeys = null; } return primaryKeys != null; } private DbObjectKey CreateDbObjectKey(TableDetailsRow row, DbObjectType objectType) { TableDetailsCollection table = row.Table; return new DbObjectKey(row[table.CatalogColumn], row[table.SchemaColumn], row[table.TableNameColumn], objectType); } /// /// Populates DefiningQuery attribute of RO view entities /// /// /// /// private void FixupKeylessEntitySets(EntityContainer entityContainer, LoadMethodSessionState session) { // if there are views to process if (session.ReadOnlyEntities.Count > 0) { // // create 'bogus' metadataworkspace // MetadataWorkspace metadataWorkspace = CreateMetadataWorkspace(entityContainer, session); // // For all tables/views that we could infer valid keys, update DefiningQuery with // provider specific ReadOnly view SQL // foreach (EntityType entityType in session.ReadOnlyEntities) { EntitySet entitySet = session.EntityTypeToSet[entityType]; DbObjectKey key = session.GetKey(entityType); // add properties that make it possible for the designer to track back these // types to their source db objects Listproperties = new List (); if (key.Schema != null) { properties.Add(System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorSchemaAttributeName, key.Schema)); } properties.Add(System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorNameAttributeName, key.TableName)); entitySet.AddMetadataProperties(properties); FixupViewEntitySetDefiningQuery(entitySet, metadataWorkspace); } } } /// /// Creates 'transient' metadataworkspace based on store schema (EntityContainer) and trivial C-S mapping /// /// /// ///private MetadataWorkspace CreateMetadataWorkspace(EntityContainer entityContainer, LoadMethodSessionState session) { MetadataWorkspace metadataWorkspace = new MetadataWorkspace(); EntityModelSchemaGenerator modelGen = new EntityModelSchemaGenerator(entityContainer); modelGen.GenerateMetadata(); // register edmitemcollection metadataWorkspace.RegisterItemCollection(modelGen.EdmItemCollection); // register StoreItemCollection metadataWorkspace.RegisterItemCollection(session.ItemCollection); // register mapping using (MemoryStream memStream = new MemoryStream()) { using (XmlWriter xmlWriter = XmlWriter.Create(memStream)) { modelGen.WriteStorageMapping(xmlWriter); xmlWriter.Close(); } memStream.Seek(0, SeekOrigin.Begin); using (XmlReader xmlReader = XmlReader.Create(memStream)) { List xmlReaders = new List (); xmlReaders.Add(xmlReader); metadataWorkspace.RegisterItemCollection(new StorageMappingItemCollection(modelGen.EdmItemCollection, session.ItemCollection, xmlReaders)); } } return metadataWorkspace; } /// /// Generates provider specific, read only SQL and updates entitySet DefiningQuery /// /// /// private void FixupViewEntitySetDefiningQuery(EntitySet entitySet, MetadataWorkspace metadataWorkspace) { DbQueryCommandTree dbCommandTree = new DbQueryCommandTree(metadataWorkspace, DataSpace.SSpace); DbExpressionBinding inputBinding = dbCommandTree.CreateExpressionBinding(dbCommandTree.CreateScanExpression(entitySet), entitySet.Name); List> projectList = new List >(entitySet.ElementType.Members.Count); foreach (EdmMember member in entitySet.ElementType.Members) { Debug.Assert(member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty, "Every member must be a edmproperty"); EdmProperty propertyInfo = (EdmProperty)member; projectList.Add(new KeyValuePair (member.Name, dbCommandTree.CreatePropertyExpression(propertyInfo, inputBinding.Variable))); } dbCommandTree.Query = dbCommandTree.CreateProjectExpression(inputBinding, dbCommandTree.CreateNewRowExpression(projectList)); dbCommandTree.Validate(); // // get provider SQL and set entitySet DefiningQuery // entitySet.DefiningQuery = (DbProviderServices.GetProviderServices(_loader.EntityConnection.StoreProviderFactory) .CreateCommandDefinition(dbCommandTree)) .CreateCommand().CommandText; Debug.Assert(!String.IsNullOrEmpty(entitySet.DefiningQuery), "DefiningQuery must not be null or empty"); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections.Generic; using System.Diagnostics; using System.Xml; using SOM=System.Data.EntityModel; using System.Globalization; using System.Data.Metadata.Edm; using System.Data.Entity.Design.Common; using System.Data.Entity.Design.SsdlGenerator; using System.Data.Common; using System.Data.EntityClient; using System.IO; using System.Data.Mapping; using System.Data.Common.Utils; using System.Collections.ObjectModel; using System.Data.Common.CommandTrees; using System.Text; using System.Linq; namespace System.Data.Entity.Design { ////// Responsible for Loading Database Schema Information /// public sealed partial class EntityStoreSchemaGenerator { private const string CONTAINER_SUFFIX = "Container"; private EntityStoreSchemaGeneratorDatabaseSchemaLoader _loader; private readonly string _provider; private string _providerManifestToken = string.Empty; private EntityContainer _entityContainer = null; private StoreItemCollection _storeItemCollection; private string _namespaceName; private MetadataItemSerializer.ErrorsLookup _errorsLookup; private List_invalidTypes; /// /// Creates a new EntityStoreGenerator /// /// The name of the provider to use to load the schema information. /// A connection string to the DB that should be loaded from. /// The namespace name to use for the store metadata that is generated. public EntityStoreSchemaGenerator(string providerInvariantName, string connectionString, string namespaceName) { EDesignUtil.CheckStringArgument(providerInvariantName, "providerInvariantName"); EDesignUtil.CheckArgumentNull(connectionString, "connectionString"); // check for NULL string and support empty connection string EDesignUtil.CheckStringArgument(namespaceName, "namespaceName"); _namespaceName = EntityModelSchemaGenerator.CreateValidNamespaceName(namespaceName, 'z'); if (_namespaceName != namespaceName) { throw EDesignUtil.InvalidNamespaceNameArgument(namespaceName); } _provider = providerInvariantName; _loader = new EntityStoreSchemaGeneratorDatabaseSchemaLoader(providerInvariantName, connectionString); } ////// Gets the EntityContainer that was created /// public EntityContainer EntityContainer { get { return _entityContainer; } } ////// Gets the StoreItemCollection that was created /// public StoreItemCollection StoreItemCollection { get { return _storeItemCollection; } } ////// Creates a Metadata schema from the DbSchemaLoader that was passed in /// ///The new metadata for the schema that was loaded public IListGenerateStoreMetadata() { List filters = new List (); return GenerateStoreMetadata(filters); } /// /// Creates a Metadata schema from the DbSchemaLoader that was passed in /// /// The filters to be applied during generation. ///The new metadata for the schema that was loaded public IListGenerateStoreMetadata(IEnumerable filters) { EDesignUtil.CheckArgumentNull(filters, "filters"); if (_entityContainer != null) { _entityContainer = null; _storeItemCollection = null; _errorsLookup = null; _invalidTypes = null; } LoadMethodSessionState session = new LoadMethodSessionState(); 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)); 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); CreateEdmFunctions(session); foreach (EdmFunction function in session.Functions) { function.SetReadOnly(); session.ItemCollection.AddInternal(function); } if (!HasErrorSeverityErrors(session.Errors)) { _entityContainer = entityContainer; _storeItemCollection = session.ItemCollection; _errorsLookup = session.ItemToErrorsMap; _invalidTypes = new List (session.InvalidTypes); } } catch (Exception e) { if (EntityUtil.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 (session.Errors); } /// /// Writes the Schema to xml /// /// The name of the file to write the xml to. public void WriteStoreSchema(string outputFileName) { EDesignUtil.CheckStringArgument(outputFileName, "outputFileName"); CheckValidItemCollection(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter writer = XmlWriter.Create(outputFileName, settings)) { WriteStoreSchema(writer); } } ////// Writes the Schema to xml. /// /// The XmlWriter to write the xml to. public void WriteStoreSchema(XmlWriter writer) { EDesignUtil.CheckArgumentNull(writer, "writer"); CheckValidItemCollection(); // we are going to add this EntityStoreSchemaGenerator namespace at the top of // the file so that when we mark the entitysets with where they came from // we don't have to repeat the namespace on each node. The VS tools use // the source information to give better messages when refreshing the .ssdl from the db // e.g. //// ("store", DesignXmlConstants.EntityStoreSchemaGeneratorNamespace); MetadataItemSerializer.WriteXml(writer, StoreItemCollection, _namespaceName, _errorsLookup, _invalidTypes, _provider, _providerManifestToken, xmlPrefixToNamespace); } /// /// Creates an EntityConnection loaded with the providers metadata for the /// /// The provider invariant name. /// The connection for the providers connection. ///An EntityConnection that can query the ConceptualSchemaDefinition for the provider. public static EntityConnection CreateStoreSchemaConnection(string providerInvariantName, string connectionString) { EDesignUtil.CheckArgumentNull(providerInvariantName, "providerInvariantName"); EDesignUtil.CheckArgumentNull(connectionString, "connectionString"); DbProviderFactory factory; try { factory = DbProviderFactories.GetFactory(providerInvariantName); } catch (ArgumentException e) { throw EDesignUtil.Argument(Strings.EntityClient_InvalidStoreProvider(providerInvariantName), e); } DbProviderServices providerServices = DbProviderServices.GetProviderServices(factory); DbConnection providerConnection = factory.CreateConnection(); if (providerConnection == null) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderFactoryReturnedNullFactory(providerInvariantName)); } providerConnection.ConnectionString = connectionString; MetadataWorkspace workspace = GetProviderSchemaMetadataWorkspace(providerServices, providerConnection); // create the connection with the information we have return new EntityConnection(workspace, providerConnection); } private static MetadataWorkspace GetProviderSchemaMetadataWorkspace(DbProviderServices providerServices, DbConnection providerConnection) { XmlReader csdl = null; XmlReader ssdl = null; XmlReader msl = null; try { // create the metadata workspace MetadataWorkspace workspace = new MetadataWorkspace(); string manifestToken = providerServices.GetProviderManifestToken(providerConnection); DbProviderManifest providerManifest = providerServices.GetProviderManifest(manifestToken); // create the EdmItemCollection IListerrors; ssdl = providerManifest.GetInformation(DbProviderManifest.StoreSchemaDefinition); string location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.StoreSchemaDefinition); List ssdlLocations = new List (1); ssdlLocations.Add(location); StoreItemCollection storeItemCollection = new StoreItemCollection(new XmlReader[] { ssdl }, ssdlLocations.AsReadOnly(), out errors); ThrowOnError(errors); workspace.RegisterItemCollection(storeItemCollection); csdl = DbProviderServices.GetConceptualSchemaDescription(); location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.ConceptualSchemaDefinition); List csdlLocations = new List (1); csdlLocations.Add(location); EdmItemCollection edmItemCollection = new EdmItemCollection(new XmlReader[] { csdl }, csdlLocations.AsReadOnly(), out errors); ThrowOnError(errors); workspace.RegisterItemCollection(edmItemCollection); msl = providerManifest.GetInformation(DbProviderManifest.StoreSchemaMapping); location = Strings.DbProviderServicesInformationLocationPath(providerConnection.GetType().Name, DbProviderManifest.StoreSchemaMapping); List mslLocations = new List (1); mslLocations.Add(location); StorageMappingItemCollection mappingItemCollection = new StorageMappingItemCollection(edmItemCollection, storeItemCollection, new XmlReader[] { msl }, mslLocations, out errors); ThrowOnError(errors); workspace.RegisterItemCollection(mappingItemCollection); // make the views generate here so we can wrap the provider schema problems // in a ProviderIncompatibleException ForceViewGeneration(workspace); return workspace; } catch (ProviderIncompatibleException) { // we don't really want to catch this one, just rethrow it throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderSchemaErrors, e); } throw; } finally { if (csdl != null) ((IDisposable)csdl).Dispose(); if (ssdl != null) ((IDisposable)ssdl).Dispose(); if (msl != null) ((IDisposable)msl).Dispose(); } } private static void ForceViewGeneration(MetadataWorkspace workspace) { ReadOnlyCollection containers = workspace.GetItems (DataSpace.SSpace); Debug.Assert(containers.Count != 0, "no s space containers found"); Debug.Assert(containers[0].BaseEntitySets.Count != 0, "no entity sets in the sspace container"); workspace.GetCqtView(containers[0].BaseEntitySets[0]); } private static void ThrowOnError(IList errors) { if (errors.Count != 0) { if (!MetadataHelper.CheckIfAllErrorsAreWarnings(errors)) { throw EDesignUtil.ProviderIncompatible(Strings.ProviderSchemaErrors, EntityUtil.InvalidSchemaEncountered(Helper.CombineErrorMessage(errors))); } } } private void CheckValidItemCollection() { if (_entityContainer == null) { throw EDesignUtil.EntityStoreGeneratorSchemaNotLoaded(); } } internal static bool HasErrorSeverityErrors(IEnumerable errors) { foreach (EdmSchemaError error in errors) { if (error.Severity == EdmSchemaErrorSeverity.Error) { return true; } } return false; } private AssociationSet CreateAssociationSet(LoadMethodSessionState session, AssociationType type) { AssociationSet set = new AssociationSet(type.Name, type); foreach(AssociationEndMember end in type.RelationshipEndMembers) { EntitySet entitySet = session.GetEntitySet(end); DbObjectKey key = session.GetKey(entitySet.ElementType); AssociationSetEnd setEnd = new AssociationSetEnd(entitySet, set, end); set.AddAssociationSetEnd(setEnd); } set.SetReadOnly(); return set; } private EntitySet CreateEntitySet( LoadMethodSessionState session, EntityType type ) { DbObjectKey key = session.GetKey(type); string schema = key.Schema; string table = null; if (key.TableName != type.Name) { table = key.TableName; } EntitySet entitySet = new EntitySet(type.Name, schema, table, null, type); MetadataProperty property = System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorTypeAttributeName, GetSourceNameFromObjectType(key.ObjectType)); List properties = new List (); properties.Add(property); entitySet.AddMetadataProperties(properties); entitySet.SetReadOnly(); return entitySet; } private string GetSourceNameFromObjectType(DbObjectType dbObjectType) { switch(dbObjectType) { case DbObjectType.Table: return DesignXmlConstants.TypeValueTables; default: Debug.Assert(dbObjectType == DbObjectType.View, "did you change to a call that could have different types?"); return DesignXmlConstants.TypeValueViews; } } private void CreateEdmFunctions(LoadMethodSessionState session) { using(FunctionDetailsReader reader = _loader.LoadStoredProcedureDetails(session.Filters)) { DbObjectKey currentFunction = new DbObjectKey(); List parameters = new List (); while(reader.Read()) { DbObjectKey rowFunction = reader.CreateDbObjectKey(); if (rowFunction != currentFunction) { if (!currentFunction.IsEmpty) { CreateEdmFunction(session, parameters); parameters.Clear(); } currentFunction = rowFunction; } parameters.Add(reader.CreateMemento()); } if (parameters.Count != 0) { CreateEdmFunction(session, parameters); } } } private void CreateEdmFunction(LoadMethodSessionState session, List 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 errors = new List (); if (!row.IsReturnTypeNull) { TypeUsage returnType = GetFunctionTypeUsage(session, row.ReturnType); if (returnType != null) { returnParameter = new FunctionParameter(EdmConstants.ReturnType, returnType, ParameterMode.ReturnValue); } else { isValid = false; errors.Add(new EdmSchemaError( Strings.UnsupportedFunctionReturnDataType( row.ProcedureName, row.ReturnType), (int)ModelBuilderErrorCode.UnsupportedType, EdmSchemaErrorSeverity.Warning)); } } bool caseSensitive = false; UniqueIdentifierService uniqueIdentifiers = new UniqueIdentifierService(caseSensitive); List functionParameters = new List (); for (int i = 0; i < parameters.Count && !row.IsParameterNameNull; i++) { row.Attach(parameters[i]); TypeUsage parameterType = null; if(!row.IsParameterTypeNull) { parameterType = GetFunctionTypeUsage(session, row.ParameterType); } 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( 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); EdmFunction function = new EdmFunction(functionName, _namespaceName, DataSpace.SSpace, new EdmFunctionPayload { Schema = row.IsSchemaNull ? null : row.Schema, StoreFunctionName = functionName != row.ProcedureName ? row.ProcedureName : null, IsAggregate = row.IsIsAggregate, IsBuiltIn = row.IsBuiltIn, IsNiladic = row.IsNiladic, IsComposable = row.IsComposable, ReturnParameter = returnParameter, Parameters = functionParameters.ToArray() }); session.AddErrorsForType(function, errors); if (isValid) { session.Functions.Add(function); } else { session.InvalidTypes.Add(function); } } private TypeUsage GetFunctionTypeUsage(LoadMethodSessionState session, string dataType) { PrimitiveType primitiveType; if (session.TryGetStorePrimitiveType(dataType, out primitiveType)) { TypeUsage usage = TypeUsage.Create(primitiveType, FacetValues.NullFacetValues); return usage; } return null; } private void CreateAssociationTypes(LoadMethodSessionState session) { RelationshipDetailsCollection relationships = _loader.LoadRelationships(session.Filters); string currentRelationshipId = string.Empty; List columns = new List (); foreach (RelationshipDetailsRow row in relationships.Rows) { string rowRelationshipId = row.RelationshipId; if (rowRelationshipId != currentRelationshipId) { if (!string.IsNullOrEmpty(currentRelationshipId)) { CreateAssociationType(session, columns); columns.Clear(); } currentRelationshipId = rowRelationshipId; } columns.Add(row); } if (!string.IsNullOrEmpty(currentRelationshipId)) { CreateAssociationType(session, columns); } } private void CreateAssociationType(LoadMethodSessionState session, List 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 allFkColumnsAreNullable = AreAllFkKeyColumnsNullable(fkEntityType, columns); RelationshipMultiplicity pkMultiplicity = allFkColumnsAreNullable ? 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 ( !allFkColumnsAreNullable && 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, DataSpace.SSpace); type.AddMember(pkEnd); type.AddMember(fkEnd); List errors = new List (); bool isValid = CreateReferentialConstraint(session, type, pkEnd, fkEnd, columns, errors); string errorMessage; 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 toPropertyOfAddedAssc 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(toPropertyOfAddedAssc.DeclaringType) && toProperty.Equals(toPropertyOfAddedAssc)) { 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 bool TryGetEndEntities( LoadMethodSessionState session, RelationshipDetailsRow row, out EntityType pkEntityType, out EntityType fkEntityType) { RelationshipDetailsCollection table = row.Table; DbObjectKey pkKey = new DbObjectKey(row[table.PKCatalogColumn], row[table.PKSchemaColumn], row[table.PKTableColumn], DbObjectType.Unknown); DbObjectKey fkKey = new DbObjectKey(row[table.FKCatalogColumn], row[table.FKSchemaColumn], row[table.FKTableColumn], DbObjectType.Unknown); bool worked = session.TryGetEntity(pkKey, out pkEntityType); worked &= session.TryGetEntity(fkKey, out fkEntityType); return worked; } private static bool AreRelationshipColumnsTheTypesEntireKey( EntityType entity, List columns, Func getColumnName) { if (entity.KeyMembers.Count != columns.Count) { // to be the entire key, // must have the same number of columns return false; } foreach (RelationshipDetailsRow row in columns) { if (!entity.KeyMembers.Contains(getColumnName(row))) { // not a key return false; } } return true; } private static bool AreAllFkKeyColumnsNullable( EntityType entity, List columns) { foreach (RelationshipDetailsRow row in columns) { EdmProperty property; if (entity.Properties.TryGetValue(row.FKColumn, false, out property)) { if (!property.Nullable) { return false; } } else { Debug.Fail("Why didn't we find the column?"); return false; } } return true; } private AssociationEndMember CreateAssociationEnd(LoadMethodSessionState session, EntityType type, RelationshipMultiplicity multiplicity, UniqueIdentifierService usedEndNames, OperationAction deleteAction ) { string role = usedEndNames.AdjustIdentifier(type.Name); RefType refType = type.GetReferenceType(); AssociationEndMember end = new AssociationEndMember(role, refType, multiplicity); end.DeleteBehavior = deleteAction; session.RelationshipEndTypeLookup.Add(end, type); return end; } private bool CreateReferentialConstraint(LoadMethodSessionState session, AssociationType association, AssociationEndMember pkEnd, AssociationEndMember fkEnd, List columns, List errors) { EdmProperty[] fromProperties = new EdmProperty[columns.Count]; EdmProperty[] toProperties = new EdmProperty[columns.Count]; EntityType pkEntityType = session.RelationshipEndTypeLookup[pkEnd]; EntityType fkEntityType = session.RelationshipEndTypeLookup[fkEnd]; for (int index = 0; index < columns.Count; index++) { EdmProperty property; if(!pkEntityType.Properties.TryGetValue(columns[index].PKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, pkEntityType.Name + "." + columns[index].PKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } fromProperties[index] = property; if(!fkEntityType.Properties.TryGetValue(columns[index].FKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, fkEntityType.Name + "." + columns[index].FKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } toProperties[index] = property; } ReferentialConstraint constraint = new ReferentialConstraint(pkEnd, fkEnd, fromProperties, toProperties); association.AddReferentialConstraint(constraint); return true; } static internal bool IsFkPartiallyContainedInPK(AssociationType association, out string errorMessage) { ReferentialConstraint constraint = association.ReferentialConstraints[0]; EntityType toType = (EntityType)constraint.ToProperties[0].DeclaringType; bool toPropertiesAreFullyContainedInPk = true; bool toPropertiesContainedAtLeastOnePK = false; foreach (EdmProperty edmProperty in constraint.ToProperties) { // check if there is at least one to property is not primary key toPropertiesAreFullyContainedInPk &= toType.KeyMembers.Contains(edmProperty); // check if there is one to property is primary key toPropertiesContainedAtLeastOnePK |= toType.KeyMembers.Contains(edmProperty); } if (!toPropertiesAreFullyContainedInPk && toPropertiesContainedAtLeastOnePK) { string foreignKeys = MembersToCommaSeparatedString((System.Collections.IEnumerable)constraint.ToProperties); string primaryKeys = MembersToCommaSeparatedString((System.Collections.IEnumerable)toType.KeyMembers); errorMessage = Strings.UnsupportedForeignKeyPattern(association.Name, foreignKeys, primaryKeys, toType.Name); return true; } errorMessage = ""; return false; } private static string MembersToCommaSeparatedString(System.Collections.IEnumerable members) { StringBuilder builder = new StringBuilder(); builder.Append("{"); StringUtil.ToCommaSeparatedString(builder, members); builder.Append("}"); return builder.ToString(); } private void CreateViewEntityTypes(LoadMethodSessionState session) { TableDetailsCollection viewDetails = _loader.LoadViewDetails(session.Filters); CreateEntityTypes(session, viewDetails, DbObjectType.View); } private void CreateTableEntityTypes(LoadMethodSessionState session) { TableDetailsCollection tableDetails = _loader.LoadTableDetails(session.Filters); CreateEntityTypes(session, tableDetails, DbObjectType.Table); } private void CreateEntityTypes(LoadMethodSessionState session, TableDetailsCollection details, DbObjectType objectType) { DbObjectKey currentKey = new DbObjectKey(); List singleTableColumns = new List (); List primaryKeys = new List (); foreach (TableDetailsRow row in details.Rows) { DbObjectKey rowKey = CreateDbObjectKey(row, objectType); if (rowKey != currentKey) { if (singleTableColumns.Count != 0) { CreateEntityType( session, singleTableColumns, primaryKeys, objectType, null); singleTableColumns.Clear(); primaryKeys.Clear(); } currentKey = rowKey; } singleTableColumns.Add(row); if (row.IsPrimaryKey) { primaryKeys.Add(row.ColumnName); } } // pick up the last one if (singleTableColumns.Count != 0) { CreateEntityType( session, singleTableColumns, primaryKeys, objectType, null); } } private void CreateEntityType(LoadMethodSessionState session, IList columns, ICollection primaryKeys, DbObjectType objectType, List errors) { Debug.Assert(columns.Count != 0, "Trying to create an EntityType with 0 properties"); // // Handle Tables without explicit declaration of keys // DbObjectKey tableKey = CreateDbObjectKey(columns[0], objectType); EntityCreationStatus status = EntityCreationStatus.Normal; if (errors == null) { errors = new List (); } if (primaryKeys.Count == 0) { List pKeys = new List (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"); List members = new List (); List excludedKeyColumns = new List (); foreach (TableDetailsRow row in columns) { bool isPartOfKey = primaryKeys != null && primaryKeys.Contains(row.ColumnName); PrimitiveType primitiveType; if ( row.IsDataTypeNull() || !session.TryGetStorePrimitiveType(row.DataType, out primitiveType)) { string message; if(!row.IsDataTypeNull()) { message = Strings.UnsupportedDataType(row.DataType, row.GetMostQualifiedTableName(), row.ColumnName); } else { message = Strings.UnsupportedDataTypeUnknownType(row.ColumnName, row.GetMostQualifiedTableName()); } errors.Add(new EdmSchemaError(message, (int)ModelBuilderErrorCode.UnsupportedType, EdmSchemaErrorSeverity.Warning)); if(isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } if (isPartOfKey && !Helper.IsValidKeyType(primitiveType.PrimitiveTypeKind)) { // make it a read-only table by calling this method recursivly with no keys List priorErrors = new List (); priorErrors.Add(new EdmSchemaError(Strings.InvalidTypeForPrimaryKey(row.GetMostQualifiedTableName(), row.ColumnName, row.DataType), (int)ModelBuilderErrorCode.InvalidKeyTypeFound, EdmSchemaErrorSeverity.Warning)); string[] keyColumns = new string[0]; CreateEntityType(session, columns, keyColumns, objectType, priorErrors); return; } Dictionary facets = primitiveType.GetAssociatedFacetDescriptions().ToDictionary(fd => fd.FacetName, fd => fd.DefaultValueFacet); facets[DbProviderManifest.NullableFacetName] = Facet.Create(facets[DbProviderManifest.NullableFacetName].Description, row.IsNullable); if (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Decimal) { Facet precision; if (facets.TryGetValue(DbProviderManifest.PrecisionFacetName, out precision)) { if (!row.IsPrecisionNull() && !precision.Description.IsConstant) { if (row.Precision < precision.Description.MinValue || row.Precision > precision.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.PrecisionFacetName, row.Precision, precision.Description.MinValue, precision.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[precision.Name] = Facet.Create(precision.Description, (byte)row.Precision); } } Facet scale; if (facets.TryGetValue(DbProviderManifest.ScaleFacetName, out scale)) { if (!row.IsScaleNull() && !scale.Description.IsConstant) { if (row.Scale < scale.Description.MinValue || row.Scale > scale.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.ScaleFacetName, row.Scale, scale.Description.MinValue, scale.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[scale.Name] = Facet.Create(scale.Description, (byte)row.Scale); } } } else if ((primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.DateTime) || (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Time) || (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.DateTimeOffset)) { Facet datetimePrecision; if (facets.TryGetValue(DbProviderManifest.PrecisionFacetName, out datetimePrecision)) { if (!row.IsDateTimePrecisionNull() && !datetimePrecision.Description.IsConstant) { if (row.DateTimePrecision < datetimePrecision.Description.MinValue || row.DateTimePrecision > datetimePrecision.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.PrecisionFacetName, row.DateTimePrecision, datetimePrecision.Description.MinValue, datetimePrecision.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[datetimePrecision.Name] = Facet.Create(datetimePrecision.Description, (byte)row.DateTimePrecision); } } } else if (primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String || primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Binary) { Facet maxLength; if (facets.TryGetValue(DbProviderManifest.MaxLengthFacetName, out maxLength)) { if (!row.IsMaximumLengthNull() && !maxLength.Description.IsConstant) { if (row.MaximumLength < maxLength.Description.MinValue || row.MaximumLength > maxLength.Description.MaxValue) { DbObjectKey key = CreateDbObjectKey(row, objectType); errors.Add(new EdmSchemaError( Strings.ColumnFacetValueOutOfRange( DbProviderManifest.MaxLengthFacetName, row.MaximumLength, maxLength.Description.MinValue, maxLength.Description.MaxValue, row.ColumnName, key), (int)ModelBuilderErrorCode.FacetValueOutOfRange, EdmSchemaErrorSeverity.Warning)); if (isPartOfKey) { excludedKeyColumns.Add(row.ColumnName); } continue; } facets[maxLength.Name] = Facet.Create(maxLength.Description, row.MaximumLength); } } } if (!row.IsIsIdentityNull() && row.IsIdentity) { Facet facet = Facet.Create(System.Data.Metadata.Edm.Converter.StoreGeneratedPatternFacet, StoreGeneratedPattern.Identity); facets.Add(facet.Name, facet); } else if (!row.IsIsServerGeneratedNull() && row.IsServerGenerated) { Facet facet = Facet.Create(System.Data.Metadata.Edm.Converter.StoreGeneratedPatternFacet, StoreGeneratedPattern.Computed); facets.Add(facet.Name, facet); } members.Add(new EdmProperty(row.ColumnName, TypeUsage.Create(primitiveType, facets.Values))); } if (excludedKeyColumns.Count > 0) { // see if we have any keys left if (primaryKeys != null && excludedKeyColumns.Count < primaryKeys.Count) { List newKeys = new List (); foreach (string key in primaryKeys) { if (!excludedKeyColumns.Contains(key)) { newKeys.Add(key); } } primaryKeys = newKeys; 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); EntityType entityType = new EntityType(typeName, _namespaceName, DataSpace.SSpace, primaryKeys, members); 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; } entityType.SetReadOnly(); session.AddErrorsForType(entityType, errors); } private static bool InferKeyColumns(LoadMethodSessionState session, IList columns, List pKeys, DbObjectKey tableKey, ref ICollection primaryKeys) { for (int i = 0; i < columns.Count; i++) { if (!columns[i].IsNullable) { PrimitiveType primitiveType; if (session.TryGetStorePrimitiveType(columns[i].DataType, out primitiveType) && Helper.IsValidKeyType(primitiveType.PrimitiveTypeKind) ) { pKeys.Add(columns[i].ColumnName); } } } // if there are valid key column candidates, make them the new key columns if (pKeys.Count > 0) { primaryKeys = pKeys; } else { primaryKeys = null; } return primaryKeys != null; } private DbObjectKey CreateDbObjectKey(TableDetailsRow row, DbObjectType objectType) { TableDetailsCollection table = row.Table; return new DbObjectKey(row[table.CatalogColumn], row[table.SchemaColumn], row[table.TableNameColumn], objectType); } /// /// Populates DefiningQuery attribute of RO view entities /// /// /// /// private void FixupKeylessEntitySets(EntityContainer entityContainer, LoadMethodSessionState session) { // if there are views to process if (session.ReadOnlyEntities.Count > 0) { // // create 'bogus' metadataworkspace // MetadataWorkspace metadataWorkspace = CreateMetadataWorkspace(entityContainer, session); // // For all tables/views that we could infer valid keys, update DefiningQuery with // provider specific ReadOnly view SQL // foreach (EntityType entityType in session.ReadOnlyEntities) { EntitySet entitySet = session.EntityTypeToSet[entityType]; DbObjectKey key = session.GetKey(entityType); // add properties that make it possible for the designer to track back these // types to their source db objects Listproperties = new List (); if (key.Schema != null) { properties.Add(System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorSchemaAttributeName, key.Schema)); } properties.Add(System.Data.EntityModel.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromOtherNamespaceXmlAttribute(DesignXmlConstants.EntityStoreSchemaGeneratorNamespace, DesignXmlConstants.EntityStoreSchemaGeneratorNameAttributeName, key.TableName)); entitySet.AddMetadataProperties(properties); FixupViewEntitySetDefiningQuery(entitySet, metadataWorkspace); } } } /// /// Creates 'transient' metadataworkspace based on store schema (EntityContainer) and trivial C-S mapping /// /// /// ///private MetadataWorkspace CreateMetadataWorkspace(EntityContainer entityContainer, LoadMethodSessionState session) { MetadataWorkspace metadataWorkspace = new MetadataWorkspace(); EntityModelSchemaGenerator modelGen = new EntityModelSchemaGenerator(entityContainer); modelGen.GenerateMetadata(); // register edmitemcollection metadataWorkspace.RegisterItemCollection(modelGen.EdmItemCollection); // register StoreItemCollection metadataWorkspace.RegisterItemCollection(session.ItemCollection); // register mapping using (MemoryStream memStream = new MemoryStream()) { using (XmlWriter xmlWriter = XmlWriter.Create(memStream)) { modelGen.WriteStorageMapping(xmlWriter); xmlWriter.Close(); } memStream.Seek(0, SeekOrigin.Begin); using (XmlReader xmlReader = XmlReader.Create(memStream)) { List xmlReaders = new List (); xmlReaders.Add(xmlReader); metadataWorkspace.RegisterItemCollection(new StorageMappingItemCollection(modelGen.EdmItemCollection, session.ItemCollection, xmlReaders)); } } return metadataWorkspace; } /// /// Generates provider specific, read only SQL and updates entitySet DefiningQuery /// /// /// private void FixupViewEntitySetDefiningQuery(EntitySet entitySet, MetadataWorkspace metadataWorkspace) { DbQueryCommandTree dbCommandTree = new DbQueryCommandTree(metadataWorkspace, DataSpace.SSpace); DbExpressionBinding inputBinding = dbCommandTree.CreateExpressionBinding(dbCommandTree.CreateScanExpression(entitySet), entitySet.Name); List> projectList = new List >(entitySet.ElementType.Members.Count); foreach (EdmMember member in entitySet.ElementType.Members) { Debug.Assert(member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty, "Every member must be a edmproperty"); EdmProperty propertyInfo = (EdmProperty)member; projectList.Add(new KeyValuePair (member.Name, dbCommandTree.CreatePropertyExpression(propertyInfo, inputBinding.Variable))); } dbCommandTree.Query = dbCommandTree.CreateProjectExpression(inputBinding, dbCommandTree.CreateNewRowExpression(projectList)); dbCommandTree.Validate(); // // get provider SQL and set entitySet DefiningQuery // entitySet.DefiningQuery = (DbProviderServices.GetProviderServices(_loader.EntityConnection.StoreProviderFactory) .CreateCommandDefinition(dbCommandTree)) .CreateCommand().CommandText; Debug.Assert(!String.IsNullOrEmpty(entitySet.DefiningQuery), "DefiningQuery must not be null or empty"); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MouseWheelEventArgs.cs
- CodeComment.cs
- DateTimeFormatInfo.cs
- TextRangeEditTables.cs
- ChangeProcessor.cs
- XmlCodeExporter.cs
- BackEase.cs
- ControlPropertyNameConverter.cs
- ExtenderProvidedPropertyAttribute.cs
- SevenBitStream.cs
- SchemaImporter.cs
- MetadataElement.cs
- TreeNodeCollection.cs
- ComponentManagerBroker.cs
- CommandValueSerializer.cs
- Conditional.cs
- CachedPathData.cs
- CompilerError.cs
- Wizard.cs
- RetriableClipboard.cs
- DigestTraceRecordHelper.cs
- XmlChoiceIdentifierAttribute.cs
- MsmqProcessProtocolHandler.cs
- ResolveMatchesCD1.cs
- TextFormatterImp.cs
- CheckedListBox.cs
- GenericAuthenticationEventArgs.cs
- BaseParaClient.cs
- XmlSchemaInferenceException.cs
- WebPartAuthorizationEventArgs.cs
- ReferenceConverter.cs
- SecurityDescriptor.cs
- DataGridViewImageColumn.cs
- ScriptManager.cs
- DataGridViewComboBoxEditingControl.cs
- FunctionNode.cs
- ExpressionsCollectionEditor.cs
- ActiveDocumentEvent.cs
- DataBindEngine.cs
- TypeExtensionConverter.cs
- IndexExpression.cs
- Positioning.cs
- RIPEMD160.cs
- WindowsStatic.cs
- ApplyTemplatesAction.cs
- Stack.cs
- StrokeNodeOperations2.cs
- EmptyEnumerable.cs
- RegexRunnerFactory.cs
- DataGridViewDataErrorEventArgs.cs
- WorkflowTransactionOptions.cs
- Attributes.cs
- SecureStringHasher.cs
- JournalEntry.cs
- VectorCollectionConverter.cs
- MissingManifestResourceException.cs
- FaultCode.cs
- counter.cs
- PersonalizationEntry.cs
- XmlSchemaType.cs
- ProfileProvider.cs
- WebBrowserBase.cs
- UnionExpr.cs
- DataGridViewCellStyle.cs
- parserscommon.cs
- SubpageParaClient.cs
- _Rfc2616CacheValidators.cs
- ProtocolsConfigurationHandler.cs
- WebPartEditorOkVerb.cs
- AttachInfo.cs
- ComAwareEventInfo.cs
- MetabaseSettingsIis7.cs
- DataColumn.cs
- ModifierKeysValueSerializer.cs
- EFAssociationProvider.cs
- DictionaryBase.cs
- ArraySortHelper.cs
- LocalizationComments.cs
- NameValueConfigurationCollection.cs
- TextWriterTraceListener.cs
- CancellationState.cs
- TextDecorations.cs
- PropertyChangingEventArgs.cs
- WinEventWrap.cs
- Odbc32.cs
- StartUpEventArgs.cs
- _Events.cs
- RowVisual.cs
- Win32SafeHandles.cs
- PageAsyncTask.cs
- WindowsButton.cs
- Hash.cs
- TextAction.cs
- SafeFileHandle.cs
- DisposableCollectionWrapper.cs
- SqlRemoveConstantOrderBy.cs
- Win32SafeHandles.cs
- DesignBindingEditor.cs
- VirtualPathProvider.cs
- UserNameSecurityTokenProvider.cs