Я пишу программу, которая может использовать БД SQLite или БД MySql в качестве альтернативы. (В зависимости от того, нужно ли его использовать нескольким пользователям, сетевой инфраструктуре и т. Д.)
Я написал общий интерфейс DBType и два класса, которые реализуют его в зависимости от типа БД. Есть функция DataTable GetAllRows(tableName)
, которая, как вы ожидаете, просто извлекает все строки таблицы и заполняет System.Data.DataTable. Для SQLite я использовал соединитель SQLIte
и для MySql я использовал MySql разъем .
Теперь проблема в том, что когда я читаю строки с помощью специального DataReader (поставляемого из соединителей), DataTable устанавливает типы столбцов с типами, возвращаемыми из этих соединителей. Соединитель SQLite использует System.DateTime
для полей DateTime, MySql использует MySql.Data.Types.MySqlDateTime
.
Я также реализовал структуру провайдера сущностей, поэтому я неявно преобразую каждый полученный DataRow в сущность (по требованию), чтобы поля DateTime превратились в поле DateTime в соответствующий класс сущности.
Например, предположим, что в БД есть таблица:
userTable:
field "userName" as string,
field "date" as DateTime
с одной строкой:
"asd" 2000/01/01
Я вызываю GetAllRows для этой таблицы, теперь у меня есть DataTable с 2 столбцами:
первый столбец является строковым полем, второй столбец - System.DateTime или MySqlDateTime в зависимости от типа БД, которую я использую для этого прогона.
Строка будет преобразована в сущность
class userTableEntity
{
string name;
DateTime data;
/* methods... */
implicit operator userTableEntity(DataRow row);
}
userTableEntity u = datarow; //uses the implicit operator
Если я хочу вернуться и преобразовать сущность в DataRow, я должен использовать что-то вроде этого:
MyDataRow[userFieldName] = entity.name;
MyDataRow[dataFieldName] = entity.data;
Если я использую SQLite, это не проблема. Если я использую MySql, строка MyDataRow[dataFieldName] = entity.data
выдаст исключение типа, потому что MyDataRow[dataFieldName]
имеет тип MySqlDateTime
, а entity.data представляет собой System.DateTime
Конечно, я могу переключаться между SQLite / MySql перед назначением поля, но у меня есть что-то вроде 75 таблиц с большим количеством полей DateTime, поэтому я не хочу создавать случай переключения (или if) каждый раз по очевидным причинам.
Я пытался создать класс MyDateTime, который выполняет внутреннее преобразование с такими операторами:
implicit operator DateTime
implicit operator MySql.Data.Types.MySqlDateTime
но эти операторы никогда не будут вызваны, потому что тип выражения MyDataRow[dataFieldName]
равен object
, а не DateTime
или MySqlDateTime
! Также я не могу написать неявный оператор для объекта, потому что класс MyDateTime является подтипом объекта, поэтому он не разрешен.
Обратите внимание, что MySql.Data.Types.MySqlDateTime
имеет несколько полезных методов:
public static explicit operator DateTime(MySqlDateTime val);
public DateTime GetDateTime();
public DateTime Value { get; }
и ктор:
public MySqlDateTime(DateTime dt);
Как я могу организовать код, чтобы сделать что-то вроде MyDataRow[dataFieldName] = entity.data
без исключения? Я могу изменить тип entity.data, но не хочу каждый раз переключать регистр, и я предпочитаю поддерживать DataTable во время его чтения (без изменения типа в столбцах).