Page 16 - MSDN Magazine, March 2019
P. 16

The Working Programmer TED NEWARD Coding Naked: Naked Properties
Welcome back, NOFers. (I’ve decided that sounds better than call- ingthosewhousenakedobjects“nakedcoders.”)Inthelastpiece,I started building out the domain model for my conference system, which allowed me to store speakers, but it’s a pretty plain-vanilla setup thus far. I haven’t done any of the sorts of things you’d normally need to do, like verifying that first or last names aren’t empty, or supporting a “topics” field that’s one of a bound set of options, and so on. All of these are reasonable things to want to support in a UI (as well as a data model), so if this “naked” approach is going to be used for real-world scenarios, it needs to be able to do them, as well.
Fortunately, the NOF designers knew all that.
Naked Concepts
Let’s go back and talk about how NOF handles this in the general case before I get into the specifics.
Remember, the goal of NOF is to keep from having to write UI code that could otherwise be signaled using some aspect of the domain objects themselves, and the best way to do that sort of signaling is through the use of custom attributes. In essence, you use NOF custom attributes to annotate various elements of the domain object—properties and methods, for the most part—and the NOF client understands, based on the presence of the attribute, or data contained inside the attribute, that it has to customize the UI for that object in some manner. Note that NakedObjects doesn’t need to actually define many of these custom attributes, as they come “for free” from the System.ComponentModel namespace in the standard .NET distribution. Reusability!
Specifying non-zero names is one of the easiest validations to apply, because it’s a static one.
However, sometimes it’s not quite as simple as “this should always be the case.” For example, if certain properties have to be disabled based on the internal state of the object (such as an “on-parental-leave” property that needs to be disabled if an employee has no spouse or children), then code will need to be executed, and that’s something a custom attribute can’t provide. In those situations, NOF relies on convention: specifically, NOF will look for particularly named meth- ods on the class. If the parental-leave property is named OnLeave,
then the method that NOF will execute to determine whether to disabletheOnLeavepropertywouldbecalledDisableOnLeave.
Let’s see how this works out in practice.
Naked Speakers, Redux
Currently, the Speaker class has just three properties on it, First- Name, LastName and Age. (That’s not counting the Id property, which isn’t visible to the user, and the FullName property, which is computed out of the FirstName and LastName properties; because they aren’t user-modifiable, they’re not really of concern here. Yet.) It wouldn’t make sense for this conference system to allow for empty first or last names, and a negative age probably wouldn’t make much sense, either. Let’s fix these first.
Specifying non-zero names is one of the easiest validations to apply, because it’s a static one. There’s no complicated logic required—the length of the strings supplied to each property has to be greater than zero. This is handled by the StringLength attri- bute on each property, like so:
[StringLength(100,
ErrorMessage = "First name must be between 1 and 100 characters", MinimumLength = 1)]
public virtual string FirstName { get; set; }
[StringLength(100,
ErrorMessage = "Last name must be between 1 and 100 characters", MinimumLength = 1)]
public virtual string LastName { get; set; }
That takes care of the empty-names problem.
Age is even easier, because I can use the Range custom attribute to specify acceptable minimum and maximum age ranges. (Would I really consider bringing in a speaker younger than 21? Possibly, because I want to encourage school-age kids to speak, but anyone younger than 13 would probably be a tough sell.) Applying the Range attribute, then, would look like this:
[Range(13, 90, ErrorMessage = "Age must be between 13 and 90")] public virtual int Age { get; set; }
Note that the StringLength and Range attributes also take an ErrorMessageResourceName value, in case error messages are stored in resources (which they should be, for easy internationalization).
Build, and run; notice how the UI will now automatically enforce these constraints. Even better, to the degree possible, the constraints will be enforced in the database, as well. Nifty!
In and of themselves, these attributes act essentially as data model validations, with a small amount of UI to support them. However, you often want to change up elements of the UI that have nothing to do with data validation, as well. For example, currently, the
12 msdn magazine


































































































   14   15   16   17   18