Книги онлайн и без регистрации » Разная литература » Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 261 262 263 264 265 266 267 268 269 ... 407
Перейти на страницу:
сценарии потребуется реализовать лишь члены, кратко описанные в табл. 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(), использующий 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. Ниже показан добавленный код:

private readonly PropertyInfo[] _propertyInfos;

private readonly Dictionary<int, string> _nameDictionary;

public MyDataReader(List<T> records)

{

  Records = records;

  _propertyInfos = typeof(T).GetProperties();

  _nameDictionary = new Dictionary<int,string>();

}

Модифицируйте конструктор, чтобы он принимал строку подключения SQLConnection, а также строки для имен схемы и таблицы, куда будут вставлены записи, и добавьте для этих значений переменные уровня класса:

private readonly SqlConnection _connection;

private readonly string _schema;

private readonly string _tableName;

public MyDataReader(List<T> records, SqlConnection connection,

                    string schema, string tableName)

{

  Records = records;

  _propertyInfos = typeof(T).GetProperties();

  _nameDictionary = new Dictionary<int, string>();

  _connection = connection;

  _schema = schema;

  _tableName = tableName;

}

Далее реализуйте метод GetSchemaTable(), который извлекает информацию SQL Server, касающуюся целевой таблицы:

public DataTable GetSchemaTable()

{

  using var schemaCommand =

    new SqlCommand($"SELECT * FROM {_schema}.{_tableName}", _connection);

  using var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly);

  return reader.GetSchemaTable();

}

Модифицируйте конструктор, чтобы использовать SchemaTable для создания словаря, который содержит поля целевой таблицы в порядке их следования внутри базы данных:

public MyDataReader(List<T> records, SqlConnection connection,

                    string schema, string tableName)

{

  ...

  DataTable schemaTable = GetSchemaTable();

  for (int x = 0; x<schemaTable?.Rows.Count;x++)

  {

    DataRow col = schemaTable.Rows[x];

    var columnName = col.Field<string>("ColumnName");

    _nameDictionary.Add(x,columnName);

  }

}

Теперь показанные далее методы могут быть реализованы обобщенным образом, используя полученную посредством рефлексии информацию:

public int FieldCount => _propertyInfos.Length;

public object GetValue(int i)

  => _propertyInfos

      .First(x=>x.Name.Equals(_nameDictionary[i],

                              StringComparison.OrdinalIgnoreCase))

      .GetValue(Records[_currentIndex]);

Для справки ниже приведены остальные методы, которые должны присутствовать (но не реализованы):

public string GetName(int i) => throw new NotImplementedException();

public int GetOrdinal(string name) => throw new NotImplementedException();

public string GetDataTypeName(int i) => throw new NotImplementedException();

public Type GetFieldType(int i) => throw new NotImplementedException();

public int GetValues(object[] values) => throw new NotImplementedException();

public bool GetBoolean(int i) => throw new NotImplementedException();

public byte GetByte(int i) => throw new NotImplementedException();

public long GetBytes(int i, long fieldOffset, byte[] buffer,

  int bufferoffset, int length)

  => throw new NotImplementedException();

public char GetChar(int i) => throw new NotImplementedException();

public long GetChars(int i, long fieldoffset, char[] buffer,

   int bufferoffset, int length)

   => throw new NotImplementedException();

public Guid GetGuid(int i) => throw new NotImplementedException();

public short GetInt16(int i) => throw new NotImplementedException();

public int GetInt32(int i) => throw new NotImplementedException();

public long GetInt64(int i) => throw new NotImplementedException();

public float GetFloat(int i) => throw new NotImplementedException();

public double GetDouble(int i)  => throw new NotImplementedException();

public string GetString(int i) => throw new NotImplementedException();

public decimal GetDecimal(int i) => throw new NotImplementedException();

public DateTime GetDateTime(int i) => throw new NotImplementedException();

public IDataReader GetData(int i) => throw new NotImplementedException();

public bool IsDBNull(int i) => throw new NotImplementedException();

object IDataRecord.this[int i] => throw new NotImplementedException();

object IDataRecord.this[string name] => throw new NotImplementedException();

public void Close() => throw new NotImplementedException();

public DataTable GetSchemaTable() => throw new NotImplementedException();

public bool NextResult() => throw new NotImplementedException();

public int Depth { get; }

public bool IsClosed { get; }

public int RecordsAffected { get; }

Выполнение массового копирования

Добавьте в папку BulkImport новый файл открытого статического класса по имени ProcessBulkImport.cs. Поместите в начало файла следующие операторы using:

using System;

using System.Collections.Generic;

using System.Data;

using System.Linq;

using Microsoft.Data.SqlClient;

Добавьте код для поддержки открытия и закрытия подключений (похожий на код в классе InventoryDal):

private const string ConnectionString =

  @"Data Source=.,5433;User Id=sa;Password=P@ssw0rd;Initial Catalog=AutoLot";

private static SqlConnection _sqlConnection = null;

private static void OpenConnection()

{

  _sqlConnection = new SqlConnection

  {

    ConnectionString = ConnectionString

  };

  _sqlConnection.Open();

}

private static

1 ... 261 262 263 264 265 266 267 268 269 ... 407
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. В коментария нецензурная лексика и оскорбления ЗАПРЕЩЕНЫ! Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?