{"id":530,"date":"2009-08-21T22:48:28","date_gmt":"2009-08-21T12:48:28","guid":{"rendered":"http:\/\/www.malcolmgroves.com\/blog\/?p=530"},"modified":"2015-03-13T11:05:13","modified_gmt":"2015-03-13T00:05:13","slug":"more-attributes-in-delphi-2010","status":"publish","type":"post","link":"http:\/\/www.malcolmgroves.com\/blog\/?p=530","title":{"rendered":"More Attributes in Delphi 2010"},"content":{"rendered":"<p>In <a href=\"http:\/\/www.malcolmgroves.com\/blog\/?p=476\">my previous post on Attributes in Delphi 2010<\/a>, I showed a basic view of the mechanics involved in creating, applying and querying Attributes. I didn\u2019t really cover an example of why you might use them.<\/p>\n<p>Probably the most common example given is for persistence, and indeed, someone over on the <a href=\"http:\/\/wings-of-wind.com\/2009\/08\/17\/rad-studio-2010-review-9-\u2013-attributes-the-new-rtti-and-db-access\/\" target=\"_blank\">Wings of Wind website<\/a> has posted an example along those lines. I\u2019d like to show off a different use for them: Validation.<\/p>\n<p><!--more-->Note, what I\u2019ll show you below is meant to be an example of attribute usage, not an example of a robust, complete validation framework. View it like a proof of concept, not finished code.<\/p>\n<p>OK, disclaimers out of the way, in this scenario I\u2019d like to be able to add meta-data to my classes specifying validation rules. Maybe I want to be able to say that for my TPerson class to be considered valid, it must have a non-empty Name value, and the Age value must be between 18 to 65. One way to achieve this is to decorate the relevant properties with Attributes specifying these rules, and then have some code that uses RTTI to walk through whatever object is passed to it and validates each of <a href=\"http:\/\/www.malcolmgroves.com\/blog\/wp-content\/uploads\/2009\/08\/validationattributes.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"margin: 0px 10px 0px 0px; display: inline; border-width: 0px;\" title=\"Click for full size image.\" src=\"http:\/\/www.malcolmgroves.com\/blog\/wp-content\/uploads\/2009\/08\/validationattributes-thumb.jpg\" alt=\"Click for full size image.\" width=\"244\" height=\"180\" align=\"left\" border=\"0\" \/><\/a>the property values according to the attributes attached to it.<\/p>\n<p>Make sense? OK, let\u2019s have a look.<\/p>\n<p>I\u2019ve defined a few Validation attributes. Not a full set, just a few to show them working on different types. There\u2019s a small hierarchy which makes the validation code a little more generic. So far I\u2019ve only created Attributes for strings and Integers, but you should get the idea.<\/p>\n<p>If we have a look at the source for one of the leaf classes, the definition looks like this:<\/p>\n<pre lang=\"Delphi\">  MinimumIntegerAttribute = class(BaseIntegerValidationAttribute)\r\n  private\r\n    FMinimumValue: Integer;\r\n  public\r\n    constructor Create(MinimumValue : Integer; const FailureMessage : string);\r\n    function Validate(Value : Integer) : boolean; override;\r\n  end;<\/pre>\n<p>The constructor just stores the two parameters, and the validate method is very simple (well, the rule is simple in this case). It looks like this:<\/p>\n<pre lang=\"Delphi\">function MinimumIntegerAttribute.Validate(Value: Integer): boolean;\r\nbegin\r\n  Result := Value &gt;= FMinimumValue;\r\nend;<\/pre>\n<p>Now that I have these attributes defined, I can use them like this:<\/p>\n<pre lang=\"Delphi\">  TPerson = class\r\n  private\r\n    FName: String;\r\n    FAge: Integer;\r\n  public\r\n    [NonEmptyString('Must provide a Name')]\r\n    property Name : String read FName write FName;\r\n    [MinimumInteger(18, 'Must be at least 18 years old')]\r\n    [MaximumInteger(65, 'Must be no older than 65 years')]\r\n    property Age : Integer read FAge write FAge;\r\n  end;<\/pre>\n<p>Yes, another TPerson. Sorry, not very imaginative.<\/p>\n<p>As you can see, I\u2019ve tagged the Name property with the NonEmtpyString attribute, and supplied a failure message should it be found invalid. I\u2019ve also tagged the Age property with both a minimum and maximum value attribute. What would probably be more useful is to define a validation attribute for a range, but I wanted an excuse to show tagging two attributes on a single property.<\/p>\n<p>For the sake of this example, I\u2019ve created a simple function that does the validation, like this:<\/p>\n<pre lang=\"Delphi\">function Validate(Target : TObject; ErrorList : TStrings) : boolean;\r\nvar\r\n  ctx : TRttiContext;\r\n  t : TRttiType;\r\n  p : TRttiProperty;\r\n  a : TCustomAttribute;\r\nbegin\r\n  Result := True;\r\n\r\n  if not Assigned(Target) then\r\n    raise Exception.Create('Can''t validate nil object');\r\n\r\n  if not Assigned(ErrorList) then\r\n    raise Exception.Create('Can''t validate with a nil ErrorList');\r\n\r\n  ctx := TRttiContext.Create;\r\n  try\r\n    t := ctx.GetType(Target.ClassType);\r\n    for p in t.GetProperties do\r\n      for a in p.GetAttributes do\r\n        if a is BaseIntegerValidationAttribute then\r\n        begin\r\n          if not BaseIntegerValidationAttribute(a).Validate(p.GetValue(Target).AsInteger) then\r\n            ErrorList.Add(BaseValidationAttribute(a).FailureMessage);\r\n        end\r\n        else if a is BaseStringValidationAttribute then\r\n        begin\r\n          if not BaseStringValidationAttribute(a).Validate(p.GetValue(Target).AsString) then\r\n            ErrorList.Add(BaseValidationAttribute(a).FailureMessage);\r\n        end\r\n  finally\r\n    ctx.Free;\r\n  end;\r\nend;<\/pre>\n<p>First I do some checking to make sure that the instance passed in to be validated is not actually nil, and the same for the Errorlist where I\u2019ll report any issues.<\/p>\n<p>Then, for each property, I grab each of the attributes. Depending on what type of attribute it is, I call the Validate method, passing in the property value. If the call to Validate fails, I add the FailureMessage to the ErrorList.<\/p>\n<p>This could certainly be improved. For example, I don\u2019t like that I have to update the Validate method for each type I want to Validate, and simply dumping any errors into a TStrings is pretty lame. Providing a list of ValidationFailure objects would probably be more helpful. However, for 45 minutes work, I think it shows the concept reasonably well. You can download the sample code <a href=\"https:\/\/github.com\/malcolmgroves\/delphi-experiments\/tree\/master\/ValidationAttributes\">here<\/a>.<\/p>\n<p>Whether you want to use Attributes for persistence, validation or for something else entirely, hopefully this has been useful. A few people have said they don\u2019t see the point of attributes. Maybe it\u2019s worth exploring them and giving them some time to \u201cbake\u201d in your thoughts. It\u2019s said that our language shapes our thoughts, and it may just be a case that because Delphi has not supported attributes in the past, we\u2019re not used to thinking about solutions that use them. As with Generics, Anonymous Methods and Interfaces before them, I find that the more I become familiar with them, the more ideas come forward about how I might use them<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my previous post on Attributes in Delphi 2010, I showed a basic view of the mechanics involved in creating, applying and querying Attributes. I didn\u2019t really cover an example of why you might use them. Probably the most common example given is for persistence, and indeed, someone over on the Wings of Wind website [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[98],"tags":[50,19,48],"class_list":["post-530","post","type-post","status-publish","format-standard","hentry","category-coding","tag-attributes","tag-delphi","tag-embarcadero"],"_links":{"self":[{"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/530","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=530"}],"version-history":[{"count":19,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/530\/revisions"}],"predecessor-version":[{"id":1799,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/530\/revisions\/1799"}],"wp:attachment":[{"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=530"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.malcolmgroves.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}