Написание заданий ETL на чистом C# - Пример кода – объединение двух файлов в один

ОГЛАВЛЕНИЕ

Пример кода – объединение двух файлов в один

Включенный пример имеет блочные тесты для каждой операции, чтобы можно было подробно изучить каждую из них. Также можно запустить консольное приложение, выполняющее все сразу. Оно считывает два файла, объединяет их по общему свойству (Id) и записывает конечные результаты в другой текстовый файл.

Ниже приведен код главного процесса:

public class MainProcess : EtlProcess
{
    protected override void Initialize()
    {
        Register(new JoinUserRecords()
            .Left(new UserNameRead(Settings.Default.NamesFile))
            .Right(new UserAddressRead(Settings.Default.AddressesFile))
        );

        Register(new UserFullWrite(Settings.Default.OutputFile));
    }
}

Показаны первые две операции:

public class UserNameRead : AbstractOperation
{
    public UserNameRead(string filePath)
    {
        this.filePath = filePath;
    }

    string filePath = null;

    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        using (FileEngine file = FluentFile.For<UserNameRecord>().From(filePath))
        {
            foreach (object obj in file)
            {
                yield return Row.FromObject(obj);
            }
        }
    }
}

public class UserAddressRead : AbstractOperation
{
    public UserAddressRead(string filePath)
    {
        this.filePath = filePath;
    }

    string filePath = null;

    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        using (FileEngine file = FluentFile.For<UserAddressRecord>().From(filePath))
        {
            foreach (object obj in file)
            {
                yield return Row.FromObject(obj);
            }
        }
    }
}

Обратите внимание на использование FileHelpers через Rhino ETL для выполнения ввода-вывода в файл, избавляющего от необходимости разбираться с тем, как разобрать CSV, с файлом с разделителями табуляции и т.д. Он считывает строго типизированные данные C# из файлов, а затем преобразует их в объект Row, используемый Rhino ETL для передачи данных от операции к операции. Итак, эти операции создают объект строки для каждой строки во входных файлах и передают их следующей операции –  объединению:

public class JoinUserRecords : JoinOperation
{
    protected override void SetupJoinConditions()
    {
        InnerJoin
            .Left("Id")
            .Right("Id");
    }

    protected override Row MergeRows(Row leftRow, Row rightRow)
    {
        Row row = new Row();
        row.Copy(leftRow);

        //скопировать все свойства, не входящие в записи пользователя
        row["Address"] = rightRow["Address"];

        return row;
    }
}

Строки объединяются по их общему свойству в методе SetupJoinConditions, а затем надо реализовать еще один абстрактный метод, чтобы указать, что надо делать с объединенными строками. Например, может требоваться всего один столбец из объединенной строки, или могут требоваться все столбцы. Метод MergeRows управляет этим.

Конечный процесс выводит объединенные строки:

public class UserFullWrite : AbstractOperation
{
    public UserFullWrite(string filePath)
    {
        this.filePath = filePath;
    }

    string filePath = null;

    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        FluentFile engine = FluentFile.For<UserFullRecord>();
        engine.HeaderText = "Id\tName\tAddress";
        using (FileEngine file = engine.To(filePath))
        {
            foreach (Row row in rows)
            {
                file.Write(row.ToObject<UserFullRecord>());

                //при необходимости передать строки другой следующей операции
                yield return row;
            }
        }
    }
}

Снова используются FileHelpers через вспомогательные методы/классы Rhino ETL.

Напоследок

Здесь были показаны только операции с файлами, потому что их было проще распространить и заставить работать на машинах других людей. SQL, разумеется, поддерживается, и есть много операций для считывания таблицы и автоматического заполнения строк сразу. Более подробные данные о них смотрите в блочных тестах.

Настоящая мощность проявляется при выполнении более сложных задач, например, вызов веб-службы, использование объектов COM, организация поточной обработки и т.д., в задании ETL. Теперь можно использовать имеющийся набор навыков и опыт для выполнения указанных задач, а не пытаться придумать обходные пути и приемы в продукте с конструктором.