📜 ⬆️ ⬇️

Details about attributes in Delphi 2010. Example - Data Verification

Mouse In my previous article on Attributes in Delphi 2010 in brief , I showed the basics of creating, applying, and polling attributes. However, I did not give an example of what you could use them for.

Probably the most common example is for persistence, and, indeed, someone posted a similar example on the Wings of Wind website. I would like to show their other uses - for data validation (Validation).


Note that what will be shown below is just an example of using attributes, and not a complete framework for checking data. Treat it as a test of concept, not as ready-made code.
')
So, with the warnings finished, in that case I would like to be able to add metadata to my classes specifying validation rules. Maybe I want the class TPerson class to be considered correct when Name is a non-empty value and Age value ranges from 18 to 65. One way to achieve this is to provide the corresponding properties with attributes that define these rules, and then use some code that uses RTTI to process any transmitted object and verifies each click for a full-sized image. Property values ​​correspond to attributes attached to them.


Got it? Great, now let's see.

I defined several attributes for data validation to show how they work with different types. A small hierarchy has been created that makes the verification code a bit more general. I created attributes for strings and numbers only, but you get the idea.

Take a look at the source code for one of the classes:

MinimumIntegerAttribute = class (BaseIntegerValidationAttribute)
private
FMinimumValue: Integer;
public
constructor Create(MinimumValue : Integer; const FailureMessage : string );
function Validate(Value : Integer) : boolean; override ;
end;



The constructor contains two parameters, the verification method is very simple (in this case, the rule is simple). It looks like this:

function MinimumIntegerAttribute.Validate(Value: Integer): boolean;
begin
Result := Value >= FMinimumValue;
end;



Now I have defined the attributes and can use them like this:

TPerson = class
private
FName: String ;
FAge: Integer;
public
[NonEmptyString( 'Must provide a Name' )]
property Name : String read FName write FName;
[MinimumInteger(18, 'Must be at least 18 years old' )]
[MaximumInteger(65, 'Must be no older than 65 years' )]
property Age : Integer read FAge write FAge;
end;


Yes, another TPerson . Not very original.

As you can see, I supplied the Name property with the NonEmtpyString attribute, and added an error message for the wrong value. I also noted the Age property with minimum and maximum attributes. Probably more useful is defining an attribute for a range, but I wanted to show the application of two attributes to a single property.

For this example, I created a simple function that does a check:

function Validate(Target : TObject; ErrorList : TStrings) : boolean;
var
ctx : TRttiContext;
t : TRttiType;
p : TRttiProperty;
a : TCustomAttribute;
begin
Result := True;

if not Assigned(Target) then
raise Exception.Create( 'Can' 't validate nil object' );

if not Assigned(ErrorList) then
raise Exception.Create( 'Can' 't validate with a nil ErrorList' );

ctx := TRttiContext.Create;
try
t := ctx.GetType(Target.ClassType);
for p in t.GetProperties do
for a in p.GetAttributes do
if a is BaseIntegerValidationAttribute then
begin
if not BaseIntegerValidationAttribute(a).Validate(p.GetValue(Target).AsInteger) then
ErrorList.Add(BaseValidationAttribute(a).FailureMessage);
end
else if a is BaseStringValidationAttribute then
begin
if not BaseStringValidationAttribute(a).Validate(p.GetValue(Target).AsString) then
ErrorList.Add(BaseValidationAttribute(a).FailureMessage);
end
finally
ctx.Free;
end;
end;


First, I do a check to make sure that the faithful instance of the object is not nil , and the same for the Errorlist , in which I will enter different messages.

Next, for each property, I will select all the attributes. Depending on the type of attribute, I will call the verification method, passing the value to be verified. If the call to Validate fails, I add FailureMessage to the ErrorList list.

Of course, you can do better. For example, I do not like that I have to update the Validate method for each type being checked and just output errors in TStrings . Presenting a list of ValidationFailure objects is likely to be more correct. However, in 45 minutes of work, I think this describes the idea quite well. You can download sample code here .

I hope this will be useful for you if you want to use attributes for persistence, data validation or for something else.
Several people said that they did not see the point in attributes. It may be worth exploring them and leaving them for a while so that they can “mature” in your head. They say that our language shapes our thoughts, and this may be simply because Delphi did not support attributes in the past, we are not used to thinking about them in search of a solution. As with generics, anonymous methods and interfaces to them, I find that the more I study them, the more ideas there are to use them.

translated.by/you/more-attributes-in-delphi-2010/into-ru/trans
Translation: © r3code, TDelphiBlog.

Source: https://habr.com/ru/post/105776/


All Articles