Язык программирования C#9 и платформа .NET5
Исследование класса SqlBulkCopy
Класс
имеет один метод,SqlBulkCopy(и его асинхронную версиюWriteToServer()), который обрабатывает список записей и помещает данные в базу более эффективно, чем последовательность операторовWriteToServerAsync(), выполненная с помощью объектов команд. МетодInsertперегружен, чтобы принимать объектWriteToServer(), объектDataTableили массив объектовDataReader. Придерживаясь тематики главы, мы собираемся использовать версиюDataRow, которая принимаетWriteToServer(), так что необходимо создать специальный класс чтения данных.DataReaderСоздание специального класса чтения данных
Желательно, чтобы специальный класс чтения данных был обобщенным и содержал список моделей, которые нужно импортировать. Создайте в проекте
новую папку по имениAutoLot.DAL, a в ней — новый файл интерфейсаBulkImport, реализующегоIMyDataReader.cs, со следующим кодом:IDataReaderusing System.Collections.Generic;using System.Data;namespace AutoLot.Dal.BulkImport{public interface IMyDataReader<T> : IDataReader{List<T> Records { get; set; }}}Далее реализуйте специальный класс чтения данных. Как вы уже видели, классы чтения данных содержат много частей, отвечающих за перемещение данных. Хорошая новость в том, что для
придется реализовать лишь несколько из них. Создайте новый файл класса по имениSqlBulkCopyи добавьте в него перечисленные ниже операторыMyDataReader.cs:usingusing System;using System.Collections.Generic;using System.Data;using System.Linq;using System.Reflection;Сделайте класс открытым и запечатанным и обеспечьте реализацию классом интерфейса
. Добавьте конструктор для принятия записей и установки свойства:IMyDataReaderpublic sealed class MyDataReader<T> : IMyDataReader<T>{public List<T> Records { get; set; }public MyDataReader(List<T> records){Records = records;}}Предложите Visual Studio или Visual Studio Code самостоятельно реализовать все методы (либо скопировать их), что даст вам отправную точку для специального класса чтения данных. В рассматриваемом сценарии потребуется реализовать лишь члены, кратко описанные в табл. 21.7.
Начните с метода
, который возвращаетRead(), если класс для чтения находится в конце списка, илиfalse(с инкрементированием счетчика уровня класса), если конец списка еще не достигнут. Добавьте переменную уровня класса, которая будет хранить текущий индексtrue, и обновите методList<T>, как показано ниже:Read()public class MyDataReader<T> : IMyDataReader<T>{...private int _currentIndex = -1;public bool Read(){if (_currentIndex + 1 >= Records.Count){return false;}_currentIndex++;return true;}}Каждый метод
и свойствоGetXXX()требуют знания специфической модели, подлежащей загрузке. Вот как выглядит методFieldCount, использующийGetValue():CarViewModelpublic object GetValue(int i){Car currentRecord = Records[_currentIndex] as Car;return i switch{0 => currentRecord.Id,1 => currentRecord.MakeId,2 => currentRecord.Color,3 => currentRecord.PetName,4 => currentRecord.TimeStamp,_ => string.Empty,};}База данных содержит только четыре таблицы, но это означает необходимость в наличии четырех вариаций класса чтения данных. А подумайте о реальной производственной базе данных, в которой таблиц гораздо больше!Решить проблему можно более эффективно с применением рефлексии (см. главу 17) и LINQ to Objects (см. главу 13).
Добавьте переменные
для хранения значенийreadonlyмодели и словарь, который будет использоваться для хранения местоположения поля и имени таблицы в SQL Server. Модифицируйте конструктор, чтобы он принимал свойства обобщенного типа и инициализировал объектPropertyInfo. Ниже показан добавленный код:Dictionary
