Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» Работа с неUNICODE и UNICODE базами одного Delphi-приложения

Автор: romano501
Дата сообщения: 11.04.2013 11:11
Приложение на Delphi XE с использованием FIBPlus 7.3.
База данных в кодировке WIN1251.
Возникла потребность обеспечить работу в программе для пользователей грузии.
Решением было создать базу в UTF8, а в приложении менять Charset подключения к БД.
Для этого была создана БД с аналогичной структурой в кодировке UTF8.
Я расчитывал на то, что меняя DataBase.ConectParams.Charset c WIN1251 на UFT8 я смогу отрывать неUNICODE и UNICODE версию базы.
Но когда я делаю DataSet.Open из базы с кодировкой UFT8 выдается ошибка:
> Type mismatch for field 'NAME', expecting: Stri ng actual: WideString.
Тип поля NAME VARCHAR(40), к примеру.
Это значит что нужно удалить все String-поля во всех DataSet.FieldsEditors и заново их добавить, только после этого DataSets будут открываться.
Что значительно усложняет дело. Значит не получится из одного и того же исходного кода скомпилировать приложение, которое будет работать как с Unicode и неUnicode базой.
Подскажите, есть ли выход из этой ситуации? Как можно сделать чтобы одна версия исходного кода позволяла работать с базой в неUnicode и Unicode без перекомпиляции?

Суппорт Devrace.com морозится уже неделю
Автор: OXDBA
Дата сообщения: 11.04.2013 11:49

Цитата:
Это значит что нужно удалить все String-поля во всех DataSet.FieldsEditors

Удалить все поля всех типов

Цитата:
и заново их добавить

Не добавлять ничего
Автор: romano501
Дата сообщения: 11.04.2013 12:01
Нельзя не добавлять. В проекте порядка 70 форм, Columns используются как в Гридах, так и в коде
Автор: OXDBA
Дата сообщения: 11.04.2013 13:04

Цитата:
Columns используются как в Гридах,

Гридам совершенно параллельно статические или динамические TField'ы.

Цитата:
так и в коде

Эти грабельки сам разложил или кто помог?
Автор: romano501
Дата сообщения: 11.04.2013 13:11
Сам конечно же Ну книжечки по Delphi еще когда-то давно помогли


Цитата:
Гридам совершенно параллельно статические или динамические TField'ы.

Ну как параллельно, если в Field-е определено форматирование, заголовок, ширина и причие няшечки?
Автор: miwa
Дата сообщения: 11.04.2013 13:18

Цитата:
Ну книжечки по Delphi еще когда-то давно помогли

Ужс. Это в каких книжечках написано делать так, как у тебя?

По теме - я бы начал с вникание в работу String-а и Unicode в Delphi и FB. После чего все перевел на Unicode для всех пользователей.

Добавлено:

Цитата:
Ну как параллельно, если в Field-е определено форматирование, заголовок, ширина и причие няшечки?

А что мешает то же делать в коде? Паралельно подстраиваясь под параметры монитора пользователя.
Автор: romano501
Дата сообщения: 11.04.2013 13:26
Проект в принцпе Unicode-ready, просто клиентских установок дофига чтобы всех переводить.
Думал сделать отдельный билд для грузина и отпустить его восвояси.


Цитата:
Ужс. Это в каких книжечках написано делать так, как у тебя?

Не понимаю сарказма. Что здесь ужасного? Как любой dbaware-control будет отображать Field, если он не добавлен в DataSet.Fields ?

Добавлено:

Цитата:
А что мешает то же делать в коде?

Ну типа визуальная разработка и все такое...
А еще репозиторий полей FibPlus
Автор: OXDBA
Дата сообщения: 11.04.2013 14:24

Цитата:
Ну как параллельно, если в Field-е определено форматирование, заголовок, ширина и причие няшечки?

DataSet.DefaultFormats, DataSet.Fields[0].DisplayLabel, DataSet.Fields[0].DisplayWidth и прочие няшечки.

Цитата:
Как любой dbaware-control будет отображать Field, если он не добавлен в DataSet.Fields ?

А что ему может помешать?

Цитата:
Ну типа визуальная разработка и все такое...

Не надо об этом

Цитата:
А еще репозиторий полей FibPlus

Ему вообще фиолетово как DataSet.Fields заполняется, в design или run time.
Автор: romano501
Дата сообщения: 11.04.2013 15:50

Цитата:
DataSet.Fields[0].DisplayLabel,  DataSet.Fields[0].DisplayWidth и прочие няшечки.

Ну да, для 30 датасетов с 10-20-30 и больше полями прописывать все это и многое другое руками в коде - то еще веселье...
Автор: delover
Дата сообщения: 11.04.2013 16:58
romano501
Чтото про добавление полей динамически и статически - глухо натянуто у преподов. Если динамически лучше значит дизайн редактор плохо справляется с этим. Если так то можно выяснить типы и отличия создаваемые динамически и просто их заменить в форме.

Походу я обязан один филд одного общего датасета в DB модуле прописывать во всех гридинах и инспекторах как DisplayLabel так и формат. Про ширину полей умолчим, но всё остальное не соответствует изначальной постановке работы разработчика.

Добавлено:
romano501

Цитата:
Если так то можно выяснить типы

Если проблемма в том что TFIBWideStringField надо заменить на TFIBStringField, то это всего лишь замена в тексовом редакторе формы. Если не справитесь в ПМ дам телефон скинете базу, очень интересно.

Добавлено:
Кстати без вопросов переходите на Unicode - как ни странно - преобразования в 2 раза быстрее чем с WideString.
procedure _VarToUStr(var S: UnicodeString; const V: TVarData);
varOleStr: S := Copy(WideString(Pointer(V.VOleStr)), 1, MaxInt);
Боролись со счётчиком

Добавлено:

Цитата:
Не добавлять ничего

А вдруг разработчик хочет иметь контроль компилятора раньше чем ошибки всплывут у клиента??? Я знаком с мнением иметь динамические филды и раньше глупо доверял этому мнению (IMXO), я выбрал другой путь - мне так удобнее.
Автор: romano501
Дата сообщения: 11.04.2013 19:37
delover
TFIBStringField было изначально, но когда открываешь UNICODE-базу, то строковое поле становится TFIBWideStringField

Преобразования типа

Цитата:
varOleStr:   S := Copy(WideString(Pointer(V.VOleStr)), 1, MaxInt);

у меня в проектах не используется.

А что останавливало от перевода проекта полностью на UNICODE:
1) программа используется только для славянских пользователей. Это один пользователь из грузии захотел чтоб по грузински все писалось и я попробовал реализовать поддержку, не ожидал что будет затык
2) не хочется чтобы объем БД становился больше
3) даже если выпустить новую версию, переведя все строковые фиелды на WideString нужно вручную каждую базу каждого клиента переконвертировать. По времени это достаточно долго.

Проекту изначально не нужен был Unicode.

delover

Цитата:
я выбрал другой путь - мне так удобнее.

Всмысле определить фиелдсы в датасетах на этапе дизайна форм/датамодуля?

Автор: miwa
Дата сообщения: 11.04.2013 19:45

Цитата:
Ну да, для 30 датасетов с 10-20-30 и больше полями прописывать все это и многое другое руками в коде - то еще веселье...


Поэтому в проектах с более чем одной формой все формы наследуются от какой-то одной базовой, в которой кроме прочего делаются подобные вещи. Один раз. И потом во всем проекте все везде одинаково красиво.
Автор: romano501
Дата сообщения: 11.04.2013 19:46
... и этот проект не исключение
Автор: miwa
Дата сообщения: 12.04.2013 00:12

Цитата:
... и этот проект не исключение

Ну так же ж отлично Я не знаю специфики проекта, поэтому решение в лоб

Код:
for c := 0 to self.ComponentCount do
if self.Components[c] is TpFIBDataSet then
with (self.Components[c] as TpFIBDataSet) do
begin
DefaultFormat := ...;
AutoCommit := ...;
for j := 0 to FieldCount do
case Fields[j].DataType of
...
end;
end;
Автор: romano501
Дата сообщения: 12.04.2013 06:27
miwa
Все DataSets находятся в DataModule.
Цикл ни к чему, т.к. есть DataSet.DafaultFormats. И у каждого Field есть свой собственный атрибут: DisplayLabel, DisplayWidth, не говоря уже о том, что у некоторых полей есть обработчик GetText, SetText и тому подобные, обработчик проверки условия и еще некоторые детали.
И вообще, нереально для CRUD-приложения, напичканого различными DBControls, писать весь код руками, вместо определения DataSet.FieldsDefs в DesignTime.
Согласен что для какой-то задачи обработки данных, где нет активного взаимодействия с пользователем, динамическая работа с Fields будет более оправдана.
У меня есть в проекте DataSet без Fields. Это отчетная форма, где каждый запрос имеет различный состав полей. И работа с таким DataSet динамически - довольно неудобно, но приходится, что делать.
Код fdsEmployeeID.Value, fdsEmployeeNAME.Value гораздо информативнее, чем fdsEmployee.Fields[0].Value и fdsEmployeeю.Fields[1].Value.
Это еще надо помнить какой номер у каждого поля для каждого DataSet - да ну нах.
Ну еще контроль во время компиляции - тоже немаловажно.
Автор: OXDBA
Дата сообщения: 12.04.2013 09:18

Код:
const
fnEmployeeId = 'ID';
....
fdsEmployee.FieldByName(fnEmployeeId).asInteger
Автор: romano501
Дата сообщения: 12.04.2013 11:12
OXDBA


Код:
const
fnEmployeeId = 'ID';
....
fdsEmployee.FieldByName(fnEmployeeId).asInteger
Автор: miwa
Дата сообщения: 12.04.2013 12:10

Цитата:
Ну да, для 30 датасетов с 10-20-30 и больше полями прописывать все это и многое другое руками в коде - то еще веселье...



Цитата:
После изменения длины строкового поля, либо домена достаточно переоткрыть датасет в Design-time чтобы в DataSet.FieldsDefs подставилась новая длина поля и все.


Таки датасет один, или 30?


Цитата:
Все DataSets находятся в DataModule.

Вообще все? Со всего приложения? На екране место еще не закончилось? А что будет, если закончится?


Цитата:
Предлагаю все таки вернуться к моему вопросу, а не обсуждению стилей программирования.

Тогда предлагаю полный переход на юникод. Потому что сегодня грузинский клиент что-то захотел, завтра казахский будет ругаться, что его 1251 не совпадает с вашей, послезавтра турки захотят вашу программу, а через месяц отечественный заказчик откроет представительство в Китае. Глобализация
Автор: romano501
Дата сообщения: 12.04.2013 12:17
Датасетов много, 30 это я обозначил примерный объем, пока в одном DataModule помещаются

Итак, вариант №2 - полный переход на UNICODE - принято
Автор: delover
Дата сообщения: 12.04.2013 14:26
romano501

Цитата:
Цитата:
varOleStr:   S := Copy(WideString(Pointer(V.VOleStr)), 1, MaxInt);

у меня в проектах не используется.


Процедура _VarToUStr подключается автоматически, она в модуле Variants.pas,
когда:
String2010 := TField(MyWidestringField).Value;
Проверить достаточно просто - встаньте там отладчиком и посортируйте.
Если у Вас нет WideStringField, то скорее всего из вариантов, которые содержат видестринг строка не извлекается.


Добавлено:

Цитата:
встаньте там отладчиком и посортируйте.  

Не смотрите, в новых фибах нормальная сортировка. Однако я сам иногда ленюсь и пользуюсь переменными Variant.
Автор: romano501
Дата сообщения: 13.04.2013 08:49
delover
У меня была еще надежда что в FIB-ах какая-нить директива есть, которая приравняет TFIBStringField к TFIBWideStringField
Автор: delover
Дата сообщения: 13.04.2013 09:42
romano501
Мне очень понравилась сортировка в новых фибах - её можно переопределить. Это нужно например для текстового поля номер документа, там префикс и номер. Текстовая сортировка - это не правильно. Но сейчас пользуем старые фибы, сиё мне недоступно, как и уникод. А проблемм переопределить класс любых компонентов прямо перед декларацией формы, таких проблемм нет. Я облявляю
type
TFIBStringField = class(FIBdataset.TFIBWideStringField)
...
end;
type
TdmMain = class(TDataModule)
...

И немного подправляю ручками CheckField virtual

Добавлено:
Чтобы понимать что такое датасет необходимо знать несколько процедур

Код:
function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
procedure CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef); override;
procedure CreateFields; override;
procedure InternalInitFieldDefs; override;
procedure InternalOpen; override;
procedure BindFields(Binding: Boolean); virtual; // не переопределял
Автор: delover
Дата сообщения: 13.04.2013 15:18
Я Лука Кизя без перевода читал, советую так же.
Автор: romano501
Дата сообщения: 13.04.2013 16:49
delover

Цитата:
type   TFIBStringField = class(FIBdataset.TFIBWideStringField)

Это как раз то что мне нужно. Спрятать это определение за $DEFINE и UNICODE-версия приложения готова.
Благодарю за наводку!


Цитата:
Я Лука Кизя без перевода читал, советую так же.

Дай ссылку, пожалста, бо я даже не понимаю что это за набор слов "Лука Кизя"
Автор: delover
Дата сообщения: 15.04.2013 18:44
romano501

Цитата:
Дай ссылку, пожалста

Лука Кизя это очень известный Украинский афтор, я читал книгу - интернета тогда небыло.


Цитата:
Это как раз то что мне нужно.

Рад за наводку если что и мне пригодится.
Автор: romano501
Дата сообщения: 16.04.2013 07:21
delover
Не получилось сделать переопределение
type
TFIBStringField = class(FIBdataset.TFIBWideStringField)

На этапе запуска программы выдается сообщение
---------------------------
Debugger Exception Notification
---------------------------
Project raised exception class EReadError with message 'Property EmptyStrToNull does not exist'.

Подобный код
TFIBStringField = class(FIBdataset.TFIBWideStringField)
FEmptyStrToNull: Boolean;
public
property EmptyStrToNull: Boolean read FEmptyStrToNull default False;
end;

не компилируется с сообщением
[DCC Error] DataModule.pas(16): E2217 Published field 'FEmptyStrToNull' not a class or interface type
Автор: delover
Дата сообщения: 16.04.2013 08:51
romano501
public
property

попробуй
published
property

так как из формы читается только паблишед

Добавлено:
1) property EmptyStrToNull: Boolean read FEmptyStrToNull default False;
Это свойство у тебя readonly по этому оно тоже не может быть изменено при прочтении с формы. Я бы просто скопировал текст из FIB исходника
2) FEmptyStrToNull: Boolean; Это у тебя паблишед филд, мне кажется должно быть:
TFIBStringField = class(FIBdataset.TFIBWideStringField)
private
FEmptyStrToNull: Boolean;
public

Добавлено:
romano501

Цитата:
default False;

Объявление свойства умолчания нужно только при сохранении компонента в файл. Так как даже при желании вы не сможете зарегистрировать класс в DesignIDE, то никто Ваш класс сохранять не будет. default писать не надо.
Автор: delover
Дата сообщения: 16.04.2013 11:07
Посмотрел, в общем надо так:

Код: type
TFIBStringField = class(FIBDataSet.TFIBWideStringField)
private
FEmptyStrToNull: boolean;
published
property EmptyStrToNull:boolean read FEmptyStrToNull write FEmptyStrToNull ;
end;

TpFIBDataSet = class(pFIBDataSet.TpFIBDataSet)
protected
procedure CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef); override;
//код пустышка
end;
Автор: romano501
Дата сообщения: 20.04.2013 09:04
delover, приветствую!
Твой последний код сработал, однако вот этот кусок кода не понадобился.

Код:
TpFIBDataSet = class(pFIBDataSet.TpFIBDataSet)  
protected    
procedure CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef); override;        
//код пустышка  
end;

Страницы: 1

Предыдущая тема: Програмированние в AHK


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.