# Получение данных из внешних источников. CSV. ## Реализация поставщика данных из CSV файла 1. Для начала создаём файл с данными для импорта. Я продолжаю тему *кошек*, вы реализуете свою предметную область: >Мы должны уметь работать с разными типами данных, поэтому я добавил в исходные данные тип **Date** (дата прививки). Формат даты имеет множество разных форматов, но при экспорте обычно используют SQL формат (`yyyy-mm-dd`) ```csv age,breed,color,name,photo,dateOfVaccination 1,"Дворняжка","Белый","Ириска",,2024-04-29 2,"Шотландская вислоухая","Коричневый","Изи",,2020-01-31 3,"Сиамский","Цветной","Макс",,2022-05-10 ``` * в первой строке названия полей, соответствующие модели данных * разделитель запятая * строковые литералы (текст, в котором могут встретиться спец.символы, а лучше все текстовые поля) заключаем в двойные кавычки * для отсутствующих полей (у меня `photo`) оставляем пустую строку Файл сохраняем в подкаталог `bin/debug/net8.0-windows` вашего проекта - туда, где создается исполняемый (.exe) файл (название зависит от версии .NET и платформы). 1. Правим модель (у нас добавилось свойство "Дата прививки") ```cs public class Cat { public string name { get; set; } public int age{ get; set; } public string color { get; set; } // порода public string breed { get; set; } public string photo { get; set; } // новое свойство public DateOnly dateOfVaccination { get; set; } } ``` Обратите внимание, я использую тип данных **DateOnly** - это дата без времени 1. Загружаем данные из файла в класс поставщика данных >Тут есть два варианта: либо каждый раз загружать данные при вызове метода **getCats**, либо загрузить один раз при создании экземпляра класса (в конструкторе). Оба варианта имеют право на жизнь: первый имеет смысл применять, если данные часто меняются, второй, если данные статичны. Я реализую второй вариант. [Вспоминаем материалы лекции про типы данных](./t5_file_types.md#csv) и создаём для работы с CSV файлами класс **CSVDataProvider**, реализующий интерфейс **IDataProvider**: * Создаём приватную переменную для хранения загруженного списка и считываем данные в конструкторе: Библиотека **CsvHelper** достаточно "умная" и преобразует строку в дату автоматически Но в то же время они и "слишком умная", если данные не соответсвуют модели (не хватает полей) или типу (например, в дате нет времени), то запись игнорируется ```cs public class СSVDataProvider : IDataProvider { private List catList; // конструктор класса public CSVDataProvider() { using (var reader = new StreamReader("./cat.csv")) { using (var csv = new CsvReader( reader, CultureInfo.InvariantCulture)) { // CsvHelper использует отложенное чтение через // yeld, поэтому сразу преобразуем в список catList = csv.GetRecords().ToList(); } } } } ``` * реализуем интерфейс **IDataProvider** (пишем метод **getCats**): ```cs public IEnumerable getCats() { return catList; } ``` 1. Теперь в конструкторе окна меняем класс провайдера и всё продолжает работать ```cs public MainWindow() { InitializeComponent(); DataContext = this; // Globals.dataProvider = new LocalDataProvider(); Globals.dataProvider = new CSVDataProvider(); CatList = Globals.dataProvider.getCats(); } ``` --- ## ЗАДАНИЕ 1. Подготовить набор данных из вашей предметной области (не менее 10 записей) с разными типами (обязательно должны быть: **Int**, **Double**, **String**, **DateTime**, **Boolean**) 1. Реализовать класс **CSVDataProvider** для своей предметной области 1. Все поля вывести в **ListBox**-e. Для даты указать формат вывода: `Binding="{Binding dateOfVaccination,StringFormat='dd.MM.yyyy'}"` (не помню, проверял ли я такой вариант, если не заработает, то используйте вариант с переопределением метода _ToString_)