Шрифт:
Интервал:
Закладка:
Исследование класса SqlBulkCopy
Класс SqlBulkCopy имеет один метод, WriteToServer() (и его асинхронную версию WriteToServerAsync()), который обрабатывает список записей и помещает данные в базу более эффективно, чем последовательность операторов Insert, выполненная с помощью объектов команд. Метод WriteToServer() перегружен, чтобы принимать объект DataTable, объект DataReader или массив объектов DataRow. Придерживаясь тематики главы, мы собираемся использовать версию WriteToServer(), которая принимает DataReader, так что необходимо создать специальный класс чтения данных.
Создание специального класса чтения данных
Желательно, чтобы специальный класс чтения данных был обобщенным и содержал список моделей, которые нужно импортировать. Создайте в проекте AutoLot.DAL новую папку по имени BulkImport, a в ней — новый файл интерфейса IMyDataReader.cs, реализующего IDataReader, со следующим кодом:
using System.Collections.Generic;
using System.Data;
namespace AutoLot.Dal.BulkImport
{
public interface IMyDataReader<T> : IDataReader
{
List<T> Records { get; set; }
}
}
Далее реализуйте специальный класс чтения данных. Как вы уже видели, классы чтения данных содержат много частей, отвечающих за перемещение данных. Хорошая новость в том, что для SqlBulkCopy придется реализовать лишь несколько из них. Создайте новый файл класса по имени MyDataReader.cs и добавьте в него перечисленные ниже операторы using:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
Сделайте класс открытым и запечатанным и обеспечьте реализацию классом интерфейса IMyDataReader. Добавьте конструктор для принятия записей и установки свойства:
public 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.
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})Начните с метода 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(), использующий CarViewModel:
public 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 для хранения значений PropertyInfo модели и словарь, который будет использоваться для хранения местоположения поля и имени таблицы в SQL Server. Модифицируйте конструктор, чтобы он принимал свойства обобщенного типа и инициализировал объект Dictionary. Ниже показан добавленный код:
- Понимание SQL - Мартин Грубер - Базы данных