05.05.2011, 15:13 | #1 |
Участник
|
Обработка DBF через .net
Возможно кому то пригодится NET компонент (DLL) для работы с файлами dbf в Аксапта...
Приимущества данного решения: - Надежность (не требуется использования "глючных" ADODB, ODBC и так далее.) - Быстродействие (обработка файла KLADR.DBF (198 тыс записей) заняла 25 секунд, пример - ниже) - Возможность использования как на сороне клиента, так и на стороне сервера в т.ч. x64 Компонент самописный, взято отсюда: http://www.sql.ru/Forum/actualthread...194256#7194256 (спасибо автору) Компонент преобразует dbf файл в стандартный объект NET DataTable. Так же умеет делать и обратную процедуру. Пример использования - ниже. Как и другие NET компоненты, данную DLL перед использованием нужно "прописать" в Reference AOT аксапты. X++: static void DBFTest(Args _args) { System.Data.DataTable dt; System.Data.DataRowCollection rows; System.Data.DataRow row; int cnt; int i; str name; str code; int t1, t2; ; new InteropPermission(InteropKind::ClrInterop).assert(); try { t1 = timeNow(); dt = DBFclient.DirectAccess::Load("c:\\kladr.DBF"); rows = dt.get_Rows(); cnt = rows.get_Count(); info(strFmt("строк %1", cnt)); for (i = 0; i < cnt; i++) { row = rows.get_Item(i); name = row.get_Item("name"); code = row.get_Item("code"); } } catch (exception::CLRError) { throw error(AifUtil::getClrErrorMessage()); } catch (exception::Internal) { throw error("Internal"); } CodeAccessPermission::revertAssert(); t2 = timeNow(); info(strFmt("%1", t2 - t1)); } |
|
|
За это сообщение автора поблагодарили: Pustik (5), Ace of Database (4), lev (5), Poleax (4), MikeR (3), gl00mie (10). |
05.05.2011, 15:45 | #2 |
MCT
|
Перенес в базу знаний
__________________
Axapta book for developer |
|
05.05.2011, 15:58 | #3 |
Участник
|
См. также http://fastdbf.codeplex.com
|
|
|
За это сообщение автора поблагодарили: someOne (2). |
06.05.2011, 13:37 | #4 |
Участник
|
Цитата:
Сообщение от gl00mie
См. также http://fastdbf.codeplex.com
Некоторые особенности этого варианта: - штатно нельзя задать кодировку символов файла при чтении (по умолчанию она в стандартной западной кодировке, а российские файлы DBF кодируются в кодировке 866, в основном). Пришлось "подправить" это в DLL. - прилагается. - текстовые поля возврашаются без "обрезанных" с права пробелов, например X++: "Адыгея " А во остальном этот вариант - очень даже "FastDbf" Вот пример использования на том же примере... Файл - 198 тыс строк, время работы примера - 21 секунда, даже быстрее чем предидущий пример!!! X++: static void DBFTest(Args _args) { SocialExplorer.IO.FastDBF.DbfFile dbfFile; SocialExplorer.IO.FastDBF.DbfRecord dbfRecord; SocialExplorer.IO.FastDBF.DbfHeader dbfHeader; int cnt; int i; str name; str code; int name_idx; int code_idx; int t1, t2; ; new InteropPermission(InteropKind::ClrInterop).assert(); try { t1 = timeNow(); dbfFile = new SocialExplorer.IO.FastDBF.DbfFile(); dbfFile.Open("c:\\KLADR.DBF", System.IO.FileMode::Open); dbfHeader = dbfFile.get_Header(); cnt = dbfHeader.get_RecordCount(); // определяет количество строк в файле dbfRecord = new SocialExplorer.IO.FastDBF.DbfRecord(dbfHeader, System.Text.Encoding::GetEncoding(866)); // можно задать кодировку в dbf файле // определение порядкового номера столбца по его имени name_idx = dbfRecord.FindColumn("name"); code_idx = dbfRecord.FindColumn("code"); info(strFmt("строк в файле %1", cnt)); for (i = 0; i < cnt; i++) { dbfFile.Read(i, dbfRecord); name = dbfRecord.get_Item(name_idx); // Для корректного отображения результата по текстовым полям нужно использовать strrTrim для удаления пробелов в конце строки name = strRtrim(name); code = dbfRecord.get_Item(code_idx); } dbfFile.Close(); } catch (exception::CLRError) { error(AifUtil::getClrErrorMessage()); dbfFile.Close(); } catch (exception::Internal) { throw error("Internal"); } t2 = timeNow(); info(strFmt("Время обработки %1", t2 - t1)); } |
|
06.05.2011, 15:20 | #5 |
Участник
|
Там не помешают кое-какие косметические улучшения, к примеру, enum'ы, используемые для указания параметров методов, там кое-где объявлены внутри классов - их лучше вынести на уровень namespace'а, чтобы можно было ссылаться на них в коде Х++ (IntelliSense в MorphX тоже по-другому их "не видит"). После этого может потребоваться добавить соотв. namespace в список using в ряде исходников библиотеки, чтобы они нормально компилировались. Плюс, чтобы работать с dbf на сервере, сборка должна быть помещена в GAC, а для этого у нее должно быть это... strong name, т.е. надо будет еще ее подписать. А так да, очень обстоятельно сделано...
Последний раз редактировалось gl00mie; 06.05.2011 в 15:24. |
|
06.05.2011, 15:56 | #6 |
Участник
|
Цитата:
например X++: c:\Program Files\Microsoft Dynamics *\Bin\ По моему так даже проще и надежнее - проще обновлять версии DLL сборок |
|
01.12.2011, 21:27 | #7 |
Участник
|
Коллега допилил библиотеку FastDBF, устранив кое-какие косяки, правда, не возьмусь утверждать, сопровождены ли исправления соотв. комментариями.
PS. Библиотека давно и успешно используется в пром. эксплуатации, дала очень серьезный выигрыш в скорости. Последний раз редактировалось gl00mie; 01.12.2011 в 21:31. |
|
|
За это сообщение автора поблагодарили: Logger (3), leva (1), pedrozzz (1). |
06.12.2011, 16:34 | #8 |
OntargIT
|
Мы тоже ее используем. В промышленной эксплуатации грузим таблицы размером от 60mb, сделали warper и table field mapping. Работает стабильно уже пол-года.
|
|
15.12.2011, 15:00 | #9 |
Участник
|
Коллеги, а есть у кого рабочий пример для создания DBF c помощью "FastDbf"
Не могу понять как работать с заголовком DBF, подскажите в чем не прав - пример создает файл нулевой длины и не отпускает его пока открыта АХ. X++: static void DBFCreate(Args _args) { SocialExplorer.IO.FastDBF.DbfFile dbfFile = new SocialExplorer.IO.FastDBF.DbfFile(); SocialExplorer.IO.FastDBF.DbfRecord dbfRecord; SocialExplorer.IO.FastDBF.DbfHeader dbfHeader; ; dbfFile.Create("c:\\tt1.dbf"); dbfHeader = dbfFile.get_Header(); dbfHeader.AddColumn("StrCol", SocialExplorer.IO.FastDBF.DbfColumnType::Character, 20, 0); dbfHeader.AddColumn("NumCol1", SocialExplorer.IO.FastDBF.DbfColumnType::Number, 15, 3); dbfHeader.AddColumn("NumCol2", SocialExplorer.IO.FastDBF.DbfColumnType::Number, 3, 0); dbfHeader.AddColumn("DateCol", SocialExplorer.IO.FastDBF.DbfColumnType::Date); dbfHeader.AddColumn("BoolCol", SocialExplorer.IO.FastDBF.DbfColumnType::Boolean); dbfRecord = new SocialExplorer.IO.FastDBF.DbfRecord(dbfHeader, System.Text.Encoding::GetEncoding(1251)); dbfRecord.set_Item(1, 'Test'); dbfRecord.set_Item(2, '100.10'); dbfRecord.set_Item(3, '10'); dbfRecord.SetDateValue(4,str2datetime('31.12.2010', 123)); dbfRecord.set_Item(5, '1'); dbfFile.Write(dbfRecord); dbfFile.WriteHeader(); dbfFile.Close(); } |
|
15.12.2011, 16:56 | #10 |
Участник
|
Цитата:
При работе с NET не забывайте о таких особенностях: 1. Ядро аксапты не перехватывает CLR ошибки, поэтому их нужно перехватывать в коде самому, иначе остаются "висеть" инициализированные объекты (в вашем случае захваченные файлы). Со сборкой мусора при использовании CLR не все так просто... Кроме того вы просто не сможете понять в чем причина ошибки... 2. В NET принято нумерация последовательности объектов (строк, стобцов...) с нуля а не с единицы как в аксапте. X++: static void DBFCreate(Args _args) { SocialExplorer.IO.FastDBF.DbfFile dbfFile = new SocialExplorer.IO.FastDBF.DbfFile(); SocialExplorer.IO.FastDBF.DbfRecord dbfRecord; SocialExplorer.IO.FastDBF.DbfHeader dbfHeader; ; try { winApi::deleteFile("c:\\tt1.dbf"); dbfFile.Create("c:\\tt1.dbf"); dbfHeader = dbfFile.get_Header(); dbfHeader.AddColumn("StrCol", SocialExplorer.IO.FastDBF.DbfColumnType::Character, 20, 0); dbfHeader.AddColumn("NumCol1", SocialExplorer.IO.FastDBF.DbfColumnType::Number, 15, 3); dbfHeader.AddColumn("NumCol2", SocialExplorer.IO.FastDBF.DbfColumnType::Number, 3, 0); dbfHeader.AddColumn("DateCol", SocialExplorer.IO.FastDBF.DbfColumnType::Date); dbfHeader.AddColumn("BoolCol", SocialExplorer.IO.FastDBF.DbfColumnType::Boolean); dbfRecord = new SocialExplorer.IO.FastDBF.DbfRecord(dbfHeader, System.Text.Encoding::GetEncoding(1251)); dbfRecord.set_Item(0, 'Test'); dbfRecord.set_Item(1, "100"); dbfRecord.set_Item(2, "10"); dbfRecord.SetDateValue(3,str2datetime('31.12.2010', 123)); dbfRecord.set_Item(4, 'f'); dbfFile.Write(dbfRecord); dbfFile.WriteHeader(); dbfFile.Close(); } catch (Exception::CLRError) { dbfFile.Close(); Error(AifUtil::getClrErrorMessage()); } } |
|
|
За это сообщение автора поблагодарили: gl00mie (2), alvares (1). |
21.02.2012, 07:53 | #11 |
Постигающий
|
Люди, помогите! Зарегистрировал FastDBF через своего клиента AX2009 , положив DLL в папку c:\Program Files\Microsoft Dynamics AX\50\Server\*имя сервера*\Bin\
После чего у меня на машине код импорта DBF отрабатывает на ура, но у других пользователей вылетает ошибка: Цитата:
Объект "CLRObject" не может быть создан
X++: dbfFile = new SocialExplorer.IO.FastDBF.DbfFile(); CAS используем: X++: new InteropPermission(InteropKind::ClrInterop).assert(); UPD. проблема решена перемещением выполнения кода на сервер Последний раз редактировалось Андрей К.; 21.02.2012 в 08:11. |
|
21.02.2012, 08:11 | #12 |
Участник
|
|
|
22.02.2012, 00:12 | #13 |
Участник
|
Вы, значит, забыли положить сборку также в каталога bin клиента (на всех хостах, где у вас может запускаться код работы с этой сборкой).
|
|
|
За это сообщение автора поблагодарили: farlander (1), Андрей К. (1). |
27.09.2012, 15:30 | #14 |
Участник
|
При работе с FastDBF рекомендую обязательно использовать dbfRecord.Clear()
На вышеприведенном примере: X++: .... bfRecord = new SocialExplorer.IO.FastDBF.DbfRecord(dbfHeader, System.Text.Encoding::GetEncoding(1251)); dbfRecord.Clear(); //!!!!!!!!!!! dbfRecord.set_Item(0, 'Test'); dbfRecord.set_Item(1, "100"); .......... Принудительный вызов dbfRecord.Clear() все лечит |
|
Теги |
.net, dbf, fastdbf, законченный пример, полезное |
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|