/// <summary>
        ///     Load file data with the given hash from the given repository.
        /// </summary>
        /// <param name="fileRepository"></param>
        /// <param name="token"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        internal static byte[] LoadFileData(IFileRepository fileRepository, string token, IProcessingContext context)
        {
            byte[] data;

            if (string.IsNullOrWhiteSpace(token))
            {
                return(null);
            }

            using (var stream = new MemoryStream( ))
            {
                try
                {
                    using (var sourceStream = fileRepository.Get(token))
                    {
                        sourceStream.CopyTo(stream);
                    }
                    data = stream.ToArray( );
                }
                catch (FileNotFoundException)
                {
                    context.WriteWarning($"File not found. (Hash: {token})");
                    data = null;
                }
                catch (Exception ex)
                {
                    context.WriteWarning($"An error occurred getting the file from file repository. DataHash: {token}. Error {ex}.");
                    data = null;
                }
            }

            return(data);
        }
示例#2
0
        /// <summary>
        ///     Load entities.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public virtual IEnumerable <EntityEntry> GetEntities(IProcessingContext context)
        {
            /////
            // Query entities that are part of the solution
            /////
            const string sql = @"select EntityUid
                            from AppEntity e
                            where e.AppVerUid = @appVer";

            using (IDbCommand command = CreateCommand( ))
            {
                command.CommandText = sql;
                command.AddParameterWithValue("@appVer", AppVerId);

                using (IDataReader reader = command.ExecuteReader( ))
                {
                    while (reader.Read( ))
                    {
                        if (reader.IsDBNull(0))
                        {
                            context.WriteWarning("Unexpected null UpgradeId in Entity.");
                            continue;
                        }

                        var entry = new EntityEntry
                        {
                            EntityId = reader.GetGuid(0)
                        };

                        yield return(entry);
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        ///     Load entities.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public IEnumerable <EntityEntry> GetEntities(IProcessingContext context)
        {
            /////
            // Query entities that are part of the solution
            /////
            const string sql = @"select Uid from _Entity";

            using (IDbCommand command = CreateCommand( ))
            {
                command.CommandText = sql;

                using (IDataReader reader = command.ExecuteReader( ))
                {
                    while (reader.Read( ))
                    {
                        if (reader.IsDBNull(0))
                        {
                            context.WriteWarning("Unexpected null UpgradeId in Entity.");
                            continue;
                        }

                        var entry = new EntityEntry
                        {
                            EntityId = reader.GetGuid(0)
                        };

                        yield return(entry);
                    }
                }
            }
        }
示例#4
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 virtual IEnumerable <Guid> GetDoNotRemove(IProcessingContext context)
        {
            /////
            // Query entities that have been marked as do-not-remove during upgrade of a solution.
            /////
            const string sql = @"select EntityUid
                            from AppDoNotRemove d
                            where d.AppVerUid = @appVer";

            using (IDbCommand command = CreateCommand( ))
            {
                command.CommandText = sql;
                command.AddParameterWithValue("@appVer", AppVerId);

                using (IDataReader reader = command.ExecuteReader( ))
                {
                    while (reader.Read( ))
                    {
                        if (reader.IsDBNull(0))
                        {
                            context.WriteWarning("Unexpected null EntityUid in AppDoNotRemove.");
                            continue;
                        }

                        Guid entityUid = reader.GetGuid(0);
                        yield return(entityUid);
                    }
                }
            }
        }
示例#5
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");
                    }
                }
            }
        }
示例#6
0
        public void WriteDocumentData(IEnumerable <DocumentDataEntry> data, IProcessingContext context)
        {
            Func <DataColumn[]> getColumnsAction = () => new[]
            {
                new DataColumn("DataHash", typeof(string))
                {
                    AllowDBNull = false
                }
            };

            Func <DocumentDataEntry, DataRow, PopulateRowResult> populateRowAction = (entry, row) =>
            {
                if (entry.Data == null)
                {
                    return(PopulateRowResult.InvalidData);
                }

                using (var source = new MemoryStream(entry.Data))
                {
                    try
                    {
                        Factory.DocumentFileRepository.Put(source);
                    }
                    catch (Exception ex)
                    {
                        context.WriteWarning($"An error occurred putting document into file repository. DataHash: {entry.DataHash}. Error {ex.ToString( )}.");

                        return(PopulateRowResult.InvalidData);
                    }
                }

                return(PopulateRowResult.Ignore);
            };

            var executionArguments = new ExecutionArguments <DocumentDataEntry>
            {
                Entries           = data,
                GetColumnsAction  = getColumnsAction,
                TableName         = "Document",
                Context           = context,
                PopulateRowAction = populateRowAction,
                SkipCommandExec   = true,
                ExecuteAction     = ExecuteAction.Writing,
            };

            Execute(executionArguments);
        }
示例#7
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)
        {
            Func <DataColumn[]> getColumnsAction = () => new[]
            {
                new DataColumn("DataHash", typeof(string))
                {
                    AllowDBNull = false
                }
            };

            Func <BinaryDataEntry, DataRow, PopulateRowResult> populateRowAction = (entry, row) =>
            {
                if (entry.Data == null)
                {
                    return(PopulateRowResult.InvalidData);
                }

                using (var source = new MemoryStream(entry.Data))
                {
                    try
                    {
                        Factory.BinaryFileRepository.Put(source);
                    }
                    catch (Exception ex)
                    {
                        context.WriteWarning(string.Format("An error occurred copying binary file into file repository. DataHash: {0}. Error {1}.", entry.DataHash, ex.ToString()));

                        return(PopulateRowResult.InvalidData);
                    }
                }

                return(PopulateRowResult.Ignore);
            };

            var executionArguments = new ExecutionArguments <BinaryDataEntry>
            {
                Entries           = data,
                GetColumnsAction  = getColumnsAction,
                TableName         = "Binary",
                SkipCommandExec   = true,
                Context           = context,
                PopulateRowAction = populateRowAction,
                ExecuteAction     = ExecuteAction.Writing
            };

            Execute(executionArguments);
        }
示例#8
0
        /// <summary>
        ///     Load entities.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        IEnumerable <EntityEntry> IDataSource.GetEntities(IProcessingContext context)
        {
            if (_entityCache == null)
            {
                var data = new List <EntityEntry>( );

                /////
                // Query entities that are part of the solution
                /////
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = "spGetTenantAppEntities";
                    command.CommandType = CommandType.StoredProcedure;
                    command.AddParameterWithValue("@solutionId", SolutionId);
                    command.AddParameterWithValue("@tenant", TenantId);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        while (reader.Read( ))
                        {
                            if (reader.IsDBNull(0))
                            {
                                context?.WriteWarning("Unexpected null UpgradeId in Entity.");

                                continue;
                            }

                            var entry = new EntityEntry
                            {
                                EntityId = reader.GetGuid(0)
                            };

                            data.Add(entry);
                        }
                    }
                }

                _entityCache = data;
            }

            return(_entityCache);
        }
示例#9
0
        /// <summary>
        ///     Load relationships.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        IEnumerable <RelationshipEntry> IDataSource.GetRelationships(IProcessingContext context)
        {
            if (_relationshipCache == null)
            {
                var data = new List <RelationshipEntry>( );

                /////
                // Query entities that are part of the solution
                /////
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = CommandText.TenantSourceGetRelationshipsCommandText;
                    command.CommandType = CommandType.Text;
                    command.AddParameterWithValue("@tenant", TenantId);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        while (reader.Read( ))
                        {
                            if (reader.IsDBNull(0))
                            {
                                context?.WriteWarning("Unexpected null UpgradeId in Entity.");

                                continue;
                            }

                            Guid typeId = reader.GetGuid(0);
                            Guid fromId = reader.GetGuid(1);
                            Guid toId   = reader.GetGuid(2);

                            RelationshipEntry entry = new RelationshipEntry(typeId, fromId, toId);

                            data.Add(entry);
                        }
                    }
                }

                _relationshipCache = data;
            }

            return(_relationshipCache);
        }
示例#10
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");
                    }
                }
            }
        }
示例#11
0
        /// <summary>
        ///     Return the decrypted secure data stored with the tenant.
        /// </summary>
        public IEnumerable <SecureDataEntry> GetSecureData(IProcessingContext context)
        {
            var result = new List <SecureDataEntry>();

            /////
            // Query entities that are part of the solution
            /////
            using (IDbCommand command = CreateCommand())
            {
                command.CommandText = "spSecuredDataReadTenant";
                command.CommandType = CommandType.StoredProcedure;
                command.AddParameterWithValue("@tenantId", TenantId);

                using (IDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        if (reader.IsDBNull(0))
                        {
                            context?.WriteWarning("Unexpected null SecureDataEntry.");

                            continue;
                        }

                        var secureId      = reader.GetGuid(0);
                        var secureContext = reader.GetString(1);
                        var data          = reader.GetString(2);

                        var entry = new SecureDataEntry(secureId, secureContext, EncryptString(data));

                        result.Add(entry);
                    }
                }
            }

            return(result);
        }
示例#12
0
        /// <summary>
        /// Reads the relationships.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="reader">The reader.</param>
        /// <param name="cardinality">The cardinality.</param>
        /// <param name="dict">The dictionary.</param>
        /// <param name="toOne">To one.</param>
        /// <param name="fromOne">From one.</param>
        /// <param name="droppedRelationships">if set to <c>true</c> [dropped relationships].</param>
        private void ReadRelationships(IProcessingContext context, IDataReader reader, Dictionary <long, CardinalityEnum_Enumeration> cardinality, Dictionary <RelationshipEntryKey, RelationshipEntry> dict, Dictionary <RelationshipEntryCardinalityKey, RelationshipEntry> toOne, Dictionary <RelationshipEntryCardinalityKey, RelationshipEntry> fromOne, bool droppedRelationships)
        {
            while (reader.Read( ))
            {
                if (reader.IsDBNull(0))
                {
                    context?.WriteWarning("Unexpected null UpgradeId in Entity.");

                    continue;
                }

                Guid typeId = reader.GetGuid(0);
                Guid fromId = reader.GetGuid(1);
                Guid toId   = reader.GetGuid(2);

                CardinalityEnum_Enumeration?card = null;

                if (reader.FieldCount > 3 && !reader.IsDBNull(3))
                {
                    CardinalityEnum_Enumeration cardValue;

                    if (cardinality.TryGetValue(reader.GetInt64(3), out cardValue))
                    {
                        card = cardValue;
                    }
                }

                RelationshipEntry entry = card == null ? new RelationshipEntry(typeId, fromId, toId) : new RelationshipEntry(typeId, fromId, toId, card.Value);

                if (RelationshipRestrictions != null)
                {
                    if (RelationshipRestrictions.Any(restriction => !restriction.IsAllowed(entry)))
                    {
                        continue;
                    }
                }

                var key = entry.GetKey( );

                RelationshipEntry value;

                bool violation = false;

                Action addFrom = null;
                Action addTo   = null;

                if (entry.Cardinality != null && (entry.Cardinality == CardinalityEnum_Enumeration.ManyToOne || entry.Cardinality == CardinalityEnum_Enumeration.OneToOne))
                {
                    var cardinalityKey = new RelationshipEntryCardinalityKey(entry.TypeId, entry.FromId);

                    if (toOne.TryGetValue(cardinalityKey, out value))
                    {
                        if (entry.TypeId != value.TypeId || entry.FromId != value.FromId || entry.ToId != value.ToId)
                        {
                            /////
                            // Cardinality violation.
                            /////
                            EventLog.Application.WriteWarning(string.Format("Detected cardinality violation {7}({0}).\n\nExisting Type: {1}\nExisting From: {2}\nExisting To: {3}\n\nDropped Type: {4}\nDropped From: {5}\nDropped To: {6}\n",
                                                                            entry.Cardinality,
                                                                            value.TypeId.ToString("B"),
                                                                            value.FromId.ToString("B"),
                                                                            value.ToId.ToString("B"),
                                                                            entry.TypeId.ToString("B"),
                                                                            entry.FromId.ToString("B"),
                                                                            entry.ToId.ToString("B"),
                                                                            droppedRelationships ? "processing previously dropped relationship " : ""));
                        }

                        violation = true;
                    }
                    else
                    {
                        RelationshipEntry relationshipEntry = entry;
                        addFrom = () => toOne.Add(cardinalityKey, relationshipEntry);
                    }
                }

                if (!violation && entry.Cardinality != null && (entry.Cardinality == CardinalityEnum_Enumeration.OneToMany || entry.Cardinality == CardinalityEnum_Enumeration.OneToOne))
                {
                    var cardinalityKey = new RelationshipEntryCardinalityKey(entry.TypeId, entry.ToId);

                    if (fromOne.TryGetValue(cardinalityKey, out value))
                    {
                        if (entry.TypeId != value.TypeId || entry.FromId != value.FromId || entry.ToId != value.ToId)
                        {
                            /////
                            // Cardinality violation.
                            /////
                            EventLog.Application.WriteWarning(string.Format("Detected cardinality violation {7}({0}).\n\nExisting Type: {1}\nExisting From: {2}\nExisting To: {3}\n\nDropped Type: {4}\nDropped From: {5}\nDropped To: {6}\n",
                                                                            entry.Cardinality,
                                                                            value.TypeId.ToString("B"),
                                                                            value.FromId.ToString("B"),
                                                                            value.ToId.ToString("B"),
                                                                            entry.TypeId.ToString("B"),
                                                                            entry.FromId.ToString("B"),
                                                                            entry.ToId.ToString("B"),
                                                                            droppedRelationships ? "processing previously dropped relationship " : ""));
                        }

                        violation = true;
                    }
                    else
                    {
                        RelationshipEntry relationshipEntry = entry;
                        addTo = () => fromOne.Add(cardinalityKey, relationshipEntry);
                    }
                }

                if (violation)
                {
                    continue;
                }

                addFrom?.Invoke( );

                addTo?.Invoke( );

                if (!dict.TryGetValue(key, out value))
                {
                    dict.Add(key, entry);
                }
                else
                {
                    if (entry.Cardinality != CardinalityEnum_Enumeration.ManyToMany)
                    {
                        if (entry.TypeId != value.TypeId || entry.FromId != value.FromId || entry.ToId != value.ToId)
                        {
                            /////
                            // Cardinality violation.
                            /////
                            EventLog.Application.WriteWarning(string.Format("Detected cardinality violation {7}({0}).\n\nExisting Type: {1}\nExisting From: {2}\nExisting To: {3}\n\nDropped Type: {4}\nDropped From: {5}\nDropped To: {6}\n",
                                                                            entry.Cardinality,
                                                                            value.TypeId.ToString("B"),
                                                                            value.FromId.ToString("B"),
                                                                            value.ToId.ToString("B"),
                                                                            entry.TypeId.ToString("B"),
                                                                            entry.FromId.ToString("B"),
                                                                            entry.ToId.ToString("B"),
                                                                            droppedRelationships ? "processing previously dropped relationship " : ""));
                        }
                    }
                }
            }
        }
        /// <summary>
        ///     Load entities.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override IEnumerable <EntityEntry> GetEntities(IProcessingContext context)
        {
            /////
            // Query entities that are part of the solution
            /////
            const string sql = @"
DECLARE @name UNIQUEIDENTIFIER
DECLARE @isOfType UNIQUEIDENTIFIER

SELECT @name = EntityUid FROM AppData_Alias WHERE Data = 'name' AND Namespace = 'core'
SELECT @isOfType = EntityUid FROM AppData_Alias WHERE Data = 'isOfType' AND Namespace = 'core'

SELECT DISTINCT e.EntityUid, en.Data, etn.Data
FROM AppEntity e
LEFT JOIN AppData_NVarChar en ON e.EntityUid = en.EntityUid AND e.AppVerUid = en.AppVerUid AND en.FieldUid = @name
LEFT JOIN AppRelationship et ON e.EntityUid = et.FromUid AND e.AppVerUid = et.AppVerUid AND et.TypeUid = @isOfType
LEFT JOIN AppData_NVarChar etn ON et.ToUid = etn.EntityUid AND etn.FieldUid = @name
WHERE e.AppVerUid = @appVer";

            var map = new Dictionary <Guid, EntityStagingEntry>( );

            using (IDbCommand command = CreateCommand( ))
            {
                command.CommandText = sql;
                command.AddParameterWithValue("@appVer", AppVerId);

                using (IDataReader reader = command.ExecuteReader( ))
                {
                    if (reader != null)
                    {
                        while (reader.Read( ))
                        {
                            if (reader.IsDBNull(0))
                            {
                                context.WriteWarning("Unexpected null UpgradeId in Entity.");
                                continue;
                            }

                            EntityStagingEntry entry;

                            if (!map.TryGetValue(reader.GetGuid(0), out entry))
                            {
                                entry = new EntityStagingEntry
                                {
                                    EntityId       = reader.GetGuid(0),
                                    EntityName     = reader.IsDBNull(1) ? null : reader.GetString(1),
                                    EntityTypeName = reader.IsDBNull(2) ? null : reader.GetString(2)
                                };

                                map[entry.EntityId] = entry;
                            }
                            else
                            {
                                if (entry.EntityTypeName != null && !reader.IsDBNull(2))
                                {
                                    string[] split = entry.EntityTypeName.Split(new[]
                                    {
                                        ','
                                    });

                                    string type = reader.GetString(2);

                                    if (split.All(s => s.Trim( ).ToLowerInvariant( ) != type))
                                    {
                                        entry.EntityTypeName += ", " + type;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(map.Values);
        }
示例#14
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)
        {
            Func <DataColumn[]> getColumnsAction = () => new[]
            {
                new DataColumn("OldDataHash", typeof(string))
                {
                    AllowDBNull = false
                },
                new DataColumn("NewDataHash", typeof(string))
                {
                    AllowDBNull = false
                },
                new DataColumn("FileExtension", typeof(string))
                {
                    AllowDBNull = true
                }
            };

            Func <BinaryDataEntry, DataRow, PopulateRowResult> populateRowAction = (entry, row) =>
            {
                if (entry.Data == null)
                {
                    return(PopulateRowResult.InvalidData);
                }

                using (var source = new MemoryStream(entry.Data))
                {
                    try
                    {
                        // Add the file to the repository
                        string newDataHash = Factory.AppLibraryFileRepository.Put(source);

                        row[0] = entry.DataHash;
                        row[1] = newDataHash;
                        row[2] = string.IsNullOrWhiteSpace(entry.FileExtension) ? null : entry.FileExtension;
                    }
                    catch (Exception ex)
                    {
                        context.WriteWarning(string.Format("An error occurred putting binary file into file repository. DataHash: {0}. Error {1}.", entry.DataHash, ex.ToString()));

                        return(PopulateRowResult.InvalidData);
                    }
                }

                return(PopulateRowResult.Success);
            };

            Func <IDbCommand, int> customCommandExecuteAction = command =>
            {
                // Upgrade datahashes and file extensions
                command.CommandText = string.Format(CommandText.AppLibraryUpgradeFileDataHashesAndFileExtensions, "#Binary");
                command.CommandType = CommandType.Text;
                command.AddParameterWithValue("@appVerId", ApplicationVersionId);
                command.AddParameterWithValue("@fileDataHashFieldId", Helpers.FileDataHashFieldUpgradeId);
                command.AddParameterWithValue("@fileExtensionFieldId", Helpers.FileExtensionFieldUpgradeId);
                command.ExecuteNonQuery();
                return(0);
            };

            Action <int> setCopiedCountAction = count =>
            {
                if (context != null)
                {
                    context.Report.Counts.Add(new StatisticsCount("Copied Binary Data", count, StatisticsCountType.Copied));
                }
            };

            var executionArguments = new LibraryAppTargetExecutionArguments <BinaryDataEntry>
            {
                Entries                    = data,
                GetColumnsAction           = getColumnsAction,
                TableName                  = "#Binary",
                Context                    = context,
                SetCopiedCountAction       = setCopiedCountAction,
                PopulateRowAction          = populateRowAction,
                ClearExistingData          = false,
                CustomCommandExecuteAction = customCommandExecuteAction
            };

            Execute(executionArguments);
        }
示例#15
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( );
        }
        /// <summary>
        ///     Load relationships.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        IEnumerable <RelationshipEntry> IDataSource.GetRelationships(IProcessingContext context)
        {
            if (_relationshipCache == null)
            {
                var data = new List <RelationshipEntry>( );

                /////
                // Query the relationship types that are lookups.
                /////
                string lookups = $@"
DECLARE @cardinalityId BIGINT
DECLARE @oneToOneId BIGINT
DECLARE @manyToOneId BIGINT
DECLARE @oneToManyId BIGINT
DECLARE @manyToManyId BIGINT

SELECT @cardinalityId = EntityId FROM Data_Alias WHERE Data = 'cardinality' AND Namespace = 'core' AND TenantId = @tenant
SELECT @oneToOneId = EntityId FROM Data_Alias WHERE Data = 'oneToOne' AND Namespace = 'core' AND TenantId = @tenant
SELECT @oneToManyId = EntityId FROM Data_Alias WHERE Data = 'oneToMany' AND Namespace = 'core' AND TenantId = @tenant
SELECT @manyToOneId = EntityId FROM Data_Alias WHERE Data = 'manyToOne' AND Namespace = 'core' AND TenantId = @tenant
SELECT @manyToManyId = EntityId FROM Data_Alias WHERE Data = 'manyToMany' AND Namespace = 'core' AND TenantId = @tenant

SELECT e.UpgradeId, {( short ) CardinalityEnum_Enumeration.OneToOne} FROM Entity e JOIN Relationship r ON r.TenantId = e.TenantId AND e.Id = r.FromId AND r.TypeId = @cardinalityId AND ToId = @oneToOneId WHERE e.TenantId = @tenant
UNION ALL
SELECT e.UpgradeId, {( short ) CardinalityEnum_Enumeration.OneToMany} FROM Entity e JOIN Relationship r ON r.TenantId = e.TenantId AND e.Id = r.FromId AND r.TypeId = @cardinalityId AND ToId = @oneToManyId WHERE e.TenantId = @tenant
UNION ALL
SELECT e.UpgradeId, {( short ) CardinalityEnum_Enumeration.ManyToOne} FROM Entity e JOIN Relationship r ON r.TenantId = e.TenantId AND e.Id = r.FromId AND r.TypeId = @cardinalityId AND ToId = @manyToOneId WHERE e.TenantId = @tenant
UNION ALL
SELECT e.UpgradeId, {( short ) CardinalityEnum_Enumeration.ManyToMany} FROM Entity e JOIN Relationship r ON r.TenantId = e.TenantId AND e.Id = r.FromId AND r.TypeId = @cardinalityId AND ToId = @manyToManyId WHERE e.TenantId = @tenant
";

                var lookupTypes = new Dictionary <Guid, CardinalityEnum_Enumeration>( );

                /////
                // Query entities that are part of the solution
                /////
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = lookups;
                    command.CommandType = CommandType.Text;

                    command.AddParameterWithValue("@tenant", TenantId);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        while (reader.Read( ))
                        {
                            Guid id = reader.GetGuid(0);
                            var  cardinalityType = ( CardinalityEnum_Enumeration )reader.GetInt32(1);

                            lookupTypes[id] = cardinalityType;
                        }
                    }

                    command.CommandText = "spGetTenantAppRelationships";
                    command.CommandType = CommandType.StoredProcedure;
                    command.AddParameterWithValue("@solutionId", SolutionId);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        while (reader.Read( ))
                        {
                            if (reader.IsDBNull(0))
                            {
                                context?.WriteWarning("Unexpected null UpgradeId in Entity.");

                                continue;
                            }

                            Guid typeId = reader.GetGuid(0);
                            Guid fromId = reader.GetGuid(1);
                            Guid toId   = reader.GetGuid(2);

                            CardinalityEnum_Enumeration cardinality;

                            var entry = lookupTypes.TryGetValue(typeId, out cardinality) ? new RelationshipEntry(typeId, fromId, toId, cardinality) : new RelationshipEntry(typeId, fromId, toId);

                            data.Add(entry);
                        }
                    }
                }

                _relationshipCache = data;
            }

            return(_relationshipCache);
        }
        /// <summary>
        ///     Load entities.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        IEnumerable <EntityEntry> IDataSource.GetEntities(IProcessingContext context)
        {
            if (_entityCache == null)
            {
                var data = new Dictionary <Guid, EntityEntry>( );

                /////
                // Query entities that are part of the solution
                /////
                using (IDbCommand command = CreateCommand( ))
                {
                    command.CommandText = "spGetTenantAppStagedEntities";
                    command.CommandType = CommandType.StoredProcedure;
                    command.AddParameterWithValue("@solutionId", SolutionId);
                    command.AddParameterWithValue("@tenant", TenantId);

                    using (IDataReader reader = command.ExecuteReader( ))
                    {
                        while (reader.Read( ))
                        {
                            if (reader.IsDBNull(0))
                            {
                                context?.WriteWarning("Unexpected null UpgradeId in Entity.");

                                continue;
                            }

                            int    depth           = reader.GetInt32(0);
                            Guid   entityUpgradeId = reader.GetGuid(1);
                            long   entityId        = reader.GetInt64(2);
                            string entityName      = reader.IsDBNull(3) ? null : reader.GetString(3);
                            long   entityTypeId    = reader.IsDBNull(4) ? 0 : reader.GetInt64(4);
                            string entityTypeName  = reader.IsDBNull(5) ? null : reader.GetString(5);

                            EntityEntry entry;

                            if (!data.TryGetValue(entityUpgradeId, out entry))
                            {
                                entry = new EntityStagingEntry
                                {
                                    EntityId       = entityUpgradeId,
                                    Id             = entityId,
                                    EntityName     = entityName,
                                    EntityTypeId   = entityTypeId,
                                    EntityTypeName = entityTypeName
                                };

                                data[entityUpgradeId] = entry;
                            }

                            var castEntry = entry as EntityStagingEntry;

                            if (depth > 0 && castEntry != null)
                            {
                                if (castEntry.Parents == null)
                                {
                                    castEntry.Parents = new List <EntityParentEntry>( );
                                }

                                castEntry.Parents.Add(new EntityParentEntry
                                {
                                    Depth = depth,
                                    ParentEntityUpgradeId     = reader.GetGuid(6),
                                    RelationshipTypeUpgradeId = reader.GetGuid(7),
                                    RelationshipTypeId        = reader.IsDBNull(8) ? 0 : reader.GetInt64(8),
                                    RelationshipTypeName      = reader.IsDBNull(9) ? null : reader.GetString(9),
                                    Reason = ( InclusionReason )Enum.Parse(typeof(InclusionReason), reader.GetString(10), true),
                                });
                            }
                        }
                    }
                }

                _entityCache = data.Values.ToList( );
            }

            return(_entityCache);
        }