Применение tagged values в БД
Применение стандартных методов проектирования баз данных не всегда удовлетворяет запросы программиста. Одно из ограничений - невозможность изменить набор атрибутов объекта после создания БД, что, при постоянно изменяемых требованиях заказчика, приводит к необходимости менять структуру БД (не потеряв при этом введенные данные), менять и заново отлаживать код программы, поля ввода/редактирования и т.д.
Вашему вниманию предлагается один из способов решения этой проблемы — применение tagged values (TV , тэг-значений, или именованных значений).
Сами значения хранятся в БД в строковом формате, но могут интерпретироваться в приложении по-разному, в зависимости от заданного типа значения. Аналог - всем известные ini-files! Основное преимущество применения TV - это возможность расширения списка значений в процессе исполнения приложения, без изменения структуры БД!
(image placeholder)
Список тэг-значений хранятся в таблице TaggedValue.
- DefaultValue - это значение будет использоваться, если не задано действующее значение.
- DefaultValueAlias - соответственно, значение для локализации.
- ValueType - тип значения. Кроме стандартных типов, можно использовать свои, все зависит от фантазии разработчика. В зависимости от типа значения выбирается встроенный редактор значений. Например, для типа "OCL" используется редактор OCL-выражений. Для перечислимого типа производится выбор значений, которые хранятся в таблице TV_ValueSet. Возможен выбор из списка объектов БД - тогда надо задать ValuesListName.
- Hint - подсказка при выборе/редактировании значения.
- HintAlias - локализованная подсказка.
- IsReadOnly - ну, это понятно.
- ValuesListName - название класса объектов, из списка значений которых будет производиться выбор. Например "City", и тогда будет предложен выбор из списка ClassByExpressionName(‘City).
Список значений для набора объектов - в таблице TV_ValueSet. Например, для логического типа можно задать значения "True/False", а возможно и "Можно/Нельзя".
Сами значения TV хранятся в таблице TV_Value. StringValue - действительное значение, значение, StringValueAlias - то, которое используется для локализации программы (или для показа юзеру ().
(image placeholder)
Для того, чтобы подключить к объекту тэг-значения, нужно создать ассоциацию типа n-n - OverridenTagedValues. Вся прелесть в том, что таким образом хранятся только переопределенные значения! Если нет значения - будет использоваться значение по-умолчанию! Классом такой ассоциации будет TV_Value, то есть, экземпляр класса - тэг-значение будет создан только при действительном вводе информации! Если грамотно подобрать умолчания, можно существенно сэкономить на размере БД!
Программная реализация.
Эта функция возвращает действующее тэг-значение по его имени. Если значение не было переопределено - возвращается значение по умолчанию.
function TMetaActorProperty.ActualValueByName(Tag: String): String;
var
_TaggedValue:TTaggedValue;
_TV_Value:TTV_Value;
begin
_TaggedValue:=Self.TaggedValues.EvaluateExpressionAsDirectElement(‘self->select(name=’+QuotedStr(Tag)+’)->first’) as TTaggedValue;
if Self.OverridenTaggedValues.Includes(_TaggedValue) then begin
_TV_Value:=Self.TV_Value.BoldObjects[Self.OverridenTaggedValues.IndexOf(_TaggedValue)];
Result:=_TV_Value.StringValue;
end
else
Result:=Self.TaggedValues.EvaluateExpressionAsString(‘self->select(name=’+QuotedStr(Tag)+’)->first.defaultValue’, 1);
end;
Эта процедура устанавливает тэг-значение.
procedure TMetaActorProperty.SetTVByName(Tag: String; Value: String);
var
_TaggedValue:TTaggedValue;
_TV_Value:TTV_Value;
Overriden: Boolean;
begin
_TaggedValue:=LocateInList(Self.TaggedValues, ‘name’, Tag) as TTaggedValue;
if not Assigned(_TaggedValue) then begin
MsgError(Self.Name+’: Not found TV=’+Tag);
Exit;
end;
Overriden:=Self.OverridenTaggedValues.Includes(_TaggedValue);
if Overriden then begin //Ранее было переопределено
_TV_Value:= //Найдем переопределенное значение
Self.TV_Value.BoldObjects[Self.OverridenTaggedValues.IndexOf(_TaggedValue)];
if (Value=_TaggedValue.DefaultValue) then //Если ввели значение по умолчанию — удалим переопределенное
Self.OverridenTaggedValues.Remove(_TaggedValue)
else //Ранее было переопределено и изменили значение
_TV_Value.StringValue:=Value;
end
else //Ранее было по умолчанию
begin
Assert(Assigned(_TaggedValue), ‘SetTVByName’);
Self.OverridenTaggedValues.Add(_TaggedValue);
_TV_Value:= //Найдем переопределенное значение
Self.TV_Value.BoldObjects[Self.OverridenTaggedValues.IndexOf(_TaggedValue)];
_TV_Value.StringValue:=Value;
end;
end;
Функция дает список подключенных тэг-значений.
function TMetaActorProperty.TaggedValues: TTaggedValueList; //Find TaggedValueList
var
expr: string;
begin
Result:=nil;
if not Assigned(Self) then Exit;
expr:=’TV_Section.allInstances->select(name=’+QuotedStr(Self.MetaClassName)+’)->first.taggedValues’;
Result:=TBoldSystem.DefaultSystem.EvaluateExpressionAsDirectElement(expr)as TTaggedValueList;
end;
Такие тэг-значения удобны для сохранения в БД настроек программы, быстрого создания прототипов проекта для согласования с заказчиком и добавления/изменения атрибутов на "лету" (в дальнейшем, после обкатки приложения, легко все оформить в виде "настоящих" атрибутов).
Для создания/редактирования тэг-значений применяется редактор на основе компонента TCommonInspector из пакета Greatis — http://www.greatis.com.
