/// <summary>
        /// Reads next batch of entities from the file
        /// </summary>
        /// <returns>Next batch of entities</returns>
        /// <remarks>
        /// Batch means a set of related entities.
        /// It can be one <see cref="SingleRecordBulkEntity"/>, one <see cref="MultiRecordBulkEntity"/> containing its child entities or a set of related child entities (for example several <see cref="BulkSiteLink"/>s logically belonging to the same SiteLink AdExtension.
        /// </remarks>
        private IEnumerable <BulkEntity> ReadNextBatch()
        {
            // Parse the next row in the file. The returned object can be:
            // * Object inherited from SingleLineBulkEntity - representing an entity from a single file line, such as BulkCampaign or BulkKeyword or BulkSiteLink
            // * Object interited from BulkEntityIdentifier with Status = Deleted - representing a delete all row
            var nextObject = _bulkStreamReader.Read();

            // If returned object is null means we have reached the end of file
            if (nextObject == null)
            {
                return(null);
            }

            // If returned object is logically part of multiline entity (for example BulkSiteLink is logically part of multiline BulkSiteLinkAdExtension)
            if (nextObject.CanEncloseInMultilineEntity)
            {
                // Create multiline object containing the current child object
                var multilineEntity = nextObject.EncloseInMultilineEntity();

                // Read related data for the multiline object (will read other child objects belonging to the same parent)
                multilineEntity.ReadRelatedDataFromStream(_bulkStreamReader);

                if (_isForFullDownload)
                {
                    // If the file is from full download, multiline object always represents fully constructed entity, regardless of the delete row presence
                    return(new[] { multilineEntity });
                }

                // Otherwise, either return the multiline entity itself or its child objects (depending on if the multiline entity is fully constructed (has all child objects), which is determined by the presence of the delete all row)
                return(ExtractChildEntitiesIfNeeded(multilineEntity));
            }

            // If the current object is not part of multiline entity (it must be at least BulkEntity), just return it
            var bulkEntity = nextObject as BulkEntity;

            if (bulkEntity != null)
            {
                return(new[] { bulkEntity });
            }

            // Something went wrong and we got an unexpected object from the file at this point...
            throw new InvalidOperationException("Invalid bulk object returned.");
        }