internal static ModuleCollectionChange ComputeDifferences(DataModel originalModel, DataModel newModel)
        {
            DataModelModuleCollection originalModules = originalModel.Modules;
            DataModelModuleCollection newModules      = newModel.Modules;

            // All the comparisons are based on data model item Id values. It is thus important
            // to make all Id properties on the data model items internal so that once an item
            // is assigned an Id, that cannot change in its entire life.
            // Assumption: Earlier validations ensure that all the items are present within this
            // model and are not shared between data models. For example, there is no possibility
            // that an association present in this data model has one of its navigation properties
            // in this model but the other navigation property in some other model. Likewise, it
            // should not be possible for two data models to reach the same resource type while
            // enumerating the hosted types. An example source code is shown below. Here, it may
            // be possible to reach 'Resource' resource type from module2 but its Parent property
            // points to module1.
            //var module1 = new ZentityContext().DataModel.Modules[0];
            //var module2 = new ZentityContext().DataModel.Modules[0];
            //module1.ResourceTypes.Add(module2.ResourceTypes["Resource"]);

            ModuleCollectionChange changes = new ModuleCollectionChange();

            CompareDataModelModules(changes, originalModules, newModules);

            CompareResourceTypes(changes, originalModules, newModules);

            CompareScalarProperties(changes, originalModules, newModules);

            CompareNavigationProperties(changes, originalModules, newModules);

            CompareAssociations(changes, originalModules, newModules);

            return(changes);
        }
        private static void CompareAssociations(ModuleCollectionChange changes, DataModelModuleCollection originalModules, DataModelModuleCollection targetModules)
        {
            List <Association> sourceAssociations = originalModules.
                                                    SelectMany(tuple => tuple.Associations).ToList();

            List <Association> targetAssociations = targetModules.
                                                    SelectMany(tuple => tuple.Associations).ToList();

            // Compute added associations.
            List <Guid> addedGuids = new List <Guid>();

            addedGuids.AddRange(targetAssociations.Select(tuple => tuple.Id).
                                Except(sourceAssociations.Select(tuple => tuple.Id)));

            changes.AddedAssociations.AddRange(targetAssociations.
                                               Where(tuple => addedGuids.Contains(tuple.Id)));

            // Compute deleted associations.
            List <Guid> removedGuids = new List <Guid>();

            removedGuids.AddRange(originalModules.SelectMany(tuple => tuple.Associations).
                                  Select(tuple => tuple.Id).
                                  Except
                                      (targetModules.SelectMany(tuple => tuple.Associations).
                                      Select(tuple => tuple.Id)));

            changes.DeletedAssociations.AddRange(originalModules.
                                                 SelectMany(tuple => tuple.Associations).
                                                 Where(tuple => removedGuids.Contains(tuple.Id)));

            // Compute updated associations.
            foreach (Association originalAssociation in
                     originalModules.SelectMany(tuple => tuple.Associations))
            {
                Association newAssociation = targetModules.
                                             SelectMany(tuple => tuple.Associations).
                                             Where(tuple => tuple.Id == originalAssociation.Id).FirstOrDefault();
                if (newAssociation != null)
                {
                    if (originalAssociation.Name != newAssociation.Name ||
                        originalAssociation.ObjectMultiplicity !=
                        newAssociation.ObjectMultiplicity ||
                        originalAssociation.ObjectNavigationProperty.Id !=
                        newAssociation.ObjectNavigationProperty.Id ||
                        originalAssociation.Parent.Id != newAssociation.Parent.Id ||
                        originalAssociation.SubjectMultiplicity !=
                        newAssociation.SubjectMultiplicity ||
                        originalAssociation.SubjectNavigationProperty.Id !=
                        newAssociation.SubjectNavigationProperty.Id ||
                        originalAssociation.Uri != newAssociation.Uri)
                    {
                        changes.UpdatedAssociations.Add(originalAssociation, newAssociation);
                    }
                }
            }
        }
        private static void CompareResourceTypes(ModuleCollectionChange changes, DataModelModuleCollection originalModules, DataModelModuleCollection targetModules)
        {
            List <ResourceType> sourceResourceTypes = originalModules.
                                                      SelectMany(tuple => tuple.ResourceTypes).ToList();

            List <ResourceType> targetResourceTypes = targetModules.
                                                      SelectMany(tuple => tuple.ResourceTypes).ToList();

            // Compute added resource types.
            List <Guid> addedGuids = new List <Guid>();

            addedGuids.AddRange(targetResourceTypes.Select(tuple => tuple.Id).
                                Except(sourceResourceTypes.Select(tuple => tuple.Id)));

            changes.AddedResourceTypes.AddRange(targetResourceTypes.
                                                Where(tuple => addedGuids.Contains(tuple.Id)));

            // Compute deleted resource types.
            List <Guid> removedGuids = new List <Guid>();

            removedGuids.AddRange(originalModules.SelectMany(tuple => tuple.ResourceTypes).
                                  Select(tuple => tuple.Id).
                                  Except
                                      (targetModules.SelectMany(tuple => tuple.ResourceTypes).
                                      Select(tuple => tuple.Id)));

            changes.DeletedResourceTypes.AddRange(originalModules.
                                                  SelectMany(tuple => tuple.ResourceTypes).
                                                  Where(tuple => removedGuids.Contains(tuple.Id)));

            // Compute updated resource types. Parent module of the resource type can change.
            // However, the new parent MUST be within the same data model.
            foreach (ResourceType originalResourceType in
                     originalModules.SelectMany(tuple => tuple.ResourceTypes))
            {
                ResourceType newResourceType = targetModules.
                                               SelectMany(tuple => tuple.ResourceTypes).
                                               Where(tuple => tuple.Id == originalResourceType.Id).FirstOrDefault();
                if (newResourceType != null)
                {
                    if (originalResourceType.BaseType == null && newResourceType.BaseType != null ||
                        originalResourceType.BaseType != null && newResourceType.BaseType == null ||
                        originalResourceType.BaseType != null && newResourceType.BaseType != null &&
                        originalResourceType.BaseType.Id != newResourceType.BaseType.Id ||
                        originalResourceType.Description != newResourceType.Description ||
                        originalResourceType.Name != newResourceType.Name ||
                        originalResourceType.Parent.Id != newResourceType.Parent.Id ||
                        originalResourceType.Uri != newResourceType.Uri)
                    {
                        changes.UpdatedResourceTypes.Add(originalResourceType, newResourceType);
                    }
                }
            }
        }
예제 #4
0
            /// <summary>
            /// Refreshes the specified model.
            /// </summary>
            /// <param name="model">The model name.</param>
            /// <param name="storeConnection">The store connection.</param>
            internal static void Refresh(DataModel model, SqlConnection storeConnection)
            {
                // Save off the modules collection reference.
                DataModelModuleCollection savedModules = model.modules;

                try
                {
                    // Pull custom resource type information from backend.
                    XmlDocument xDataModel = new XmlDocument();

                    if (storeConnection.State == ConnectionState.Closed)
                    {
                        storeConnection.Open();
                    }

                    using (DbCommand cmd = storeConnection.CreateCommand())
                    {
                        cmd.CommandText    = DataModellingResources.Core_GetDataModelModules;
                        cmd.CommandType    = CommandType.StoredProcedure;
                        cmd.CommandTimeout = model.Parent.OperationTimeout;

                        DbParameter param = cmd.CreateParameter();
                        param.DbType        = DbType.String;
                        param.Direction     = ParameterDirection.Output;
                        param.ParameterName = DataModellingResources.DataModelModules;
                        param.Size          = -1;
                        cmd.Parameters.Add(param);
                        cmd.ExecuteNonQuery();

                        xDataModel.LoadXml(param.Value.ToString());
                        // TODO: Validate the xml against a schema.
                    }

                    LoadDataModel(model, xDataModel);
                }
                // TODO: Catch a more specific exception.
                catch
                {
                    // Restore the saved modules collection, if anything goes wrong.
                    model.modules = savedModules;
                    throw;
                }
            }
        private static void CompareDataModelModules(ModuleCollectionChange changes, DataModelModuleCollection originalModules, DataModelModuleCollection targetModules)
        {
            // Compute added modules.
            List <Guid> addedGuids = new List <Guid>();

            addedGuids.AddRange(targetModules.Select(tuple => tuple.Id).
                                Except(originalModules.Select(tuple => tuple.Id)));

            changes.AddedDataModelModules.AddRange(targetModules.
                                                   Where(tuple => addedGuids.Contains(tuple.Id)));

            // Compute removed modules.
            List <Guid> removedGuids = new List <Guid>();

            removedGuids.AddRange(originalModules.Select(tuple => tuple.Id).
                                  Except(targetModules.Select(tuple => tuple.Id)));

            changes.DeletedDataModelModules.AddRange(originalModules.
                                                     Where(tuple => removedGuids.Contains(tuple.Id)));

            // Compute updated modules. We ignore the parent property of the modules.
            foreach (DataModelModule originalModule in originalModules)
            {
                DataModelModule newModule = targetModules.
                                            Where(tuple => tuple.Id == originalModule.Id).FirstOrDefault();

                if (newModule != null)
                {
                    if (originalModule.Description != newModule.Description ||
                        originalModule.NameSpace != newModule.NameSpace ||
                        originalModule.Uri != newModule.Uri)
                    {
                        changes.UpdatedDataModelModules.Add(originalModule, newModule);
                    }
                }
            }
        }
예제 #6
0
            /// <summary>
            /// Loads the data model module collection.
            /// </summary>
            /// <param name="inputModules">The input modules.</param>
            /// <param name="xDataModel">The xml document with data model information.</param>
            private static void LoadDataModelModuleCollection(DataModelModuleCollection inputModules, XmlDocument xDataModel)
            {
                // We load the module collection in three passes.
                // Pass1 - Create all resource types, scalar and navigation properties but do not
                // assign base types to the resource types. This allows us to process the derived
                // types before the base types. Otherwise, we might run into scenarios where we do
                // not have a base type reference while processing a derived type.
                // Pass2 - Assign base types to all resource types.
                // Pass3 - Create associations between various navigation properties.

                // Pass1 - Create all resource types, scalar and navigation properties but do not
                // assign base types to the resource types.
                foreach (XmlNode xModule in xDataModel.SelectNodes(DataModellingResources.XPathDataModelModule))
                {
                    DataModelModule module = new DataModelModule();
                    inputModules.Add(module);
                    LoadDataModelModule(module, xModule);
                }

                // Pass2 - Assign base types to all resource types.
                foreach (XmlNode xResourceType in
                         xDataModel.SelectNodes(DataModellingResources.XPathResourceType))
                {
                    XmlElement eResourceType = xResourceType as XmlElement;
                    Guid       id            = new Guid(eResourceType.Attributes[DataModellingResources.Id].Value);

                    // Assign BaseType.
                    if (eResourceType.HasAttribute(DataModellingResources.BaseTypeId))
                    {
                        Guid baseResourceTypeId =
                            new Guid(eResourceType.Attributes[DataModellingResources.BaseTypeId].Value);

                        ResourceType derivedType = GetResourceTypeById(inputModules, id);
                        derivedType.BaseType = GetResourceTypeById(inputModules, baseResourceTypeId);
                    }
                }

                // Pass3 - Create associations between various navigation properties.
                foreach (XmlNode xAssociation in
                         xDataModel.SelectNodes(DataModellingResources.XPathAssociation))
                {
                    XmlElement  eAssociation = xAssociation as XmlElement;
                    Association association  = new Association();

                    // Assign Id.
                    association.Id = new Guid(eAssociation.Attributes[DataModellingResources.Id].Value);

                    // Assign Name.
                    association.Name = eAssociation.Attributes[DataModellingResources.Name].Value;

                    // Assign Uri.
                    association.Uri = eAssociation.HasAttribute(DataModellingResources.Uri) ?
                                      eAssociation.Attributes[DataModellingResources.Uri].Value : null;

                    // Assign subject navigation property.
                    Guid subjectNavigationPropertyId = new Guid(eAssociation.
                                                                Attributes[DataModellingResources.SubjectNavigationPropertyId].Value);
                    association.SubjectNavigationProperty = GetNavigationPropertyById(inputModules,
                                                                                      subjectNavigationPropertyId);

                    // Assign object navigation property.
                    Guid objectNavigationPropertyId = new Guid(eAssociation.
                                                               Attributes[DataModellingResources.ObjectNavigationPropertyId].Value);
                    association.ObjectNavigationProperty = GetNavigationPropertyById(inputModules,
                                                                                     objectNavigationPropertyId);

                    // Assign predicate id.
                    association.PredicateId = new Guid(eAssociation.
                                                       Attributes[DataModellingResources.PredicateId].Value);

                    // Assign subject multiplicity.
                    string subjectMultiplicity =
                        eAssociation.Attributes[DataModellingResources.SubjectMultiplicity].Value;
                    association.SubjectMultiplicity = (AssociationEndMultiplicity)Enum.
                                                      Parse(typeof(AssociationEndMultiplicity), subjectMultiplicity);

                    // Assign object multiplicity.
                    string objectMultiplicity =
                        eAssociation.Attributes[DataModellingResources.ObjectMultiplicity].Value;
                    association.ObjectMultiplicity = (AssociationEndMultiplicity)Enum.
                                                     Parse(typeof(AssociationEndMultiplicity), objectMultiplicity);

                    // Assign view name.
                    association.ViewName = eAssociation.Attributes[DataModellingResources.ViewName].Value;
                }
            }
        private static void CompareNavigationProperties(ModuleCollectionChange changes, DataModelModuleCollection originalModules, DataModelModuleCollection targetModules)
        {
            List <NavigationProperty> sourceNavigationProperties = originalModules.
                                                                   SelectMany(tuple => tuple.ResourceTypes).
                                                                   SelectMany(tuple => tuple.NavigationProperties).ToList();

            List <NavigationProperty> targetNavigationProperties = targetModules.
                                                                   SelectMany(tuple => tuple.ResourceTypes).
                                                                   SelectMany(tuple => tuple.NavigationProperties).ToList();

            // Compute added navigation properties.
            List <Guid> addedGuids = new List <Guid>();

            addedGuids.AddRange(targetNavigationProperties.Select(tuple => tuple.Id).
                                Except(sourceNavigationProperties.Select(tuple => tuple.Id)));

            changes.AddedNavigationProperties.AddRange(targetNavigationProperties.
                                                       Where(tuple => addedGuids.Contains(tuple.Id)));

            // Compute deleted navigation properties.
            List <Guid> removedGuids = new List <Guid>();

            removedGuids.AddRange(originalModules.SelectMany(tuple => tuple.ResourceTypes).
                                  SelectMany(tuple => tuple.NavigationProperties).Select(tuple => tuple.Id).
                                  Except
                                      (targetModules.SelectMany(tuple => tuple.ResourceTypes).
                                      SelectMany(tuple => tuple.NavigationProperties).Select(tuple => tuple.Id)));

            changes.DeletedNavigationProperties.AddRange(originalModules.
                                                         SelectMany(tuple => tuple.ResourceTypes).
                                                         SelectMany(tuple => tuple.NavigationProperties).
                                                         Where(tuple => removedGuids.Contains(tuple.Id)));

            // Compute updated navigation properties. Parent of the navigation property can change.
            // No need to compare the Association. This will be handled while comparing associations.
            foreach (NavigationProperty property in originalModules.
                     SelectMany(tuple => tuple.ResourceTypes).
                     SelectMany(tuple => tuple.NavigationProperties))
            {
                NavigationProperty newProperty = targetModules.
                                                 SelectMany(tuple => tuple.ResourceTypes).
                                                 SelectMany(tuple => tuple.NavigationProperties).
                                                 Where(tuple => tuple.Id == property.Id).FirstOrDefault();
                if (newProperty != null)
                {
                    if (
                        property.Description != newProperty.Description ||
                        property.Name != newProperty.Name ||
                        property.Parent.Id != newProperty.Parent.Id ||
                        property.Uri != newProperty.Uri
                        )
                    {
                        changes.UpdatedNavigationProperties.Add(property, newProperty);
                    }
                }
            }
        }
        private static void CompareScalarProperties(ModuleCollectionChange changes, DataModelModuleCollection originalModules, DataModelModuleCollection targetModules)
        {
            List <ScalarProperty> sourceScalarProperties = originalModules.
                                                           SelectMany(tuple => tuple.ResourceTypes).
                                                           SelectMany(tuple => tuple.ScalarProperties).ToList();

            List <ScalarProperty> targetScalarProperties = targetModules.
                                                           SelectMany(tuple => tuple.ResourceTypes).
                                                           SelectMany(tuple => tuple.ScalarProperties).ToList();

            // Compute added scalar properties.
            List <Guid> addedGuids = new List <Guid>();

            addedGuids.AddRange(targetScalarProperties.Select(tuple => tuple.Id).
                                Except(sourceScalarProperties.Select(tuple => tuple.Id)));

            changes.AddedScalarProperties.AddRange(targetScalarProperties.
                                                   Where(tuple => addedGuids.Contains(tuple.Id)));

            // Compute deleted scalar properties.
            List <Guid> removedGuids = new List <Guid>();

            removedGuids.AddRange(originalModules.SelectMany(tuple => tuple.ResourceTypes).
                                  SelectMany(tuple => tuple.ScalarProperties).Select(tuple => tuple.Id).
                                  Except
                                      (targetModules.SelectMany(tuple => tuple.ResourceTypes).
                                      SelectMany(tuple => tuple.ScalarProperties).Select(tuple => tuple.Id)));

            changes.DeletedScalarProperties.AddRange(originalModules.
                                                     SelectMany(tuple => tuple.ResourceTypes).
                                                     SelectMany(tuple => tuple.ScalarProperties).
                                                     Where(tuple => removedGuids.Contains(tuple.Id)));

            // Compute updated scalar properties. Parent for the scalar property can change.
            foreach (ScalarProperty property in originalModules.
                     SelectMany(tuple => tuple.ResourceTypes).
                     SelectMany(tuple => tuple.ScalarProperties))
            {
                ScalarProperty newProperty = targetModules.
                                             SelectMany(tuple => tuple.ResourceTypes).
                                             SelectMany(tuple => tuple.ScalarProperties).
                                             Where(tuple => tuple.Id == property.Id).FirstOrDefault();
                if (newProperty != null)
                {
                    if (
                        property.DataType != newProperty.DataType ||
                        property.Description != newProperty.Description ||
                        property.MaxLength != newProperty.MaxLength ||
                        property.Name != newProperty.Name ||
                        property.Nullable != newProperty.Nullable ||
                        property.Parent.Id != newProperty.Parent.Id ||
                        property.Precision != newProperty.Precision ||
                        property.Scale != newProperty.Scale ||
                        property.Uri != newProperty.Uri
                        )
                    {
                        changes.UpdatedScalarProperties.Add(property, newProperty);
                    }
                }
            }
        }