Пример #1
0
        /// <summary>
        ///     Imports the entity
        /// </summary>
        /// <param name="tenantId"></param>
        /// <param name="importSource"></param>
        /// <param name="settings"></param>
        /// <param name="context"></param>
        internal IEnumerable <Guid> ImportEntity(long tenantId, IDataSource importSource, EntityXmlImportSettings settings, IProcessingContext context)
        {
            IList <Guid> rootGuids = GetRootGuidsFromMetadata(importSource, context);

            using (IDataSource baseline = GetBaselineSourceForImport(tenantId, rootGuids))
                using (TenantMergeTarget target = new TenantMergeTarget
                {
                    TenantId = tenantId,
                    IgnoreExternalReferences = true
                })
                {
                    /////
                    // Copy the data
                    /////
                    using (var processor = new MergeProcessor(context)
                    {
                        OldVersion = baseline,
                        NewVersion = importSource,
                        Target = target
                    })
                    {
                        processor.MergeData( );

                        CheckForMissingDependencies(context, settings);

                        target.Commit( );
                    }
                }

            CacheManager.ClearCaches(tenantId);

            return(rootGuids);
        }
Пример #2
0
        /// <summary>
        ///     Imports the tenant.
        /// </summary>
        /// <returns>the tenant id</returns>
        public static void InstallGlobalTenant( )
        {
            IProcessingContext context = new ProcessingContext( );

            context.Report.Action = AppLibraryAction.InstallGlobal;
            context.Report.Arguments.Add(new KeyValuePair <string, string>("Tenant Id", "Global"));

            IDictionary <Guid, Guid> appToAppVer = new Dictionary <Guid, Guid>( );

            // Get application versions
            // It is assumed that if there is no global tenant, then there is only one version of each of the core apps
            using (IDatabaseContext dbContext = DatabaseContext.GetContext(false))
                using (IDbCommand command = dbContext.CreateCommand())
                {
                    command.CommandText = "select FromUid AppUid, AppVerUid from AppRelationship where ToUid = @solution and TypeUid = @isOfType";
                    command.AddParameterWithValue("@solution", Guids.Solution);
                    command.AddParameterWithValue("@isOfType", Guids.IsOfType);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        // Load appVerId for apps from app library
                        while (reader.Read( ))
                        {
                            Guid appId    = reader.GetGuid(0);
                            Guid appVerId = reader.GetGuid(1);
                            appToAppVer[appId] = appVerId;
                        }
                    }
                }

            // Install apps
            Guid[] coreApps = new[]
            {
                Guids.CoreSolution,
                Guids.ConsoleSolution,
                Guids.CoreDataSolution,
                Guids.SystemSolution
            };

            // Copy system application content from the app library into the global tenant
            foreach (Guid appId in coreApps)
            {
                // Get the AppVerId
                Guid appVerId;
                if (!appToAppVer.TryGetValue(appId, out appVerId))
                {
                    throw new Exception($"Aborting: System app {0} was not present in app library.");
                }

                context.WriteInfo($"Installing app {appId} package {appVerId} to global tenant.");

                IDataSource empty  = new EmptySource( );
                IDataSource source = new LibraryAppSource
                {
                    AppId    = appId,
                    AppVerId = appVerId,
                    AppName  = appId.ToString( )
                };
                TenantMergeTarget target = new TenantMergeTarget
                {
                    TenantId = 0
                };

                using (empty)
                    using (source)
                        using ( target )
                        {
                            MergeProcessor processor = new MergeProcessor(context)
                            {
                                OldVersion = empty,
                                NewVersion = source,
                                Target     = target
                            };
                            processor.MergeData( );

                            target.Commit( );
                        }
            }


            // Copy metadata from the tenant back into the application library
            foreach (Guid appId in coreApps)
            {
                Guid appVerId = appToAppVer[appId];

                long        solutionEntityId = Entity.GetIdFromUpgradeId(appId);
                IDataSource metadataSource   = new TenantAppSource
                {
                    SolutionId = solutionEntityId,
                    TenantId   = 0
                };
                LibraryAppTarget metadataTarget = new LibraryAppTarget
                {
                    ApplicationVersionId = appVerId
                };
                using (metadataSource)
                    using ( metadataTarget )
                    {
                        CopyProcessor processor = new CopyProcessor(metadataSource, metadataTarget, context);
                        processor.CopyMetadataOnly = true;
                        processor.MigrateData( );

                        metadataTarget.Commit( );
                    }

                context.WriteInfo($"Installed app {appId} to global tenant.");
            }
        }
Пример #3
0
        /// <summary>
        ///     Merges the data.
        /// </summary>
        public void MergeData( )
        {
            IProcessingContext context = Context;

            OldVersion.Setup(context);
            NewVersion.Setup(context);
            Target.Setup(context);

            context.WriteInfo("Loading package...");
            _oldApp = AppContents.Load(OldVersion, context);

            context.WriteInfo("Loading tenant data...");
            _newApp = AppContents.Load(NewVersion, context);

            context.WriteInfo("Processing entities...");

            // Entities that the application explicitly does not want to delete
            ISet <Guid> doNotRemove = _newApp.DoNotRemove;

            /////
            // Detect entity changes
            /////
            List <EntityEntry> addedEntities,
                               removedEntities,
                               changedEntities,
                               unchangedEntities;
            HashSet <Guid> removedEntitiesSet;

            long tenantId = -1;

            TenantMergeTarget tenantMergeTarget = Target as TenantMergeTarget;

            if (tenantMergeTarget != null)
            {
                tenantId = tenantMergeTarget.TenantId;
            }

            Guid isTenantDisabled = new Guid("11209a07-9189-4099-b609-80ed1d4f3e56");

            Func <EntityEntry, bool> removeEntityAction = e =>
            {
                if (tenantId == 0 && e.EntityId == isTenantDisabled)
                {
                    EventLog.Application.WriteWarning("Attempt to delete the entity 'isTenantDisabled' from the global tenant has been prevented.");

                    return(false);
                }

                return(true);
            };

            Diff.DetectChanges(_oldApp.Entities, _newApp.Entities, null, null, removeEntityAction, null, null, out addedEntities, out removedEntities, out changedEntities, out unchangedEntities);

            // Suppress removal of entites that are flagged as 'do not remove'
            // removedEntities contains list of entities that we will actually remove
            removedEntities.RemoveAll(entityEntry => doNotRemove.Contains(entityEntry.EntityId));

            // Capture raw set of entities that are being removed (excluding the doNotRemove ones)
            removedEntitiesSet = new HashSet <Guid>(removedEntities.Select(entityEntry => entityEntry.EntityId));

            Context.Report.AddedEntities   = addedEntities;
            Context.Report.RemovedEntities = removedEntities;
            Context.Report.UpdatedEntities = changedEntities;

            if (_oldApp.Entities != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Previous Application Entities", _oldApp.Entities.Count, StatisticsCountType.PreviousApplication));
            }

            if (_newApp.Entities != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Current Application Entities", _newApp.Entities.Count, StatisticsCountType.CurrentApplication));
            }

            Context.Report.Counts.Add(new StatisticsCount("Added Entities", addedEntities.Count, StatisticsCountType.Added));
            Context.Report.Counts.Add(new StatisticsCount("Removed Entities", removedEntities.Count, StatisticsCountType.Removed));
            Context.Report.Counts.Add(new StatisticsCount("Updated Entities", changedEntities.Count, StatisticsCountType.Updated));
            Context.Report.Counts.Add(new StatisticsCount("Unchanged Entities", unchangedEntities.Count, StatisticsCountType.Unchanged));

            /////
            // Apply entity changes
            // note: 'changedEntities' is always empty
            /////
            Target.WriteEntities(addedEntities, context);

            context.WriteInfo("Processing relationships...");

            /////
            // Detect relationship changes
            /////
            List <RelationshipEntry> addedRelationships,
                                     removedRelationships,
                                     changedRelationships,
                                     unchangedRelationships;

            var changeAction = new Func <RelationshipEntry, RelationshipEntry, bool>((o, n) =>
            {
                if (n.Cardinality == CardinalityEnum_Enumeration.ManyToOne)
                {
                    n.PreviousValue = o.ToId;
                    return(true);
                }

                if (n.Cardinality == CardinalityEnum_Enumeration.OneToMany)
                {
                    n.PreviousValue = o.FromId;
                    return(true);
                }

                if (n.Cardinality == CardinalityEnum_Enumeration.OneToOne)
                {
                    if (o.FromId == n.FromId)
                    {
                        n.PreviousValue = o.ToId;
                        n.UpdateTo      = true;
                        n.UpdateFrom    = false;
                    }
                    else
                    {
                        n.PreviousValue = o.FromId;
                        n.UpdateTo      = false;
                        n.UpdateFrom    = true;
                    }
                }

                return(true);
            });

            Func <RelationshipEntry, bool> removeRelationshipAction = e =>
            {
                if (tenantId == 0)
                {
                    if (e.FromId == isTenantDisabled)
                    {
                        EventLog.Application.WriteWarning("Attempt to delete relatiosnhip from 'isTenantDisabled' in the global tenant has been prevented.");

                        return(false);
                    }

                    if (e.ToId == isTenantDisabled)
                    {
                        EventLog.Application.WriteWarning("Attempt to delete relationship to 'isTenantDisabled' in the global tenant has been prevented.");

                        return(false);
                    }
                }

                return(true);
            };

            Diff.DetectChanges(_oldApp.Relationships, _newApp.Relationships, _oldApp.MissingRelationships, null, removeRelationshipAction, changeAction, null, out addedRelationships, out removedRelationships, out changedRelationships, out unchangedRelationships);

            // Don't remove relationship content for entities that are flagged as 'do not delete' (unless the other end is being deleted)
            removedRelationships.RemoveAll(relationshipEntry =>
            {
                if (!(doNotRemove.Contains(relationshipEntry.ToId) || doNotRemove.Contains(relationshipEntry.FromId)))
                {
                    return(false);  // neither end is in the 'do not remove' list, so just carry on with the deletion (by leaving the row in the removal collection)
                }
                // Always allow apps to remove their association to an entity
                if (relationshipEntry.TypeId == Guids.InSolution || relationshipEntry.TypeId == Guids.IndirectInSolution)
                {
                    return(false);
                }

                // If the other end of the relationship (or the relationship type) is being deleted, then delete the relationship instance anyway
                if (removedEntitiesSet.Contains(relationshipEntry.ToId))
                {
                    return(false);
                }
                if (removedEntitiesSet.Contains(relationshipEntry.FromId))
                {
                    return(false);
                }
                if (removedEntitiesSet.Contains(relationshipEntry.TypeId))
                {
                    return(false);
                }

                return(true);   // the relationship is partly in the do-not-remove list, and the other end is not being removed, so don't delete this relationship
            });


            Context.Report.AddedRelationships   = addedRelationships;
            Context.Report.RemovedRelationships = removedRelationships;
            Context.Report.UpdatedRelationships = changedRelationships;

            if (_oldApp.Relationships != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Previous Application Relationships", _oldApp.Relationships.Count, StatisticsCountType.PreviousApplication));
            }

            if (_newApp.Relationships != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Current Application Relationships", _newApp.Relationships.Count, StatisticsCountType.CurrentApplication));
            }

            Context.Report.Counts.Add(new StatisticsCount("Added Relationships", addedRelationships.Count, StatisticsCountType.Added));
            Context.Report.Counts.Add(new StatisticsCount("Removed Relationships", removedRelationships.Count, StatisticsCountType.Removed));
            Context.Report.Counts.Add(new StatisticsCount("Updated Relationships", changedRelationships.Count, StatisticsCountType.Updated));
            Context.Report.Counts.Add(new StatisticsCount("Unchanged Relationships", unchangedRelationships.Count, StatisticsCountType.Unchanged));

            /////
            // Apply relationship changes
            /////
            Target.UpdateRelationships(changedRelationships, context);
            Target.WriteRelationships(addedRelationships, context);

            Target.PreDeleteEntities(removedEntities, context);

            Target.DeleteRelationships(removedRelationships, context);
            Target.DeleteEntities(removedEntities, context);

            /////
            // Invalidate all per-tenant caches at this point.
            // This is to ensure caches that are holding onto type information are flushed.
            /////
            InvalidatePerTenantCaches(tenantId);

            Func <DataEntry, bool> removedAction = de =>
            {
                if (de != null)
                {
                    if (tenantId == 0 && de.EntityId == isTenantDisabled)
                    {
                        EventLog.Application.WriteWarning("Attempt to delete field on entity 'isTenantDisabled' in the global tenant has been prevented.");

                        return(false);
                    }
                }

                return(true);
            };

            Func <DataEntry, DataEntry, bool> changedAction = (oldVal, newVal) =>
            {
                newVal.ExistingData = oldVal.Data;
                return(true);
            };

            /////
            // Detect and apply data changes
            /////
            foreach (string dataTable in Helpers.FieldDataTables)
            {
                context.WriteInfo($"Processing '{dataTable}'...");

                List <DataEntry> addedData,
                                 removedData,
                                 changedData,
                                 unchangedData;
                Dictionary <Tuple <Guid, Guid>, DataEntry> oldData     = _oldApp.FieldData[dataTable];
                Dictionary <Tuple <Guid, Guid>, DataEntry> missingData = _oldApp.MissingFieldData;
                Dictionary <Tuple <Guid, Guid>, DataEntry> newData     = _newApp.FieldData[dataTable];

                Diff.DetectChanges(oldData, newData, missingData, null, removedAction, changedAction, null, out addedData, out removedData, out changedData, out unchangedData);

                // Don't remove content for entities that are tagged as 'do not delete'
                removedData.RemoveAll(dataEntry => _newApp.DoNotRemove.Contains(dataEntry.EntityId));

                context.Report.AddedEntityData[dataTable]   = addedData;
                context.Report.RemovedEntityData[dataTable] = removedData;
                context.Report.UpdatedEntityData[dataTable] = changedData;

                if (oldData != null)
                {
                    Context.Report.Counts.Add(new StatisticsCount($"Previous Application {dataTable} Data", oldData.Count, StatisticsCountType.PreviousApplication));
                }

                if (newData != null)
                {
                    Context.Report.Counts.Add(new StatisticsCount($"Current Application {dataTable} Data", newData.Count, StatisticsCountType.CurrentApplication));
                }

                Context.Report.Counts.Add(new StatisticsCount($"Added {dataTable} Data", addedData.Count, StatisticsCountType.Added));
                Context.Report.Counts.Add(new StatisticsCount($"Removed {dataTable} Data", removedData.Count, StatisticsCountType.Removed));
                Context.Report.Counts.Add(new StatisticsCount($"Updated {dataTable} Data", changedData.Count, StatisticsCountType.Updated));
                Context.Report.Counts.Add(new StatisticsCount($"Unchanged {dataTable} Data", unchangedData.Count, StatisticsCountType.Unchanged));

                Target.WriteFieldData(dataTable, addedData, context);
                Target.DeleteFieldData(dataTable, removedData, context);
                Target.UpdateFieldData(dataTable, changedData, context);
            }

            context.WriteInfo("Processing binary data...");

            /////
            // Detect binary data changes
            /////
            List <BinaryDataEntry> addedbinaryData,
                                   removedBinaryData,
                                   changedBinaryData,
                                   unchangedBinaryData;

            Diff.DetectChanges(_oldApp.BinaryData, _newApp.BinaryData, null, null, null, null, null, out addedbinaryData, out removedBinaryData, out changedBinaryData, out unchangedBinaryData);

            context.Report.AddedBinaryData   = addedbinaryData;
            context.Report.RemovedBinaryData = removedBinaryData;
            context.Report.UpdatedBinaryData = changedBinaryData;

            if (_oldApp.BinaryData != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Previous Application Binary Data", _oldApp.BinaryData.Count, StatisticsCountType.PreviousApplication));
            }

            if (_newApp.BinaryData != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Current Application Binary Data", _newApp.BinaryData.Count, StatisticsCountType.CurrentApplication));
            }

            Context.Report.Counts.Add(new StatisticsCount("Added Binary Data", addedbinaryData.Count, StatisticsCountType.Added));
            Context.Report.Counts.Add(new StatisticsCount("Removed Binary Data", removedBinaryData.Count, StatisticsCountType.Removed));
            Context.Report.Counts.Add(new StatisticsCount("Updated Binary Data", changedBinaryData.Count, StatisticsCountType.Updated));
            Context.Report.Counts.Add(new StatisticsCount("Unchanged Binary Data", unchangedBinaryData.Count, StatisticsCountType.Unchanged));

            /////
            // Apply binary changes
            /////
            Target.WriteBinaryData(addedbinaryData, context);
            // Update before delete. This is to ensure that if any files have the IsReferencedExternally flag set
            // that they will not be deleted.
            Target.UpdateBinaryData(changedBinaryData, context);
            Target.DeleteBinaryData(removedBinaryData, context);

            context.WriteInfo("Processing document data...");

            /////
            // Detect binary data changes
            /////
            List <DocumentDataEntry> addedDocumentData,
                                     removedDocumentData,
                                     changedDocumentData,
                                     unchangedDocumentData;

            Diff.DetectChanges(_oldApp.DocumentData, _newApp.DocumentData, null, null, null, null, null, out addedDocumentData, out removedDocumentData, out changedDocumentData, out unchangedDocumentData);

            context.Report.AddedDocumentData   = addedDocumentData;
            context.Report.RemovedDocumentData = removedDocumentData;
            context.Report.UpdatedDocumentData = changedDocumentData;

            if (_oldApp.DocumentData != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Previous Application Document Data", _oldApp.DocumentData.Count, StatisticsCountType.PreviousApplication));
            }

            if (_newApp.DocumentData != null)
            {
                Context.Report.Counts.Add(new StatisticsCount("Current Application Document Data", _newApp.DocumentData.Count, StatisticsCountType.CurrentApplication));
            }

            Context.Report.Counts.Add(new StatisticsCount("Added Document Data", addedDocumentData.Count, StatisticsCountType.Added));
            Context.Report.Counts.Add(new StatisticsCount("Removed Document Data", removedDocumentData.Count, StatisticsCountType.Removed));
            Context.Report.Counts.Add(new StatisticsCount("Updated Document Data", changedDocumentData.Count, StatisticsCountType.Updated));
            Context.Report.Counts.Add(new StatisticsCount("Unchanged Document Data", unchangedDocumentData.Count, StatisticsCountType.Unchanged));

            /////
            // Apply binary changes
            /////
            Target.WriteDocumentData(addedDocumentData, context);
            Target.UpdateDocumentData(changedDocumentData, context);
            Target.DeleteDocumentData(removedDocumentData, context);

            Target.TearDown(context);
            NewVersion.TearDown(context);
            OldVersion.TearDown(context);
        }