Шаблон проектирования для импорта данных из различных типов источников и в различные типы назначения

14

Я должен спроектировать и построить скрипт импорта (в C #), который может обрабатывать следующее:

  • читать данные из разных источников (XML, XSLX, CSV)
  • проверить данные
  • записать данные в различные типы объектов (клиент, адрес)

Данные будут поступать из нескольких источников, но источник всегда будет иметь один формат импорта (csv, xml, xslx). Форматы импорта могут варьироваться от источника к источнику. Новые форматы импорта могут быть добавлены в будущем. Типы объектов назначения всегда одинаковы (клиент, адрес и некоторые другие).

Я думал об использовании дженериков и кое-что прочитал о заводском паттерне, но я довольно большой новичок в этой области, поэтому любые советы более чем приветствуются.

Каков подходящий шаблон проектирования для решения этой проблемы?

Джао
источник
Будь проще.
NoChance

Ответы:

11

Вы идете за борт с причудливыми концепциями слишком рано. Дженерики - когда вы видите дело, используйте их, но не волнуйтесь. Фабричный шаблон - слишком большой гибкости (и добавленной путаницы) для этого пока нет.

Будь проще. Используйте фундаментальные практики.

  1. Попробуйте представить себе что-то общее между чтением для XML и чтением для CSV. Такие вещи, как следующая запись, следующая строка. Поскольку могут быть добавлены новые форматы, постарайтесь представить общность, которая будет иметь определенный формат с известными. Используйте эту общность и определите «интерфейс» или контракт, которому должны соответствовать все форматы. Хотя они придерживаются общих позиций, у всех них могут быть свои особые внутренние правила.

  2. Для проверки данных попробуйте предоставить способ легко подключать новые или разные блоки кода валидатора. Итак, еще раз, попытайтесь определить интерфейс, в котором каждый валидатор, отвечающий за определенный вид построения данных, придерживается контракта.

  3. Для создания конструкций данных вы, вероятно, будете ограничены тем, кто разрабатывает предлагаемые выходные объекты больше всего. Попытайтесь выяснить, каков следующий шаг для объектов данных, и есть ли какие-либо оптимизации, которые вы можете сделать, зная конечное использование. Например, если вы знаете, что объекты будут использоваться в интерактивном приложении, вы могли бы помочь разработчику этого приложения, предоставив «суммы» или подсчеты объектов или другие виды производной информации.

Я бы сказал, что большинство из них - это шаблоны шаблонов или шаблоны стратегий. Весь проект был бы шаблоном Адаптера.

Энди Смит
источник
+1, особенно для первого абзаца (и приятно видеть, что вы пришли к тому же выводу, что и я в последнем абзаце).
Док Браун
Также имейте в виду архитектуру всего проекта, чтобы адаптировать один формат к другому. Можете ли вы представить себе ситуацию, когда кто-то может использовать только одну часть этого в другом проекте? Например, возможно, на рынке появится новый валидатор данных, который работает только с SQL-сервером. Итак, теперь вы просто не хотите читать пользовательский XML и вставлять SQL-сервер, пропуская остальные шаги.
Andyz Smith
Чтобы облегчить это, у частей не только должны быть свои внутренние контракты, которых они придерживаются, должен быть набор контрактов, которые определяют взаимодействие между частями .
Andyz Smith
@AndyzSmith - у меня та же проблема в моем коде. Я понял все о вашем коде, кроме шаблона адаптера. Когда вы сказали, что весь проект является примером шаблона адаптера, можете ли вы это проиллюстрировать?
gansub
9

Очевидная вещь - применить шаблон «Стратегия» . Иметь общий базовый класс ReadStrategyи для каждого входного формата свой подкласс, например XmlReadStrategy, CSVReadStrategyи т. Д. Это позволит вам изменять обработку импорта независимо от обработки проверки и обработки вывода.

В зависимости от деталей может также быть возможно сохранить большинство частей общего импорта и обмениваться только частями обработки ввода (например, чтение одной записи). Это может привести вас к шаблону шаблона метода .

Док Браун
источник
Означает ли это, что при использовании паттерна стратегии мне приходится создавать отдельные методы для преобразования объектов (клиента, адреса) из источника в пункт назначения. Я хотел бы прочитать, преобразовать и проверить каждый объект и поместить его в список, чтобы впоследствии этот список можно было сохранить в базе данных.
Джао
@jao: хорошо, если вы прочитаете мой ответ снова, вы увидите, что я предложил создать «ReadStrategy», а не «ConvertStrategy». Таким образом, вам нужно только написать разные методы для чтения объектов (или любой другой части вашего процесса, индивидуальной для конкретного формата файла).
Док Браун
7

Подходящим шаблоном для утилиты импорта, который может потребоваться в будущем, будет использование MEF - вы можете сохранить низкое использование памяти, загружая нужный преобразователь на лету из ленивого списка, создавая импорт MEF, который украшен атрибутами это помогает выбрать правильный конвертер для импорта, который вы пытаетесь выполнить, и обеспечивает простой способ выделения различных классов импорта.

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

MEF - это фреймворк для создания архитектуры плагинов - его внешний вид и Visual Studio - все эти прекрасные расширения в VS являются частями MEF.

Чтобы создать приложение MEF (Managed Extensability Framework), начните с включения ссылки на System.ComponentModel.Composition

Определите интерфейсы, чтобы определить, что будет делать конвертер

public interface IImportConverter
{
    int UserId { set; }        
    bool Validate(byte[] fileData, string fileName, ImportType importType);
    ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}

Это можно использовать для всех типов файлов, которые вы хотите импортировать.

Добавьте атрибуты в новый класс, которые определяют, что класс будет «экспортировать»

[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
 ...interface methods...
}

Это определит класс, который будет импортировать файлы CSV (определенного формата: Format1) и имеет настраиваемые атрибуты, которые устанавливают метаданные атрибутов экспорта MEF. Вы бы повторили это для каждого формата или типа файла, который вы хотите импортировать. Вы можете установить пользовательские атрибуты с помощью класса:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ImportAttribute : ExportAttribute
{
    public ImportAttribute(ImportType importType, ImportFileType fileType, string customerUID)
        : base(typeof(IImportConverter))
    {
        ImportType = importType;
        FileType = fileType;
        CustomerUID = customerUID;
    }

    public ImportType ImportType { get; set; }
    public ImportFileType FileType { get; set; }
    public string CustomerUID { get; set; }
}

Чтобы использовать преобразователи MEF, вам необходимо импортировать части MEF, которые вы создаете при запуске кода преобразования:

[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();

catalog собирает детали из папки, по умолчанию это местоположение приложения.

converters это ленивый список импортируемых деталей MEF

Затем, когда вы знаете, какой тип файла вы хотите конвертировать ( importFileTypeи importType), получите конвертер из списка импортированных деталей вconverters

var tmpConverter = (from x in converters
                    where x.Metadata.FileType == importFileType
                    && x.Metadata.ImportType == importType 
                    && (x.Metadata.CustomerUID == import.ImportDataCustomer.CustomerUID)
                    select x).OrderByDescending(x => x.Metadata.CustomerUID).FirstOrDefault();

if (tmpConverter != null)
{
     var converter = (IImportConverter)tmpConverter.Value;
     result = converter.ImportData(import.ImportDataFile, import.ImportDataFileName, importType);
....
}

При вызове converter.ImportDataбудет использоваться код в импортированном классе.

Может показаться, что кода много, и может потребоваться некоторое время, чтобы разобраться в происходящем, но он чрезвычайно гибок, когда дело доходит до добавления новых типов конвертеров, и может даже позволить вам добавлять новые во время выполнения.

Matt
источник
Я не слышал о MEF раньше. Что это?
Джао
2
@jao проверить ссылку для полного объяснения. Добавил несколько примеров MEF в мой ответ.
Мэтт
1
Это отличный способ начать MEF. +1
пакогомез
MEF - это технология, а не шаблон дизайна. Нет -1от меня, поскольку основная идея все еще имеет смысл и опирается на шаблон стратегии, управляемый IImportConverterинтерфейсом.
GETah
0

Каков подходящий шаблон проектирования для решения этой проблемы?

В идиомах C # для этого используется встроенная среда сериализации. Вы аннотируете объекты с помощью метаданных, а затем создаете экземпляры различных сериализаторов, которые используют эти аннотации для извлечения данных, чтобы поместить их в правильную форму, или наоборот.

Xml, JSON и двоичные формы являются наиболее распространенными, но я не удивлюсь, если другие уже существуют в красивой упакованной форме для использования.

Telastyn
источник
Что ж, это хорошо работает, если вы можете использовать свой собственный формат файла, но я думаю, что этот подход потерпит неудачу для сложных предопределенных форматов, таких как XSLX, что означает файлы MS Excel в сжатом формате XML.
Док Браун
Я могу сопоставить строку файла Excel с объектом, но мне нужно будет скопировать и адаптировать этот метод для читателей XML и CSV. И я хотел бы сохранить код как можно более чистым ...
Джао
@docBrown - как? Концептуально, превращение объекта в серию ячеек в Excel на самом деле ничем не отличается от превращения его в документ XML.
Теластин
@Telastyn: вы говорите, что можете использовать встроенную платформу сериализации .NET Framework для чтения формата XLSX? Если это так, то библиотеки типа Open XML SDK или NPOI устарели.
Док Браун
@docbrown: мои извинения, вы правы - я постоянно забываю, что нет общего базового класса сериализатора, так как это одна из первых вещей, которая делается в любой кодовой базе, в которой я работаю.
Telastyn