В общем, хочу сделать класс, который будет импортировать DataTable в Access или MS SQL.
Класс будет иметь 1 метод: Append, которому на вход будет подаваться DataTable, а метод в свою очередь будет загружать DataTable в нужный пункт назначения.
Разумеется, внутренняя логика Append у Access может сильно отличаться, например:
В Access нужно следить за тем, что бы файл не был выше 2гб, иначе бд поломается
В MS SQL можно воспользоваться BulkCopy, а в Access потребуется какой-то самопал.
Соответственно и конструкторы могут сильно отличаться по параметрам, например:
Строки подключения между двумя типами будут отличаться
В MS SQL можно использовать или не использовать транзакцию
В Access можно восстанавливать или не восстанавливать БД после каждого Append.
Как лучше всего все это оформить?
UPD:
Есть у меня некоторые предположения, как это можно сделать, но не знаю на сколько это верно.
Сделать интерфейс:
public interface IApend
{
void Append(DataTable dt);
}
Реализовать его в 2 ух классах, каждый из которых будет содержать в себе собственную логику импорта:
public class ImporterToSQL: IApend
{
//Какие-то поля
public void Append(DataTable dt)
{
//Какая-то логика
}
}
public class ImporterAccess : IApend
{
//Какие-то поля
public void Append(DataTable dt)
{
//Какая-то логика
}
}
И создать такой класс:
public class Importer
{
IApend Concrete;
public Importer(IApend concrete)
{
Concrete = concrete;
}
static Importer ImporterToSQL(object v,object v2,object v3)
{
return new Importer(new ImporterToSQL(object v, object v2, object v3));
}
static Importer ImporterToSQL(object v, object v2)
{
return new Importer(new ImporterToSQL(object v, object v2));
}
static Importer ImporterToAccess(object v, object v2, object v3)
{
return new Importer(new ImporterAccess(object v, object v2, object v3)));
}
static Importer ImporterToAccess(object v, object v2)
{
return new Importer(new ImporterAccess(object v, object v2));
}
public void Append(DataTable dt)
{
Concrete.Append(dt);
}
}
На сколько это верно и на сколько хорошо этот подход будет работать в плане добавления нового функционала(Например, мне приспичило добавить импорт из SQL в Access)? Это , вроде, зовется фабрикой классов?
Ответ
Вы на правильном пути. Общий интерфейс для конвертации -- это правильно. Только я бы назвал его чуть поконкретнее, хотя б IImporter какой-нибудь.
Что касается создания конкретных экземпляров и вашего класса Importer. Для создания действительно нужна фабрика. Однако она у вас получилась не в чистом виде. Смысла оборачивать экземпляр в ваш Importer нет -- у вас уже есть интерфейс. Просто создавайте нужные экземпляры и возвращайте их. А клиент уже будет вызывать напрямую методы:
public static class ImporterFactory
{
static IImporter CreateSQLImporter(object v, object v2, object v3)
{
return new ImporterToSQL(v, v2, v3);
}
static IImporter CreateAccessImporter(object v, object v2, object v3)
{
return new ImporterAccess(v, v2, v3);
}
// и так далее
}
В качестве параметров для конструкторов у вас наверняка выступают специфичные для каждой реализации данные: например, строка подключения для SQL и путь к mdb файлу для Access. В идеале клиент вообще не должен знать что-то о деталях реализации, а фабрика должна содержать один метод на тип. Все детали о реализации прячутся в фабрике, которая подсовывает нужные параметры напрямую из конфигурации (например, из app.config). При этом клиент вообще ничего не будет знать о реализации:
public static class ImporterFactory
{
static IImporter CreateImporter(object v)
{
var generalConfig = GetGeneralConfig();
if (generalConfig.Type == "sql")
{
var config = GetSqlImporterConfig();
return new ImporterToSQL(config.ConnectionString, v);
}
else if (generalConfig.Type == "access")
{
var config = GetAccessImporterConfig();
return new ImporterToAccess(config.ConnectionString, v);
}
}
}
Такой подход будет очень хорошо работать, если вам потребуется импорт в новое место. Вы просто напишете еще одну реализацию IImporter, внести изменения в ImporterFactory и все. Все клиентs, которые используют IImporter, менять не придется. В случае же отдельных методов вам придется менять вызывающий код, который управляет тем, какой метод вызвать.
Что касается импорта из SQL в Access, то такую задачу описанным выше способом не решить. Выше мы рассмотрели только задачу "как данные, представленные в универсальном виде (DataTable), записать в конкретное место". Задачу импорта из SQL в Access можно переформулировать как "как извлечь данные из одного места и записать их в другое место". Такую задачу полезно разбить на два шага и ввести промежуточный этап. Какой? Правильно, получение данных в универсальном виде. Таким образом, шага будет два:
Импорт из источника с преобразованием в универсальный вид
Экспорт в другое место
Соответственно, два основных контракта будут выглядеть как-то так:
interface IImporter
{
DataTable Import();
}
interface IExporter
{
void Export(DataTable data);
}
Реализация второго шага у вас уже есть, правда сейчас она называется импортом. При наличии двух шагов будет логичнее назвать их с т.з. того, как данные движутся относительно вашего приложения. Вы импортируете (загружаете) данные из одного источника и экспортируете (выгружаете) их в другое место.
Реализация первого шага будет аналогична -- различные реализации интерфейсов, фабрика.
Для осуществления процесса конвертации вам понадобится некоторая общая сущность, которая будет соединять шаги. Она может принимать в качестве зависимостей как фабрики, так и экземпляры импортера/экспортера:
public class DataConverter // придумайте имя поудачнее, пожалуйста :)
{
private readonly IImporter _importer;
private readonly IExporter _exporter;
public DataConverter(IImporter importer, IExporter exporter)
{
_importer = importer;
_exporter = exporter;
}
public void Convert()
{
var data = _importer.Import();
_exporter.Export(data);
}
}
Комментариев нет:
Отправить комментарий