public bool SaveEntry(RFCatalogEntry entry, bool raiseEvent = true, bool overwrite = false)
        {
            var materialUpdate = false;

            if (entry is RFDocument && !(entry as RFDocument).Type.Contains("."))
            {
                SystemLog.Warning(this, "Full type name required to save key {0}", entry.Key);
            }
            if (entry.Key.Plane == RFPlane.Ephemeral)
            {
                lock (_memoryStore)
                {
                    if (!_memoryStore.ContainsKey(entry.Key.ToString()))
                    {
                        _memoryStore.Add(entry.Key.ToString(), entry);
                    }
                    else
                    {
                        _memoryStore[entry.Key.ToString()] = entry;
                    }
                }
            }
            else
            {
                // don't raise events if not saved (i.e. same content)
                materialUpdate = _catalog.SaveItem(entry, overwrite);
                raiseEvent    &= materialUpdate;
            }
            if (raiseEvent)
            {
                _events.RaiseEvent(this, new RFCatalogUpdateEvent
                {
                    Key = entry.Key
                }, ProcessingKey);
            }
            return(materialUpdate);
        }
Beispiel #2
0
        protected RFCatalogEntry ExtractEntry(RFStoreType storeType, Dictionary <string, object> dataRow, bool ignoreContent)
        {
            RFCatalogEntry entry = null;

            switch (storeType)
            {
            case RFStoreType.Document:
                entry = ExtractDocument(dataRow, ignoreContent);
                break;

            default:
                throw new Exception(String.Format("Unrecognized store type {0}", storeType));
            }

            // common fields
            var type = dataRow["KeyType"].ToString();

            entry.Key        = (RFCatalogKey)RFXMLSerializer.DeserializeContract(type, dataRow["SerializedKey"].ToString());
            entry.Metadata   = RFMetadata.Deserialize(dataRow["Metadata"].ToString()) ?? new RFMetadata();
            entry.UpdateTime = (DateTimeOffset)dataRow["UpdateTime"];
            entry.Version    = (int)dataRow["Version"];
            entry.IsValid    = (bool)dataRow["IsValid"];
            return(entry);
        }
Beispiel #3
0
        public override bool SaveItem(RFCatalogEntry item, bool overwrite = false)
        {
            if (item == null)
            {
                Log.Warning(this, "SaveItem with null");
                return(false);
            }

            if (item.Key.Plane == RFPlane.User)
            {
                //Log.Debug(this, "SaveItem {0}", entry.Key.ToString());
            }
            try
            {
                var serializedContent = (item is RFDocument && item.IsValid) ? SerializeContent((item as RFDocument).Content) : new byte[0];
                var compressedContent = CompressContent(serializedContent);
                var keyType           = item.Key.GetType().FullName;
                var keyString         = item.Key.ToString();
                var keyHash           = RFStringHelpers.QuickHash(keyString);
                var keyHashResource   = "KH" + keyHash;

                using (var scope = Transaction.Current == null ? new TransactionScope(_useTransactions ? TransactionScopeOption.Required : TransactionScopeOption.Suppress, new TransactionOptions
                {
                    IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
                    Timeout = TimeSpan.FromSeconds(30)
                }) : null)
                {
                    using (var connection = new SqlConnection(_connectionString))
                    {
                        connection.Open();

                        // acquire lock on the hash
                        string lockSQL   = "sp_getapplock";
                        string unlockSQL = "sp_releaseapplock @Resource = '" + keyHashResource + "';";
                        using (var lockCommand = new SqlCommand(lockSQL, connection))
                        {
                            lockCommand.CommandType = CommandType.StoredProcedure;
                            lockCommand.Parameters.AddWithValue("@Resource", keyHashResource);
                            lockCommand.Parameters.AddWithValue("@LockMode", "Exclusive");
                            lockCommand.Parameters.AddWithValue("@LockOwner", "Transaction");
                            var retValue = lockCommand.Parameters.Add("@ReturnVal", SqlDbType.Int);
                            retValue.Direction = ParameterDirection.ReturnValue;
                            //RFStatic.Log.Debug(this, "sp_getapplock called...");
                            lockCommand.ExecuteNonQuery();
                            if ((int)retValue.Value < 0)
                            {
                                Log.Error(this, "sp_getapplock returned {0}", retValue.Value);
                            }
                            //RFStatic.Log.Debug(this, "sp_getapplock returned {0}", retValue.Value);
                        }

                        try
                        {
                            // find or create existing key
                            long catalogKeyID = 0;
                            var  getKeySQL    = String.Format("SELECT [CatalogKeyID] FROM [RIFF].[CatalogKey] WHERE [KeyHash] = @KeyHash AND [KeyType] = @KeyType AND CAST([SerializedKey] AS VARCHAR({0})) = CAST(CAST(@SerializedKey AS XML) AS VARCHAR({0}))", _maxKeyLength);
                            if (!_trackKeyHash)
                            {
                                getKeySQL = String.Format("SELECT [CatalogKeyID] FROM [RIFF].[CatalogKey] WHERE [KeyType] = @KeyType AND CAST([SerializedKey] AS VARCHAR({0})) = CAST(CAST(@SerializedKey AS XML) AS VARCHAR({0}))", _maxKeyLength);
                            }
                            using (var getCommand = CreateCommand(getKeySQL, connection))
                            {
                                getCommand.Parameters.AddWithValue("@KeyType", keyType);
                                if (_trackKeyHash)
                                {
                                    getCommand.Parameters.AddWithValue("@KeyHash", keyHash);
                                }
                                getCommand.Parameters.AddWithValue("@SerializedKey", keyString);
                                var result = getCommand.ExecuteScalar();
                                if (result != null)
                                {
                                    catalogKeyID = (long)result;
                                }
                            }

                            if (catalogKeyID == 0)
                            {
                                string createKeySQL = "INSERT INTO [RIFF].[CatalogKey] ( [KeyType], [SerializedKey], [KeyHash], [RootHash], [FriendlyString] )"
                                                      + " VALUES ( @KeyType, @SerializedKey, @KeyHash, @RootHash, @FriendlyString ); SELECT SCOPE_IDENTITY()";
                                using (var createKeyCommand = CreateCommand(createKeySQL, connection))
                                {
                                    var rootHash = RFStringHelpers.QuickHash(item.Key.RootKey().ToString());

                                    createKeyCommand.Parameters.AddWithValue("@KeyType", keyType);
                                    createKeyCommand.Parameters.AddWithValue("@SerializedKey", keyString);
                                    createKeyCommand.Parameters.AddWithValue("@KeyHash", keyHash);
                                    createKeyCommand.Parameters.AddWithValue("@RootHash", rootHash);
                                    createKeyCommand.Parameters.AddWithValue("@FriendlyString", RFStringHelpers.StringToSQL(item.Key.FriendlyString(), true, 100, false));

                                    var result = createKeyCommand.ExecuteScalar();
                                    if (result != null)
                                    {
                                        catalogKeyID = (long)((decimal)result);
                                    }
                                }
                            }

                            if (catalogKeyID == 0)
                            {
                                throw new RFSystemException(this, "Unable to create new catalog key.");
                            }

                            // lookup any existing entries and calculate next version
                            int    version       = 1;
                            string getVersionSQL = "SELECT MAX([Version]) FROM [RIFF].[CatalogEntry] WHERE [CatalogKeyID] = @CatalogKeyID";
                            using (var getVersionCommand = CreateCommand(getVersionSQL, connection))
                            {
                                getVersionCommand.Parameters.AddWithValue("@CatalogKeyID", catalogKeyID);

                                var result = getVersionCommand.ExecuteScalar();
                                if (result != null && result != DBNull.Value)
                                {
                                    version = ((int)result) + 1;
                                }
                            }

                            // there is an existing document - compare content
                            if (version > 1)
                            {
                                long existingEntryID = 0;
                                using (var loadExistingCommand = LoadEntryCommand(item.Key, version - 1, connection))
                                {
                                    using (var reader = loadExistingCommand.ExecuteReader(System.Data.CommandBehavior.SingleResult))
                                    {
                                        var dataTable = new DataTable();
                                        dataTable.Load(reader);
                                        if (dataTable != null && dataTable.Rows != null && dataTable.Rows.Count == 1)
                                        {
                                            switch (item.Key.StoreType)
                                            {
                                            case RFStoreType.Document:
                                                existingEntryID = (long)dataTable.Rows[0]["CatalogEntryID"];
                                                try
                                                {
                                                    var existingType          = dataTable.Rows[0]["ContentType"].ToString();
                                                    var existingBinaryContent = dataTable.Rows[0]["BinaryContent"] as byte[];
                                                    var existingValid         = (bool)dataTable.Rows[0]["IsValid"];

                                                    // decompress binary content to avoid
                                                    // flagging update if only compression changed
                                                    if (existingBinaryContent != null && existingBinaryContent.Length > 0 && existingValid == item.IsValid)
                                                    {
                                                        var rawExistingContent = DecompressContent(existingBinaryContent);
                                                        if (Enumerable.SequenceEqual(serializedContent ?? new byte[0], rawExistingContent ?? new byte[0]))
                                                        {
                                                            //transaction.Rollback(); -- to avoid zombiecheck errors
                                                            if (item.Key.Plane == RFPlane.User)
                                                            {
                                                                Log.Info(this, "Not required to update {0}/{1}/{2}", item.Key.GetType().Name, item.Key.FriendlyString(), item.Key.GetInstance());
                                                            }
                                                            if (!_useTransactions)     // lock will be auto released on transaction which prevents other thread coming in before transaction is completed
                                                            {
                                                                using (var unlockCommand = new SqlCommand(unlockSQL, connection))
                                                                {
                                                                    unlockCommand.ExecuteNonQuery();
                                                                }
                                                            }
                                                            scope.Complete();
                                                            return(false);
                                                        }
                                                    }
                                                }
                                                catch (Exception ex)
                                                {
                                                    Log.Exception(this, "Unable to compare to existing", ex);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                                if (overwrite && existingEntryID > 0)
                                {
                                    // update content rather than create new version
                                    var    document          = item as RFDocument;
                                    string updateDocumentSQL = "UPDATE [RIFF].[CatalogDocument] SET [BinaryContent] = @BinaryContent, [ContentType] = @ContentType where [CatalogEntryID] = @CatalogEntryID";
                                    using (var updateDocumentCommand = CreateCommand(updateDocumentSQL, connection))
                                    {
                                        updateDocumentCommand.Parameters.AddWithValue("@CatalogEntryID", existingEntryID);
                                        updateDocumentCommand.Parameters.AddWithValue("@ContentType", document.Type);
                                        if (compressedContent == null || compressedContent.Length == 0)
                                        {
                                            updateDocumentCommand.Parameters.AddWithValue("@BinaryContent", new byte[0]);
                                        }
                                        else
                                        {
                                            updateDocumentCommand.Parameters.AddWithValue("@BinaryContent", compressedContent);
                                        }

                                        var affected = updateDocumentCommand.ExecuteNonQuery();
                                        if (affected != 1)
                                        {
                                            throw new RFSystemException(this, "Unable to update document.");
                                        }
                                    }
                                    string updateEntrySQL = "UPDATE [RIFF].[CatalogEntry] SET [UpdateTime] = @UpdateTime, [Metadata] = @Metadata, [IsValid] = @IsValid where [CatalogEntryID] = @CatalogEntryID";
                                    using (var updateEntryCommand = CreateCommand(updateEntrySQL, connection))
                                    {
                                        updateEntryCommand.Parameters.AddWithValue("@CatalogEntryID", existingEntryID);
                                        updateEntryCommand.Parameters.AddWithValue("@IsValid", item.IsValid);
                                        updateEntryCommand.Parameters.AddWithValue("@Metadata", item.Metadata != null ? ((object)item.Metadata.Serialize() ?? DBNull.Value) : DBNull.Value);
                                        updateEntryCommand.Parameters.AddWithValue("@UpdateTime", item.UpdateTime.Year < 1980 ? new DateTimeOffset(DateTime.Now) : item.UpdateTime);

                                        var affected = updateEntryCommand.ExecuteNonQuery();
                                        if (affected != 1)
                                        {
                                            throw new RFSystemException(this, "Unable to update entry.");
                                        }
                                    }
                                    if (item.Key.Plane == RFPlane.User)
                                    {
                                        Log.Debug(this, "Overwritten item {0}", item.Key.ToString());
                                    }
                                    if (!_useTransactions) // lock will be auto released on transaction which prevents other thread coming in before transaction is completed
                                    {
                                        using (var unlockCommand = new SqlCommand(unlockSQL, connection))
                                        {
                                            unlockCommand.ExecuteNonQuery();
                                        }
                                    }
                                    scope?.Complete();
                                    return(true);
                                }
                            }

                            // create entry
                            long   catalogEntryID = 0;
                            string createEntrySQL = "INSERT INTO [RIFF].[CatalogEntry] ( [CatalogKeyID], [Version], [Metadata], [IsValid], [UpdateTime] )" +
                                                    " VALUES (@CatalogKeyID, @Version, @Metadata, @IsValid, @UpdateTime); SELECT SCOPE_IDENTITY()";
                            using (var createEntryCommand = CreateCommand(createEntrySQL, connection))
                            {
                                createEntryCommand.Parameters.AddWithValue("@CatalogKeyID", catalogKeyID);
                                createEntryCommand.Parameters.AddWithValue("@Metadata", item.Metadata != null ? ((object)item.Metadata.Serialize() ?? DBNull.Value) : DBNull.Value);
                                createEntryCommand.Parameters.AddWithValue("@Version", version);
                                createEntryCommand.Parameters.AddWithValue("@IsValid", item.IsValid);
                                createEntryCommand.Parameters.AddWithValue("@UpdateTime", item.UpdateTime.Year < 1980 ? new DateTimeOffset(DateTime.Now) : item.UpdateTime);

                                var result = createEntryCommand.ExecuteScalar();
                                if (result != null && result != DBNull.Value)
                                {
                                    catalogEntryID = (long)((decimal)result);
                                }
                            }

                            if (catalogEntryID == 0)
                            {
                                throw new RFSystemException(this, "Unable to create new catalog entry.");
                            }

                            // create content
                            switch (item.Key.StoreType)
                            {
                            case RFStoreType.Document:
                            {
                                var    document          = item as RFDocument;
                                string createDocumentSQL = "INSERT INTO [RIFF].[CatalogDocument] ( [CatalogEntryID], [ContentType], [BinaryContent] ) VALUES ( @CatalogEntryID, @ContentType, @BinaryContent )";
                                using (var createDocumentCommand = CreateCommand(createDocumentSQL, connection))
                                {
                                    createDocumentCommand.Parameters.AddWithValue("@CatalogEntryID", catalogEntryID);
                                    createDocumentCommand.Parameters.AddWithValue("@ContentType", document.Type);
                                    if (compressedContent == null || compressedContent.Length == 0)
                                    {
                                        createDocumentCommand.Parameters.AddWithValue("@BinaryContent", new byte[0]);
                                    }
                                    else
                                    {
                                        createDocumentCommand.Parameters.AddWithValue("@BinaryContent", compressedContent);
                                    }

                                    var affected = createDocumentCommand.ExecuteNonQuery();
                                    if (affected != 1)
                                    {
                                        throw new RFSystemException(this, "Unable to create document.");
                                    }
                                }
                            }
                            break;

                            default:
                                throw new RFSystemException(this, "Unknown store type {0}", item.Key.StoreType);
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Exception(this, ex, "Error saving entry (inner) {0}", item.Key);
                        }
                        if (!_useTransactions) // lock will be auto released on transaction which prevents other thread coming in before transaction is completed
                        {
                            using (var unlockCommand = new SqlCommand(unlockSQL, connection))
                            {
                                unlockCommand.ExecuteNonQuery();
                            }
                        }
                    }
                    if (scope != null)
                    {
                        scope.Complete();
                    }
                }
                if (item.Key.Plane == RFPlane.User)
                {
                    Log.Info(this, "Saved key {0}/{1}/{2}", item.Key.GetType().Name, item.Key.FriendlyString(), item.Key.GetInstance());
                }
                return(true);
            }
            catch (Exception ex)
            {
                Log.Exception(this, ex, "Error saving entry (outer) {0}", item.Key);
                return(false);
            }
        }
Beispiel #4
0
        public override RFCatalogEntry LoadItem(RFCatalogKey itemKey, int version = 0, bool ignoreContent = false)
        {
            RFCatalogEntry entry = null;

            if (itemKey.Plane == RFPlane.User)
            {
                //Log.Debug(this, "LoadItem {0}", catalogKey.ToString());
            }
            try
            {
                var dataRow = new Dictionary <string, object>();
                using (var connection = new SqlConnection(_connectionString))
                {
                    connection.Open();
                    try
                    {
                        long catalogEntryID = 0;
                        using (var command = LoadEntryCommand(itemKey, version, connection))
                        {
                            using (var reader = command.ExecuteReader(System.Data.CommandBehavior.SingleRow))
                            {
                                if (reader.HasRows)
                                {
                                    reader.Read();

                                    catalogEntryID = reader.GetInt64(7);

                                    dataRow.Add("CatalogKeyID", reader.GetInt64(0));
                                    dataRow.Add("KeyType", reader.GetString(1));
                                    dataRow.Add("SerializedKey", reader.GetString(2));
                                    dataRow.Add("Version", reader.GetInt32(3));
                                    dataRow.Add("Metadata", reader.IsDBNull(4) ? String.Empty : reader.GetString(4));
                                    dataRow.Add("IsValid", reader.GetBoolean(5));
                                    dataRow.Add("UpdateTime", reader.GetDateTimeOffset(6));
                                    dataRow.Add("CatalogEntryID", catalogEntryID);
                                    dataRow.Add("ContentType", reader.GetString(8));
                                    dataRow.Add("BinaryContent", reader.IsDBNull(9) ? null : reader.GetSqlBinary(9).Value);
                                }
                            }
                        }
                    }
                    catch (SqlException)
                    {
                        throw;
                    }
                    catch (InvalidOperationException)
                    {
                        throw;
                    }
                    catch (Exception ex)
                    {
                        Log.Exception(this, ex, "Error loading entry {0}", itemKey);
                    }
                }
                if (dataRow.Any())
                {
                    entry = ExtractEntry(itemKey.StoreType, dataRow, ignoreContent);
                }
            }
            catch (SqlException)
            {
                throw;
            }
            catch (InvalidOperationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                Log.Exception(this, ex, "Error loading entry {0}", itemKey);
            }

            return(entry);
        }