0

I have a class to process an excel file, with multiple sheets. The class is called ExcelFileProcessor (inherits from IFileProcessor). My current implementation is within the ExcelFileProcessor class, I have different methods to process different sheets.

  private void GenerateClientDataObj()
    {
        this.ReadLocationsFromFile();
        this.ReadOpHoursFromFile();
        this.ReadHolidaysFromFile();
        this.ReadProductsServicesFromFile();
        this.ReadUsersFromFile();

        // set the error list
        clientDataObj.ErrorList = importDataErrors;
    }

I wanted to structure this code and decided to have a ISheetProcessor abstract class to process different sheets in the excel file. This class will have two methods:

// check if the given sheet exists in the excel file
ValidateSheet();

// process the given sheet and read the content in to an object in memory
ProcessSheet();

Since I have multiple sheets, I will have to create different instances of the ISheetProcessor within the ExcelFileProcessor or inject them. Can someone tell me the best way to do that?

I use the strategy pattern to decide which file processor to use. The code is given below:

In the controller:

        var fileExtension = fileUtility.FileExtension;

        // Pass the control to the appropriate file processor to read the file content
        ProvisioningContext provisioningContext;

        switch (fileExtension)
        {
            case ".xls":
            case ".xlsx":
                provisioningContext = new ProvisioningContext(new ExcelFileProcessor(), clientId, sheetsToProcess, fileUtility);
                break;
            case ".csv":
                provisioningContext = null;
                break;
            default:
                provisioningContext = null;
                break;
        }

        return provisioningContext.ProcessFileContent();

ProvisioningContext class:

  public ProvisioningContext(IFileProcessor fileProcessor, int clientId, IList<int> sheets, FileUtil fileUtil)
    {
        this.fileProcessor = fileProcessor;
    }

 public IList<ImportDataError> ProcessFileContent()
    {
        try
        {
            string[] filesArray = new string[fileUtil.FileCount];

            filesArray[0] = fileUtil.GenerateFileName();

            // proces the file content, validate and store in the database.
            fileContent = fileProcessor.ReadContentFromFile(filesArray, sheetsToProcess);                
            SaveClientData();

            // return the list of errors to the user
            return fileContent.ErrorList;
        }
        catch (IOException ioException)
        {
            eventLogRepository.LogEvent(ioException, clientId, EventLogActionEnum.Provisioning_FileRead, EventLogSourceEnum.WebApplication);
            throw;
        }
        catch(IndexOutOfRangeException indexOutOfRangeException)
        {
            eventLogRepository.LogEvent(indexOutOfRangeException, clientId, EventLogActionEnum.Provisioning_FileRead, EventLogSourceEnum.WebApplication);
            throw;
        }
        catch (FormatException formatException)
        {
            eventLogRepository.LogEvent(formatException, clientId, EventLogActionEnum.Provisioning_FileRead, EventLogSourceEnum.WebApplication);
            throw;
        }
        catch (Exception ex)
        {
            eventLogRepository.LogEvent(ex, clientId, EventLogActionEnum.Provisioning_FileRead, EventLogSourceEnum.WebApplication);
            throw;
        }
    }

In the ExcelFileProcessor class:

  public override ClientData ReadContentFromFile(string[] filePaths, IList<int> sheets = null)
    {
        // for Excel reader, there will only be one file.
        this.OpenClientDataFile(filePaths[0]);

        // perform initial validations on the file being read to ensure the necessay worksheets are available
        string fileErrors = this.ValidateFile(sheets);

        if (!string.IsNullOrEmpty(fileErrors))
        {
            throw new InvalidOperationException(fileErrors);
        }

        this.GenerateClientDataObj();

        return clientDataObj;
    }
devC
  • 1,384
  • 5
  • 32
  • 56
  • 1
    I wrote a similar thing where I either needed to write to Http or CSV, they both have completely different requirements and implementations, yet I get them behind the same interface. Take a look if you want (it's not completely finished/perfect) https://github.com/no1melman/ElasticSearchPopulator/tree/master/src/ElasticSearchPopulator.Core – Callum Linington Jul 21 '16 at 12:48
  • Let's say you already had the `ISheetProcessor` instances in hand, regardless of how you create the different instances, what would be the logic required for choosing the correct one to use for a given sheet? – blins Jul 21 '16 at 12:51
  • @blins: There are two options, the user may want to process all sheets in the file, or selected sheets. So in the first case, I need to process all sheets (locations, services, holidays, etc..) or else only 1 or 2 of them, based on the selection. What I do is, I pass the selected sheets in to the API call. – devC Jul 22 '16 at 04:36

0 Answers0