|
Software Development Kit to the Delphi-Win32 and Free Pascal compilers |
| Home > Wiki > br/Metadata |
br/Metadataenglish (en) | português (br) Metadata Metadata é responsável por fornecer informação sobre as classes de negócio para o framework em tempo de execução. Exemplos deste tipo de informação: uma lista com os atributos da classe, tipos de atributos, atributos calculados, valores padrão e assim por diante. O metadata do PressObjects pode ser comparado com tabelas e campos de um RDBMS. Para um RDBMS não é possível armazenar informação, num banco de dados, sem tabelas e campos. Da mesma forma, para um projeto usando PressObjects, uma classe de negócio não pode ser instanciada, para receber dados, sem seu metadata.
[edit] Declaração de atributosO método InternalMetadataStr da classe de negócio (TPressObject) fornece o metadata em forma de string para o framework. A sintaxe do metadata é: 'ClassName <options> (attribute1: attrtype <options>; ...)' O modelo de exemplo a seguir declara duas classes, cada uma com seus atributos.
TClient = class(TPressObject)
_Name: TPressString;
_City: TPressReference;
protected
class function InternalMetadataStr: string; override;
end;
TCity = class(TPressObject)
_Name: TPressString;
protected
class function InternalMetadataStr: string; override;
end;
...
class function TClient.InternalMetadataStr: string;
begin
Result := 'TClient IsPersistent (' +
'Name: String(40);' +
'City: Reference(TCity);' +
')';
end;
class function TCity.InternalMetadataStr: string;
begin
Result := 'TCity IsPersistent (' +
'Name: String(30);' +
')';
end;
...
initialization
TClient.RegisterClass;
TCity.RegisterClass;
A opção IsPersistent, declarada depois do nome da classe, informa ao Press OPF que esta classe e seus descendentes devem ser persistidos (salvos), ex: o banco de dados deve fornecer tabelas e campos para salvar seus dados. Esta opção pode ser omitida quando estiver sincronizando dois modelos de negócio, ex: usando um broker do InstantObjects. Mesmo em classes persistentes, você pode ter atributos não persistentes. Como em alguns casos de #Atributos calculados, ou atributos que obtém seu valor apenas em tempo de execução. Para tanto, use a opção 'IsPersistent=False' no metadata do atributo, veja um exemplo:
class function TClient.InternalMetadataStr: string;
begin
Result := 'TClient IsPersistent (' +
'FirstName: String(20);' +
'LastName: String(20);' +
'FullName: String(40) IsPersistent=False Calc(FirstName, LastName);' +
')';
end;
[edit] Valores padrão e máscara de ediçãoO exemplo a seguir cria um atributo booleano com string personalizado para true e false, e um atributo tipo datetime formatado, que é preenchido com a data e hora correntes, sempre que a classe for instanciada:
TInvoice = class(TPressObject)
_Registered: TPressDateTime;
_Delivered: TPressBoolean;
protected
class function InternalMetadataStr: string; override;
end;
...
class function TInvoice.InternalMetadataStr: string;
begin
Result := 'TInvoice (' +
'Registered: DateTime DefaultValue="now" EdiMask="dd/MM/yyyy";' +
'Delivered: Boolean EditMask="yes;no";' +
')';
end;
Você também pode usar valores padrão para atributos do tipo #Enumerações. Como nesta classe: TInvoiceStatus = (isNew, isRegistered, isDelivered); TInvoice = class(TPressObject) _Status: TPressEnum; ... E seu metadata: TInvoice ( Status: Enum(TInvoiceStatus) DefaultValue=isNew; ... ) [edit] Atributos calculadosUm atributo calculado obtém seu valor a partir do valor de outros atributos. Seu valor muda, portanto, sempre que o valor dos outros atributos mudarem. Veja o exemplo a seguir.
TInvoice = class(TPressObject)
_Items: TPressParts;
_SumOfItems: TPressCurrency;
private
// SumOfItems getter and setter
protected
procedure InternalCalcAttribute(AAttribute: TPressAttribute); override;
class function InternalMetadataStr: string; override;
published
property SumOfItems: Currency read GetSumOfItems write SetSumOfItems;
end;
...
procedure TInvoice.InternalCalcAttribute(AAttribute: TPressAttribute);
var
VSum: Currency;
I: Integer;
begin
if AAttribute = _SumOfItems then
begin
VSum := 0;
for I := 0 to Pred(Items.Count) do
VSum := VSum + (Items[I] as TInvoiceItem).TotalItem;
SumOfItems := VSum;
end;
end;
class function TInvoice.InternalMetadataStr: string;
begin
Result := 'TInvoice (' +
'Items: Parts(TInvoiceItem);' +
'SumOfItems: Currency Calc(Items);' +
')';
end;
O metadata da classe TInvoice declara que SumOfItems é calculado usando o atributo Items. O próprio cálculo é definido no método InternalCalcAttribute, que é chamado para atualizar o valor do campo, sempre que necessário. O exemplo a seguir atualiza um atributo calculado, baseado no valor de outros dois atributos:
TInvoiceItem = class(TPressObject)
_Price: TPressCurrency;
_Discount: TPressCurrency;
_FinalPrice: TPressCurrency;
private
// getters and setters
protected
procedure InternalCalcAttribute(AAttribute: TPressAttribute); override;
class function InternalMetadataStr: string; override;
published
// attribute accessors
end;
...
procedure TInvoiceItem.InternalCalcAttribute(AAttribute: TPressAttribute);
begin
if AAttribute = _FinalPrice then
FinalPrice := Price * (100 - Discount) / 100;
end;
class function TInvoiceItem.InternalMetadataStr: string;
begin
Result := 'TInvoiceItem (' +
'Price: Currency;' +
'Discount: Currency;' +
'FinalPrice: Currency Calc(Price, Discount);' +
')';
end;
[edit] Agregações, composições e coleçõesUm relacionamento é definido no metadata da classe, colocando o nome da classe alvo entre parênteses, depois do nome do tipo de relacionamento. O PressObjects usa 'Part' e 'Parts' para especificar composições (o objeto alvo é possuído), 'Reference' e 'References' para especificar agregações (o objeto alvo não é possuído). Veja o exemplo a seguir. TInvoice = class(TPressObject) _Client: TPressReference; _Items: TPressParts; TInvoiceItem = class(TPressObject) _Product: TPressReference; _Colors: TPressReferences; TClient = class(TPressObject) _Name: TPressString; _Address: TPressPart; TAddress = class(TPressObject) _Street: TPressString; _City: TPressReference; TCity = class(TPressObject) _Name: TPressString; e os strings de metadata: TInvoice ( Client: Reference(TClient); Items: Parts(TInvoiceItem); ) TInvoiceItem ( Product: Reference(TProduct); Colors: References(TColors); ) TClient ( Name: String(40); Address: Part(TAddress); ) TAddress ( Street: String(40); City: Reference(TCity); ) TCity ( Name: String(40); ) [edit] Classe contêiner personalizadaTambém é possível criar contêineres personalizados para atributos de coleção. Eis um exemplo de como criar um novo atributo do tipo parts. Primeiro declare um novo descendente, do tipo de atributo apropriado: TInvoiceItemParts = class(TPressParts) public function Add: TInvoiceItem; ... class function ValidObjectClass: TPressObjectClass; override; end; ...sua implementação (ValidObjectClass é obrigatório) function TInvoiceItemParts.Add: TInvoiceItem; begin Result := inherited Add as TInvoiceItem; end; ... class function TInvoiceItemParts.ValidObjectClass: TPressObjectClass; begin Result := TInvoiceItem; end; ...registre o novo tipo de atributo: initialization TInvoiceItemParts.RegisterAttribute; ...e finalmente use este tipo de atributo no metadata do Pedido: TInvoice ( Client: Reference(TClient); Items: TInvoiceItemParts; ) [edit] EnumeraçõesO framework tem suporte à enumerações, contanto que seja registrado. Veja o exemplo a seguir. TPhoneType = (ptPhone, ptFax, ptMobile); TPhoneItem = class(TPressObject) _Number: TPressString; _PhoneType: TPressEnum; Metadata: TPhoneItem ( Number: String(20); PhoneType: Enum(TPhoneType); ) Registrando a enumeração: initialization PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType'); ...uma alternativa ao exemplo do registro da enumeração, mudando as representações string dos tipos dos items, a seguir: initialization PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType', [ 'Home Phone', 'Work Fax', 'Mobile Phone']); A representação string também suporta valores dinâmicos. Se o valor da variável mudar, o valor da representação também muda. initialization PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType', [ @SHomePhoneStr, @SWorkFaxStr, @SMobilePhoneStr]); [edit] Metadata de consultaA consulta a seguir procura por clientes cujo nome comece com a informação que estiver no atributo Name: TClientQuery = class(TPressQuery) _Name: TPressString; Metadata: TClientQuery(TClient) ( Name: String MatchType=mtStarting; ) O parâmetro (TClient) indica a classe dos objetos que serão recuperados. A propriedade MatchType configura o tipo de comparação que será efetuada com os valores dos atributos durante a consulta. O exemplo a seguir procura pedidos cujo atributo data esteja entre duas datas: TInvoiceQuery = class(TPressQuery) _MinDate: TPressDate; _MaxDate: TPressDate; Metadata: TInvoiceQuery(TInvoice) ( MinDate: Date DataName=Date MatchType=mtGreaterThanOrEqual; MaxDate: Date DataName=Date MatchType=mtLesserThanOrEqual; ) A propriedade DataName indica o nome do atributo persistente (campo). Use esta sintaxe quando dois ou mais atributos fizerem referência ao mesmo atributo persistente. Estas são as opções para MatchType: Nota: Distinção entre caixa ALTA e baixa e outros problemas de conjunto de caracteres (character set) devem ser responsabilidade do banco de dados. O OPF criará apenas as declarações como "<Campo> = 'Valor'", "<Campo> like 'Valor%'", baseado na informação fornecida pela Query.
|
Personal tools |