/// <summary>
        /// Creates entity relation
        /// </summary>
        /// <param name="entity"></param>
        public bool Create(IStorageEntityRelation entityRelation)
        {
            if (entityRelation == null)
            {
                throw new ArgumentNullException("entityRelation");
            }

            var mongoEntityRelation = entityRelation as MongoEntityRelation;

            if (mongoEntityRelation == null)
            {
                throw new Exception("The specified entityRelation is not mongo storage object.");
            }

            MongoTransaction transaction = null;

            if (!MongoStaticContext.Context.TransactionInProgress)
            {
                transaction = MongoStaticContext.Context.CreateTransaction(true, new MongoTransactionOptions {
                    Isolation = MongoTransactionIsolation.ReadUncommitted
                });
            }

            lock (lockObject)
            {
                try
                {
                    cachedRelations = null;
                    var created    = MongoStaticContext.Context.EntityRelations.Create(mongoEntityRelation);
                    var relation   = Read(mongoEntityRelation.Name);
                    var recRepo    = new MongoRecordRepository();
                    var entityRepo = new MongoEntityRepository();

                    var originEntity = entityRepo.Read(relation.OriginEntityId);
                    var targetEntity = entityRepo.Read(relation.TargetEntityId);

                    recRepo.CreateRecordField(originEntity.Name, $"#{relation.Name}_targets", null);
                    recRepo.CreateRecordField(targetEntity.Name, $"#{relation.Name}_origins", null);

                    InvalidateRelationIndex(relation);

                    if (transaction != null)
                    {
                        transaction.Commit();
                    }

                    cachedRelations = null;
                    return(created);
                }
                catch
                {
                    if (transaction != null)
                    {
                        transaction.Rollback();
                    }

                    throw;
                }
            }
        }
        /// <summary>
        /// Deletes entity relation
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Delete(Guid id)
        {
            lock (lockObject)
            {
                MongoTransaction transaction = null;
                if (!MongoStaticContext.Context.TransactionInProgress)
                {
                    transaction = MongoStaticContext.Context.CreateTransaction();
                }

                try
                {
                    var relation = Read(id);

                    var recRepo      = new MongoRecordRepository();
                    var entityRepo   = new MongoEntityRepository();
                    var originEntity = entityRepo.Read(relation.OriginEntityId);
                    var targetEntity = entityRepo.Read(relation.TargetEntityId);

                    recRepo.RemoveRecordField(originEntity.Name, $"#{relation.Name}_targets");
                    recRepo.RemoveRecordField(targetEntity.Name, $"#{relation.Name}_origins");

                    InvalidateRelationIndex(relation, dropIndexes: true);

                    var result = MongoStaticContext.Context.EntityRelations.Delete(Query.EQ("_id", id));

                    if (transaction != null)
                    {
                        transaction.Commit();
                    }

                    cachedRelations = null;
                    return(result);
                }
                catch
                {
                    if (transaction != null)
                    {
                        transaction.Rollback();
                    }

                    throw;
                }
            }
        }
        /// <summary>
        /// Deletes many to many relation record
        /// </summary>
        /// <param name="relationId"></param>
        /// <param name="originId"></param>
        /// <param name="targetId"></param>
        public void DeleteManyToManyRecord(Guid relationId, Guid originId, Guid targetId)
        {
            var entityRepository = new MongoEntityRepository();
            var relation         = Read(relationId);
            var originEntity     = entityRepository.Read(relation.OriginEntityId);
            var originField      = originEntity.Fields.Single(x => x.Id == relation.OriginFieldId);
            var targetEntity     = entityRepository.Read(relation.TargetEntityId);
            var targetField      = targetEntity.Fields.Single(x => x.Id == relation.TargetFieldId);

            var originColletion = MongoStaticContext.Context.GetBsonCollection("rec_" + originEntity.Name);
            var originFieldName = originField.Name;

            if (originFieldName == "id")
            {
                originFieldName = "_id";
            }
            var          originRecords      = originColletion.Find(Query.EQ(originFieldName, originId)).ToList();
            var          originRecordsCount = originRecords.Count();
            BsonDocument originRecord       = null;

            if (originRecordsCount == 0)
            {
                throw new StorageException("There are no record with specified origin id.");
            }
            else if (originRecordsCount > 1)
            {
                throw new StorageException("There are more than 1 record with same origin id.");
            }
            else
            {
                originRecord = originRecords[0];
                var         targetsElementName = $"#{ relation.Name}_targets";
                BsonElement bsonElement        = null;
                try { bsonElement = originRecord.GetElement(targetsElementName); } catch { }
                if (bsonElement != null)
                {
                    var targets = BsonTypeMapper.MapToDotNetValue(bsonElement.Value) as List <object>;
                    if (targets != null && targets.Contains(targetId))
                    {
                        targets.Remove(targetId);
                        if (targets.Count == 0)
                        {
                            targets = null;
                        }
                        originRecord[targetsElementName] = BsonTypeMapper.MapToBsonValue(targets);
                    }
                }
                else
                {
                    originRecord[targetsElementName] = BsonTypeMapper.MapToBsonValue(null);
                }
            }

            var targetColletion = MongoStaticContext.Context.GetBsonCollection("rec_" + targetEntity.Name);
            var targetFieldName = targetField.Name;

            if (targetFieldName == "id")
            {
                targetFieldName = "_id";
            }
            var          targetRecords      = targetColletion.Find(Query.EQ(targetFieldName, targetId)).ToList();
            var          targetRecordsCount = targetRecords.Count();
            BsonDocument targetRecord       = null;

            if (targetRecordsCount == 0)
            {
                throw new StorageException("There are no record with specified target id.");
            }
            else if (targetRecordsCount > 1)
            {
                throw new StorageException("There are more than 1 record with same target id.");
            }
            else
            {
                targetRecord = targetRecords[0];
                var         originsElementName = $"#{ relation.Name}_origins";
                BsonElement bsonElement        = null;
                try { bsonElement = targetRecord.GetElement(originsElementName); } catch { }
                if (bsonElement != null)
                {
                    var origins = BsonTypeMapper.MapToDotNetValue(bsonElement.Value) as List <object>;
                    if (origins != null && origins.Contains(originId))
                    {
                        origins.Remove(originId);
                        if (origins.Count == 0)
                        {
                            origins = null;
                        }
                        targetRecord[originsElementName] = BsonTypeMapper.MapToBsonValue(origins);
                    }
                }
                else
                {
                    targetRecord[originsElementName] = BsonTypeMapper.MapToBsonValue(null);
                }
            }

            MongoTransaction transaction = null;

            if (!MongoStaticContext.Context.TransactionInProgress)
            {
                transaction = MongoStaticContext.Context.CreateTransaction();
            }

            try
            {
                originColletion.Save(originRecord);
                targetColletion.Save(targetRecord);

                if (transaction != null)
                {
                    transaction.Commit();
                }
            }
            catch
            {
                if (transaction != null)
                {
                    transaction.Rollback();
                }

                throw;
            }
        }