jeudi 21 juillet 2016

C# design strategy to read different excel sheets with an excel processor

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;
    }

Aucun commentaire:

Enregistrer un commentaire