Начну с того, что в течение нескольких проектов я реализовал это двумя различными способами, но никогда не был удовлетворен результатами.
ПРИМЕЧАНИЕ: я не заинтересован в использовании ORMS!
Фон
Я работал над многими проектами, для которых требовался уровень абстракции базы данных для нескольких платформ. Еще во времена классического ASP / VBScript у нас был набор функций, которые инкапсулировали текст SQL для очистки данных, а затем строили строку, подходящую для провайдера (SQL Server, MSSQL, Oracle, DB2 и так далее).
Забегая вперед несколько лет назад, я написал DAL, который заменил все эти извилистые струнные вещи на реальные объекты. Идея была проста: вы вывели класс базы данных из абстрактного класса Database для обработки открытия / закрытия и выполнения вещей в соответствии с провайдером .NET. Также были созданы объекты Insert
, Select
, Update
, которые также подходили для провайдера. По правде говоря, хотя это работало, и программное обеспечение могло работать с любой базой данных через объекты, и программисту никогда не приходилось касаться SQL; это было громоздко. Представление новой платформы базы данных заняло время для создания всех объектов.
Пример кода:
DB.Database db = DatabasePool.Get();
try
{
DB.Select select = db.NewSelect("_people"); // MSSQL/Oracle/DB2, doesn't matter
select.Add("_id");
select.Add("_people_name");
select.Where.AndClause("_people_name", DB.Operator.Like, "bob");
DB.Results results = select.ExecuteToCollection();
// here, Select is "tailored" to the right DB provider and returns the correct
// sql.
}
finally
{
DatabasePool.Release();
}
Следующая итерация несколько улучшила это. Вместо того, чтобы иметь абстрактный класс базы данных и целый набор абстрактных объектов для представления операций, был набор конкретных объектов. Затем провайдеры предоставили делегата для каждого типа. Преобразование объектов в SQL было простым случаем выброса его в Dictionary<Type, Delegate>
, и поставщик знал, как преобразовать этот конкретный объект (или даже целочисленный тип) в SQL, одновременно выполняя очистку данных. Все было хорошо, и я улучшил средства доступа к объектам:
DB.Database db = DatabasePool.Get();
try
{
DB.Select select = new Select(db); // now a sealed, concrete object
select.Add("_id").Add("_people_name");
select.Where.And("_people_name", DB.Op.Like, "bob");
DB.Results results = select.Query();
// much better now. Select is a sealed object. The Database class has a bunch of
// delegates that convert any type (including Select, Delete, int, decimal) to
// appropriate SQL. Much less work implementing different providers.
}
finally
{
DatabasePool.Release();
}
Теперь
У меня есть возможность сделать это снова (и время проекта сделать это). Я предполагаю, что мой вопрос заключается в том, является ли это хорошим способом работы с различными поставщиками, инкапсулирующими операции базы данных в объектах? В то время как делегаты работают нормально:
1) Легко написать генератор для Column
, Select
, Delete
, int
, NULL
, и так далее, но при создании предложений WHERE
я получил кучу крошечных атомные объекты, которые составляли «части» вещей.
2) Хотя разные разновидности SQL имеют небольшие различия, большинство из них одинаковы. Класс base-database предоставил приемлемые значения по умолчанию для почти всех объектов, и разные поставщики просто заменили этих делегатов. Но я не считал это очень "понятным" для программиста. Непонятно, какие из них обрабатываются каким слоем кода.
Есть ли лучший способ? Я доволен концепцией объектов, заменяющих написание любого SQL вообще. Они позволяют очень просто динамически создавать операторы SQL с очисткой и соответствующим преобразованием типов из типа базы данных в тип .NET (например, мне никогда не приходилось иметь дело с типами баз данных вне объектов базы данных. Я всегда возвращаю DateTime , System.UInt32, bool и т. Д.).