示例#1
0
 /// <summary>
 /// Updates the document data.
 /// </summary>
 /// <param name="documentData">The document data.</param>
 /// <param name="context">The context.</param>
 public void UpdateDocumentData(IEnumerable <DocumentDataEntry> documentData, IProcessingContext context)
 {
     foreach (DocumentDataEntry documentDataEntry in documentData)
     {
         context.WriteInfo(string.Format("Updated binary data: {0}", documentDataEntry.DataHash));
     }
 }
示例#2
0
        /// <summary>
        ///     Setups the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        void IDataTarget.Setup(IProcessingContext context)
        {
            context?.WriteInfo("Initializing...");

            long userId;

            RequestContext.TryGetUserId(out userId);

            using (EntryPointContext.UnsafeToIncludeEntryPoint( ))
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = CommandText.TenantRepairTargetSetupCommandText;
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery( );

                    command.CommandText = @"
IF ( @context IS NOT NULL )
BEGIN
	DECLARE @contextInfo VARBINARY(128) = CONVERT( VARBINARY(128), @context )
	SET CONTEXT_INFO @contextInfo
END";
                    command.AddParameter("@context", DbType.AnsiString, DatabaseContextInfo.GetMessageChain(userId), 128);
                    command.ExecuteNonQuery( );
                }
        }
示例#3
0
 /// <summary>
 ///     Deletes field data.
 /// </summary>
 /// <param name="dataTable"></param>
 /// <param name="data"></param>
 /// <param name="context"></param>
 void IMergeTarget.DeleteFieldData(string dataTable, IEnumerable <DataEntry> data, IProcessingContext context)
 {
     foreach (DataEntry entry in data)
     {
         context.WriteInfo(string.Format("Deleted data: {0} {1}", entry.EntityId, entry.FieldId));
     }
 }
示例#4
0
        /// <summary>
        ///     Migrates the data.
        /// </summary>
        public void MigrateData( )
        {
            IProcessingContext context = Context;

            /////
            // Run any setup logic.
            /////
            DataSource.Setup(context);
            DataTarget.Setup(context);

            /////
            // Migrate metadata
            /////
            context.WriteInfo("Copying metadata...");
            Metadata metadata = DataSource.GetMetadata(context);

            DataTarget.SetMetadata(metadata, context);

            if (!CopyMetadataOnly)
            {
                MigrateContent(context);
            }

            /////
            // Run any teardown logic.
            /////
            DataTarget.TearDown(context);
            DataSource.TearDown(context);
        }
示例#5
0
 /// <summary>
 ///     Deletes relationships.
 /// </summary>
 /// <param name="relationships"></param>
 /// <param name="context"></param>
 void IMergeTarget.DeleteRelationships(IEnumerable <RelationshipEntry> relationships, IProcessingContext context)
 {
     foreach (RelationshipEntry rel in relationships)
     {
         context.WriteInfo(string.Format("Deleted relationship: {0} {1} {2}", rel.TypeId, rel.FromId, rel.ToId));
     }
 }
示例#6
0
 /// <summary>
 /// Deletes the document data.
 /// </summary>
 /// <param name="documentData">The document data.</param>
 /// <param name="context">The context.</param>
 public void DeleteDocumentData(IEnumerable <DocumentDataEntry> documentData, IProcessingContext context)
 {
     foreach (DocumentDataEntry documentDataEntry in documentData)
     {
         context.WriteInfo("Deleted document data: " + documentDataEntry.DataHash);
     }
 }
示例#7
0
 /// <summary>
 ///     Deletes entities.
 /// </summary>
 /// <param name="entities"></param>
 /// <param name="context"></param>
 void IMergeTarget.DeleteEntities(IEnumerable <EntityEntry> entities, IProcessingContext context)
 {
     foreach (EntityEntry entity in entities)
     {
         context.WriteInfo("Deleted entity: " + entity.EntityId);
     }
 }
示例#8
0
 /// <summary>
 ///     Write in collection of relationships.
 /// </summary>
 /// <param name="relationships"></param>
 /// <param name="context"></param>
 void IDataTarget.WriteSecureData(IEnumerable <SecureDataEntry> data, IProcessingContext context)
 {
     foreach (var entry in data)
     {
         context.WriteInfo($"Added secure entry: {entry.SecureId} {entry.Context} Length:{entry.Data.Length}");
     }
 }
示例#9
0
 /// <summary>
 ///     Write list of entities that should not be removed during upgrade operations.
 /// </summary>
 /// <param name="entities"></param>
 /// <param name="context"></param>
 void IDataTarget.WriteDoNotRemove(IEnumerable <Guid> entities, IProcessingContext context)
 {
     foreach (Guid entity in entities)
     {
         context.WriteInfo("Do not remove: " + entity);
     }
 }
示例#10
0
 public void WriteDocumentData(IEnumerable <DocumentDataEntry> data, IProcessingContext context)
 {
     foreach (DocumentDataEntry dataEntry in data)
     {
         context.WriteInfo(string.Format("Added Document DataHash:{0}", dataEntry.DataHash));
     }
 }
示例#11
0
 /// <summary>
 ///     Deletes binary data entries.
 /// </summary>
 /// <param name="binaryData"></param>
 /// <param name="context"></param>
 void IMergeTarget.DeleteBinaryData(IEnumerable <BinaryDataEntry> binaryData, IProcessingContext context)
 {
     foreach (BinaryDataEntry binaryDataEntry in binaryData)
     {
         context.WriteInfo("Deleted binary data: " + binaryDataEntry.DataHash);
     }
 }
示例#12
0
 /// <summary>
 ///     Writes the binary data.
 /// </summary>
 /// <param name="data">The data.</param>
 /// <param name="context">The context.</param>
 void IDataTarget.WriteBinaryData(IEnumerable <BinaryDataEntry> data, IProcessingContext context)
 {
     foreach (BinaryDataEntry dataEntry in data)
     {
         context.WriteInfo(string.Format("Added Binary DataHash:{0}", dataEntry.DataHash));
     }
 }
示例#13
0
        /// <summary>
        ///     Write in collection of relationships.
        /// </summary>
        void IDataTarget.WriteRelationships(IEnumerable <RelationshipEntry> relationships, IProcessingContext context)
        {
            using (IDbCommand command = CreateCommand( ))
            {
                const string sql = "insert into _Relationship (FromUid, ToUid, TypeUid, EntityUid) values (@from, @to, @type, @entity)";
                command.CommandText = sql;
                command.Parameters.Add(new SQLiteParameter("@from", DbType.String));
                command.Parameters.Add(new SQLiteParameter("@to", DbType.String));
                command.Parameters.Add(new SQLiteParameter("@type", DbType.String));
                command.Parameters.Add(new SQLiteParameter("@entity", DbType.String));

                int rowCount = 0;

                foreach (RelationshipEntry relationship in relationships)
                {
                    (( SQLiteParameter )command.Parameters[0]).Value = relationship.FromId;
                    (( SQLiteParameter )command.Parameters[1]).Value = relationship.ToId;
                    (( SQLiteParameter )command.Parameters[2]).Value = relationship.TypeId;

                    rowCount += command.ExecuteNonQuery( );

                    if (rowCount % 100 == 0)
                    {
                        context.WriteInfo($"Copying Relationship data... {rowCount} rows");
                    }
                }
            }
        }
示例#14
0
 /// <summary>
 ///     Called for binary data with new values.
 /// </summary>
 /// <param name="data"></param>
 /// <param name="context"></param>
 void IMergeTarget.UpdateBinaryData(IEnumerable <BinaryDataEntry> data, IProcessingContext context)
 {
     foreach (BinaryDataEntry entry in data)
     {
         context.WriteInfo(string.Format("Updated binary data: {0}", entry.DataHash));
     }
 }
示例#15
0
 /// <summary>
 ///     Set the application metadata.
 /// </summary>
 /// <param name="metadata"></param>
 /// <param name="context"></param>
 void IDataTarget.SetMetadata(Metadata metadata, IProcessingContext context)
 {
     context.WriteInfo("Name: " + metadata.Name);
     context.WriteInfo("Description: " + metadata.Description);
     context.WriteInfo("Version: " + metadata.Version);
     context.WriteInfo("Publisher: " + metadata.Publisher);
     context.WriteInfo("PublisherUrl: " + metadata.PublisherUrl);
     context.WriteInfo("ReleaseDate: " + metadata.ReleaseDate);
     context.WriteInfo("AppId: " + metadata.AppId);
     context.WriteInfo("AppVerId: " + metadata.AppVerId);
 }
示例#16
0
        /// <summary>
        ///     Tears down.
        /// </summary>
        /// <param name="context">The context.</param>
        void IDataTarget.TearDown(IProcessingContext context)
        {
            context?.WriteInfo("Finalizing...");

            using (EntryPointContext.UnsafeToIncludeEntryPoint( ))
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = CommandText.TenantRepairTargetTearDownCommandText;
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery( );
                }
        }
示例#17
0
        /// <summary>
        ///     Setups the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        void IDataTarget.Setup(IProcessingContext context)
        {
            if (context != null)
            {
                context.WriteInfo("Initializing...");
            }

            using (EntryPointContext.UnsafeToIncludeEntryPoint( ))
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = CommandText.TenantMergeTargetSetupCommandText;
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery( );
                }
        }
示例#18
0
        /// <summary>
        ///     Setups the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        void IDataTarget.Setup(IProcessingContext context)
        {
            if (context != null)
            {
                context.WriteInfo("Initializing...");
            }

            using (EntryPointContext.UnsafeToIncludeEntryPoint( ))
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = "CREATE TABLE #Binary ( OldDataHash NVARCHAR(MAX) COLLATE Latin1_General_CI_AI NULL, NewDataHash NVARCHAR(MAX) COLLATE Latin1_General_CI_AI NULL, FileExtension NVARCHAR(20) COLLATE Latin1_General_CI_AI NULL )";
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery();
                    command.CommandText = "CREATE TABLE #Document ( OldDataHash NVARCHAR(MAX) COLLATE Latin1_General_CI_AI NULL, NewDataHash NVARCHAR(MAX) COLLATE Latin1_General_CI_AI NULL, FileExtension NVARCHAR(20) COLLATE Latin1_General_CI_AI NULL )";
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery();
                }
        }
示例#19
0
        /// <summary>
        ///     Tears down.
        /// </summary>
        /// <param name="context">The context.</param>
        void IDataTarget.TearDown(IProcessingContext context)
        {
            if (context != null)
            {
                context.WriteInfo("Finalizing...");
            }

            using (EntryPointContext.UnsafeToIncludeEntryPoint( ))
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = "DROP TABLE #Binary";
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery( );
                    command.CommandText = "DROP TABLE #Document";
                    command.CommandType = CommandType.Text;
                    command.ExecuteNonQuery( );
                }
        }
示例#20
0
        /// <summary>
        ///     Writes the binary data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="context">The context.</param>
        void IDataTarget.WriteBinaryData(IEnumerable <BinaryDataEntry> data, IProcessingContext context)
        {
            int rowCount = 0;

            using (var command = CreateCommand( ) as SQLiteCommand)
            {
                Debug.Assert(command != null, "command != null");

                const string sql = "insert into _Filestream_Binary (FileExtension, DataHash, Data) values (@fileExtension, @dataHash, @data)";

                command.CommandType = CommandType.Text;
                command.CommandText = sql;

                foreach (BinaryDataEntry dataEntry in data)
                {
                    if (dataEntry.Data == null)
                    {
                        context.WriteWarning($"Unexpected null values when updating binary table. DataHash:{dataEntry.DataHash}");
                        continue;
                    }

                    if (!IsBinaryDataValid(dataEntry.Data, dataEntry.DataHash))
                    {
                        context.WriteWarning($"The binary data is corrupt. It will be skipped. The data hash does not match the expected value. DataHash:{dataEntry.DataHash}");
                        continue;
                    }

                    command.Parameters.Clear( );

                    command.Parameters.Add("@fileExtension", DbType.String).Value = dataEntry.FileExtension;
                    command.Parameters.Add("@dataHash", DbType.String).Value      = dataEntry.DataHash;

                    byte[] compressedData = CompressionHelper.Compress(dataEntry.Data);
                    command.Parameters.Add("@data", DbType.Binary, compressedData.Length).Value = compressedData;

                    rowCount += command.ExecuteNonQuery( );

                    if (rowCount % 100 == 0)
                    {
                        context.WriteInfo($"Copying binary data... {rowCount} rows");
                    }
                }
            }
        }
示例#21
0
        /// <summary>
        ///     Write in collection of field data.
        /// </summary>
        void IDataTarget.WriteFieldData(string dataTable, IEnumerable <DataEntry> data, IProcessingContext context)
        {
            bool isAliasTable = dataTable == "Alias";

            // Converter for 'Bit' data to convert true/false to 1/0
            Func <object, object> converter = GetDataConverter(dataTable);

            using (IDbCommand command = CreateCommand( ))
            {
                string sql = "insert into _Data_" + dataTable + " (EntityUid, FieldUid, Data) values (@entity, @field, @data)";

                command.Parameters.Add(new SQLiteParameter("@entity", DbType.String));
                command.Parameters.Add(new SQLiteParameter("@field", DbType.String));
                command.Parameters.Add(new SQLiteParameter("@data", DbType.String));

                if (isAliasTable)
                {
                    sql = "insert into _Data_" + dataTable + " (EntityUid, FieldUid, Data, [Namespace], AliasMarkerId) values (@entity, @field, @data, @namespace, @marker)";
                    command.Parameters.Add(new SQLiteParameter("@namespace", DbType.String));
                    command.Parameters.Add(new SQLiteParameter("@marker", DbType.Int32));
                }
                command.CommandText = sql;

                int rowCount = 0;

                foreach (DataEntry dataEntry in data)
                {
                    (( SQLiteParameter )command.Parameters[0]).Value = dataEntry.EntityId;
                    (( SQLiteParameter )command.Parameters[1]).Value = dataEntry.FieldId;
                    (( SQLiteParameter )command.Parameters[2]).Value = converter(dataEntry.Data);
                    if (isAliasTable)
                    {
                        (( SQLiteParameter )command.Parameters[3]).Value = dataEntry.Namespace;
                        (( SQLiteParameter )command.Parameters[4]).Value = dataEntry.AliasMarkerId;
                    }
                    rowCount += command.ExecuteNonQuery( );

                    if (rowCount % 100 == 0)
                    {
                        context.WriteInfo($"Copying '{dataTable}' Field data... {rowCount} rows");
                    }
                }
            }
        }
示例#22
0
        void IDataTarget.WriteSecureData(IEnumerable <SecureDataEntry> data, IProcessingContext context)
        {
            int rowCount = 0;

            using (var command = CreateCommand() as SQLiteCommand)
            {
                Debug.Assert(command != null, "command != null");

                const string sql = "insert into _SecureData (SecureId, Context, Data) values (@secureId, @context, @data)";

                command.CommandType = CommandType.Text;
                command.CommandText = sql;

                foreach (SecureDataEntry dataEntry in data)
                {
                    if (dataEntry.Data == null)
                    {
                        context.WriteWarning($"Unexpected null values when updating SecureData table. SecuredId:{dataEntry.SecureId}");
                        continue;
                    }

                    command.Parameters.Clear();

                    command.Parameters.Add("@secureId", DbType.String).Value    = dataEntry.SecureId;
                    command.Parameters.Add("@context", DbType.AnsiString).Value = dataEntry.Context;

                    var encodedData = Convert.ToBase64String(dataEntry.Data);    // encrypted so no point in compressing

                    command.Parameters.Add("@data", DbType.String).Value = encodedData;

                    rowCount += command.ExecuteNonQuery();

                    if (rowCount % 100 == 0)
                    {
                        context.WriteInfo($"Copying SecureData... {rowCount} rows");
                    }
                }
            }
        }
示例#23
0
        /// <summary>
        ///     Write in collection of entities.
        /// </summary>
        void IDataTarget.WriteEntities(IEnumerable <EntityEntry> entities, IProcessingContext context)
        {
            using (IDbCommand command = CreateCommand( ))
            {
                const string sql = "insert into _Entity (Uid) values (@entityUid)";
                command.CommandText = sql;
                command.Parameters.Add(new SQLiteParameter("@entityUid", DbType.String));

                int rowCount = 0;

                foreach (EntityEntry entity in entities)
                {
                    (( SQLiteParameter )command.Parameters[0]).Value = entity.EntityId;
                    rowCount += command.ExecuteNonQuery( );

                    if (rowCount % 100 == 0)
                    {
                        context.WriteInfo($"Copying Entity data... {rowCount} rows");
                    }
                }
            }
        }
示例#24
0
        /// <summary>
        ///     Migrates all data except metadata.
        /// </summary>
        private void MigrateContent(IProcessingContext context)
        {
            /////
            // Migrate entities
            /////
            context.WriteInfo("Copying Entity data...");
            IEnumerable <EntityEntry> entities      = DataSource.GetEntities(context);
            IList <EntityEntry>       addedEntities = entities as IList <EntityEntry> ?? entities.ToList( );

            context.Report.AddedEntities = addedEntities;
            Context.Report.Counts.Add(new StatisticsCount("Current Application Entities", addedEntities.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteEntities(addedEntities, context);

            /////
            // Migrate relationships
            /////
            context.WriteInfo("Copying Relationship data...");
            IEnumerable <RelationshipEntry> relationships       = DataSource.GetRelationships(context);
            IList <RelationshipEntry>       relationshipEntries = relationships as IList <RelationshipEntry> ?? relationships.ToList( );

            context.Report.AddedRelationships = relationshipEntries;
            Context.Report.Counts.Add(new StatisticsCount("Current Application Relationships", relationshipEntries.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteRelationships(relationshipEntries, context);

            /////
            // Migrate field data
            /////
            foreach (string fieldDataTable in Helpers.FieldDataTables)
            {
                context.WriteInfo(string.Format("Copying {0} Field data...", fieldDataTable));
                IEnumerable <DataEntry> fieldData   = DataSource.GetFieldData(fieldDataTable, context);
                IList <DataEntry>       dataEntries = fieldData as IList <DataEntry> ?? fieldData.ToList( );
                if (dataEntries.Count > 0)
                {
                    context.Report.AddedEntityData[fieldDataTable] = dataEntries;
                }

                Context.Report.Counts.Add(new StatisticsCount(string.Format("Current Application {0} Data", fieldDataTable), dataEntries.Count, StatisticsCountType.CurrentApplication));
                DataTarget.WriteFieldData(fieldDataTable, dataEntries, context);
            }

            context.WriteInfo("Copying Binary File data...");
            IEnumerable <BinaryDataEntry> binaryData        = DataSource.GetBinaryData(context);
            IList <BinaryDataEntry>       binaryDataEntries = binaryData as IList <BinaryDataEntry> ?? binaryData.ToList( );

            context.Report.AddedBinaryData = binaryDataEntries;
            Context.Report.Counts.Add(new StatisticsCount("Current Application Binary Data", binaryDataEntries.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteBinaryData(binaryDataEntries, context);

            context.WriteInfo("Copying Document File data...");
            IEnumerable <DocumentDataEntry> documentData        = DataSource.GetDocumentData(context);
            IList <DocumentDataEntry>       documentDataEntries = documentData as IList <DocumentDataEntry> ?? documentData.ToList( );

            context.Report.AddedDocumentData = documentDataEntries;
            Context.Report.Counts.Add(new StatisticsCount("Current Application Document Data", documentDataEntries.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteDocumentData(documentDataEntries, context);


            context.WriteInfo("Copying Secure data...");
            IEnumerable <SecureDataEntry> secureData        = DataSource.GetSecureData(context);
            IList <SecureDataEntry>       secureDataEntries = secureData as IList <SecureDataEntry> ?? secureData.ToList( );

            context.Report.AddedSecureData = secureDataEntries;
            Context.Report.Counts.Add(new StatisticsCount("Current Application Secure Data", secureDataEntries.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteSecureData(secureDataEntries, context);


            context.WriteInfo("Copying DoNotRemove data...");
            IList <Guid> doNotRemove = DataSource.GetDoNotRemove(context).ToList( );

            context.Report.AddedDoNotRemoveData = doNotRemove;
            Context.Report.Counts.Add(new StatisticsCount("Current Application DoNotRemove records", doNotRemove.Count, StatisticsCountType.CurrentApplication));
            DataTarget.WriteDoNotRemove(doNotRemove, context);
        }
示例#25
0
        /// <summary>
        /// Gets the entities that should not be removed as part of an upgrade operation.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public IEnumerable <Guid> GetDoNotRemove(IProcessingContext context)
        {
            // Namely, get the entities that:
            // 1. used to be part of the application,
            // 2. which are no longer part of the application,
            // 3. which are still present in the tenant (i.e. the app developer removed them from the app, but didn't delete them altogether).
            // 4. we also need to carry over any entities marked as 'do not remove' in previous versions

            // Step 0: Determine what the original version of this package was
            Guid?packageId = OriginalPackageId;

            if (packageId == null)
            {
                context.WriteInfo("Tenant application does not have a package Id");
                return(Enumerable.Empty <Guid>( ));
            }

            // Step 1: Get entities in the previous version
            LibraryAppSource appLibrarySource = new LibraryAppSource
            {
                AppVerId = packageId.Value
            };
            IEnumerable <Guid> entitiesInPrevVersion = appLibrarySource
                                                       .GetEntities(context)
                                                       .Select(e => e.EntityId)
                                                       .ToList( );

            // Step 2: Disregard entities that are still in the application
            IEnumerable <Guid> entitiesInTenantApp = ((IDataSource)this)
                                                     .GetEntities(context)
                                                     .Select(e => e.EntityId);
            IEnumerable <Guid> missingEntities = entitiesInPrevVersion.Except(entitiesInTenantApp).ToList();

            // Step 3: Check database to see if the entities are still present
            ISet <Guid> doNotRemove = new HashSet <Guid>( );
            DataTable   dt          = TableValuedParameter.Create(TableValuedParameterType.Guid);

            foreach (var guid in missingEntities)
            {
                dt.Rows.Add(guid);
            }
            using (IDbCommand command = CreateCommand( ))
            {
                command.CommandText = "dbo.spGetEntityIdsByUpgradeIds";
                command.CommandType = CommandType.StoredProcedure;
                command.AddParameter("@tenantId", DbType.Int64, TenantId);
                command.AddTableValuedParameter("@data", dt);

                using (IDataReader reader = command.ExecuteReader( ))
                {
                    while (reader.Read( ))
                    {
                        var guid = reader.GetGuid(0);
                        doNotRemove.Add(guid);   // entities still in the tenant should be marked as 'do not remove'
                    }
                }
            }

            // Step 4: Include entities marked as 'do not remove' in previous tenants
            var carriedOver = appLibrarySource.GetDoNotRemove(context);

            foreach (Guid guid in carriedOver)
            {
                doNotRemove.Add(guid);
            }

            // Entities that have been removed from the application, but still present in the tenant,
            // should get marked as 'do not delete' to indicate that when the application is upgraded it should not delete the entities.
            return(doNotRemove);
        }
示例#26
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);
        }
示例#27
0
        /// <summary>
        ///     Set the application metadata.
        /// </summary>
        /// <param name="metadata"></param>
        /// <param name="context"></param>
        void IDataTarget.SetMetadata(Metadata metadata, IProcessingContext context)
        {
            if (SkipMetadata)
            {
                context.WriteInfo("Skipping app library metadata");
                return;
            }

            /////
            // Write the application
            /////
            App app = Entity.GetByField <App>(metadata.AppId.ToString( ), new EntityRef("core:applicationId")).FirstOrDefault( );

            if (app == null)
            {
                app = new App
                {
                    Name          = metadata.AppName,
                    Description   = metadata.Description,
                    ApplicationId = metadata.AppId,
                    Publisher     = metadata.Publisher,
                    PublisherUrl  = metadata.PublisherUrl,
                    ReleaseDate   = metadata.ReleaseDate == DateTime.MinValue.ToUniversalTime() ? ( DateTime? )null : metadata.ReleaseDate.ToUniversalTime()
                };

                app.Save( );
            }

            /////
            // Write the app-package
            /////
            AppPackage package = app.ApplicationPackages.FirstOrDefault(ap => ap.AppVerId == metadata.AppVerId);

            if (package == null)
            {
                package = new AppPackage( );

                var version = new Version(metadata.Version);

                AppPackage existingVersion = app.ApplicationPackages.FirstOrDefault(ap => ap.AppVersionString == version.ToString( ));

                bool versionExists = false;

                while (existingVersion != null)
                {
                    versionExists = true;

                    version = new Version(version.Major, version.Minor + 1);

                    existingVersion = app.ApplicationPackages.FirstOrDefault(ap => ap.AppVersionString == version.ToString( ));
                }

                metadata.Version = version.ToString( );

                if (versionExists)
                {
                    context.WriteWarning("Version already exists.. incrementing");
                }
            }
            else
            {
                package = package.AsWritable <AppPackage>( );
                context.WriteWarning("Already installed.. overwriting");
            }

            string solutionNames = app.InSolution?.Name;

            /////
            // Localize the string values.
            /////
            package.Name                  = string.Format("{0} Application Package {1}", solutionNames ?? app.Name, metadata.Version);
            package.Description           = string.Format("Application Package for version {1} of {0}.", app.Name, metadata.Version);
            package.AppVersionString      = metadata.Version;
            package.AppVerId              = metadata.AppVerId;
            package.PackageForApplication = app;

            if (metadata.PublishDate != DateTime.MinValue && metadata.PublishDate > SqlDateTime.MinValue.Value)
            {
                package.PublishDate = metadata.PublishDate;
            }

            if (metadata.Dependencies != null)
            {
                IEntityCollection <AppPackageDependency> dependencies = new EntityCollection <AppPackageDependency>( );

                foreach (SolutionDependency dependency in metadata.Dependencies)
                {
                    AppPackageDependency appPackageDependency = new AppPackageDependency
                    {
                        Name = dependency.Name,
                        AppPackageDependencyName = dependency.DependencyName,
                        AppPackageDependencyId   = dependency.DependencyApplication,
                        AppPackageMinimumVersion = dependency.MinimumVersion == null ? null : dependency.MinimumVersion.ToString(4),
                        AppPackageMaximumVersion = dependency.MaximumVersion == null ? null : dependency.MaximumVersion.ToString(4),
                        AppPackageIsRequired     = dependency.IsRequired
                    };

                    dependencies.Add(appPackageDependency);
                }

                package.DependentAppPackageDetails = dependencies;
            }

            package.Save( );
        }