private DomainModels.ModelsBase ReadRowAsync(
            ConfigurationFile configurationFile,
            int row,
            DataRow dataRow)
        {
            DomainModels.ModelsBase toReturn = null;

            List <DomainModels.ModelsBase> modelParts =
                new List <DomainModels.ModelsBase>();

            // 3) Iterate through the configuration file and...
            DomainModels.ModelsBase modelsBase = null;
            foreach (ColumnMappingConfiguration columnMappingConfiguration in configurationFile.ColumnMappingConfigurations)
            {
                // .. instantiate new instances, based on the requested types.
                modelsBase = this.CreateAndPopulateModelsBaseInstance(
                    columnMappingConfiguration,
                    row,
                    dataRow);

                modelParts.Add(modelsBase);
            }

            // Join the models up, based on the instance type present.
            // It can only be one.
            modelsBase = modelParts
                         .SingleOrDefault(x => x is DomainModels.SchoolInformation);

            if (modelsBase != null)
            {
                // Then stitch up into a proper SchoolInformation instance.
                DomainModels.SchoolInformation schoolInformation =
                    modelsBase as DomainModels.SchoolInformation;

                schoolInformation.BaselineFunding = modelParts
                                                    .Select(x => x as DomainModels.Rates.BaselineFunding)
                                                    .Single(x => x != null);

                schoolInformation.NotionalFunding = modelParts
                                                    .Select(x => x as DomainModels.Rates.NotionalFunding)
                                                    .Single(x => x != null);

                // Illustrative funding isn't available on the 2020
                // spreadsheet.
                schoolInformation.IllustrativeFunding = modelParts
                                                        .Select(x => x as DomainModels.Rates.IllustrativeFunding)
                                                        .SingleOrDefault(x => x != null);

                toReturn = schoolInformation;
            }

            modelsBase = modelParts
                         .SingleOrDefault(x => x is DomainModels.LocalAuthorityInformation);

            if (modelsBase != null)
            {
                // Then stitch up into a property LocalAuthorityInformation
                // instance.
                DomainModels.LocalAuthorityInformation localAuthorityInformation =
                    modelsBase as DomainModels.LocalAuthorityInformation;

                localAuthorityInformation.ProvisionalFunding = modelParts
                                                               .Select(x => x as DomainModels.Rates.ProvisionalFunding)
                                                               .Single(x => x != null);

                toReturn = localAuthorityInformation;
            }

            if (modelsBase == null)
            {
                throw new NotImplementedException(
                          $"No model of type " +
                          $"{nameof(DomainModels.SchoolInformation)} present. Is " +
                          $"there a different high-level entity we need to " +
                          $"implement?");
            }

            return(toReturn);
        }
        /// <inheritdoc />
        public async Task <ProcessResponse> ProcessAsync(
            ProcessRequest processRequest,
            CancellationToken cancellationToken)
        {
            ProcessResponse toReturn = null;

            if (processRequest == null)
            {
                throw new ArgumentNullException(nameof(processRequest));
            }

            // 1) Deserialise config.
            string configFile = processRequest.ConfigFile;

            ConfigurationFile configurationFile =
                await this.configurationFileReader.ReadAsync(
                    configFile,
                    cancellationToken)
                .ConfigureAwait(false);

            // 2) Take mappings and call spreadsheet parser.
            string spreadsheetFile = processRequest.SpreadsheetFile;

            IEnumerable <DomainModels.ModelsBase> modelsBases =
                await this.spreadsheetReader.ReadAsync(
                    configurationFile,
                    spreadsheetFile,
                    cancellationToken)
                .ConfigureAwait(false);

            // 3) Take each entity and store in storage.
            // Clear storage first.
            await this.schoolInformationStorageAdapter
            .CreateTableAsync(cancellationToken)
            .ConfigureAwait(false);

            int year = configurationFile.Year;

            DomainModels.ModelsBase[] modelsBaseArray = modelsBases.ToArray();

            int length = modelsBaseArray.Length;

            DomainModels.ModelsBase                modelsBase                = null;
            DomainModels.SchoolInformation         schoolInformation         = null;
            DomainModels.LocalAuthorityInformation localAuthorityInformation = null;
            for (int i = 0; i < length; i++)
            {
                modelsBase = modelsBaseArray[i];

                if (modelsBase is DomainModels.SchoolInformation)
                {
                    schoolInformation =
                        modelsBase as DomainModels.SchoolInformation;

                    if (schoolInformation.Urn.HasValue)
                    {
                        await this.schoolInformationStorageAdapter.CreateAsync(
                            year,
                            schoolInformation,
                            cancellationToken)
                        .ConfigureAwait(false);
                    }
                    else
                    {
                        this.loggerWrapper.Warning(
                            $"A {nameof(DomainModels.SchoolInformation)} " +
                            $"instance could not be inserted, as there " +
                            $"wasn't a " +
                            $"{nameof(DomainModels.SchoolInformation.Urn)}.");
                    }
                }
                else if (modelsBase is DomainModels.LocalAuthorityInformation)
                {
                    localAuthorityInformation =
                        modelsBase as DomainModels.LocalAuthorityInformation;

                    if (localAuthorityInformation.LaNumber.HasValue)
                    {
                        await this.localAuthorityInformationStorageAdapter.CreateAsync(
                            year,
                            localAuthorityInformation,
                            cancellationToken)
                        .ConfigureAwait(false);
                    }
                    else
                    {
                        this.loggerWrapper.Warning(
                            $"A " +
                            $"{nameof(DomainModels.LocalAuthorityInformation)} " +
                            $"instance could not be inserted, as there " +
                            $"wasn't an " +
                            $"{nameof(DomainModels.LocalAuthorityInformation.LaNumber)}.");
                    }
                }
                else
                {
                    throw new NotImplementedException(
                              "Storage not configured for this type. Please " +
                              "implement.");
                }

                this.ReportInsertionProgress(i, length);
            }

            return(toReturn);
        }