March 2019

Volume 34 Number 3

[The Working Programmer]

Coding Naked: Naked Properties

By Ted Neward | March 2019

Ted NewardWelcome back, NOFers. (I’ve decided that sounds better than calling those who use naked objects “naked coders.”) In the last piece, 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!

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 methods on the class. If the parental-leave property is named OnLeave, then the method that NOF will execute to determine whether to disable the OnLeave property would be called DisableOnLeave.

Let’s see how this works out in practice.

Naked Speakers, Redux

Currently, the Speaker class has just three properties on it, FirstName, 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 attribute 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 attributes on the Speaker object are displayed in alphabetical order, which doesn’t make a ton of sense. It would be far more realistic (and useful) if the first value displayed was the full name, followed by the individual fields for first name, last name and age (as well as any other demographic information you need to capture and use).

While this could certainly become the “Welp, that was fun, time to break down and build our own UI” moment, it doesn’t need to—this is a common requirement, and NOF has it covered, via the MemberOrder attribute. Using this attribute, I can establish an “order” in which attributes should appear in the UI. So, for example, if I want the FullName attribute to appear first in the UI, I use MemberOrder and pass in the relative ordinal position “1,” like so:

[Title]
[MemberOrder(1)]
public string FullName { get { return FirstName + " " + LastName; } }

Next, I’d like to display first name, last name and age, but here I can begin to run into a problem. As I add new fields to this class over time (say, “middle name” or “email”), trying to keep all the ordinal positions in order can be tricky—if I move LastName to position 5, I have to go find everything 5 (and after) and bump each one to get the right positions. That’s a pain.

Fortunately, MemberOrder has a nifty little trick to it: The position itself can be a floating-point value, which allows fields to be “grouped,” so that now I can mark “FirstName,” “LastName,” and “Age” as ordinal positions “2.1,” “2.2,” and “2.3,” respectively, which essentially means that group “2” can be demographic information, and any new demographic information about the Speaker only requires reshuffling of the members in that particular group, as Figure 1 shows.

Figure 1 Grouping Fields

[MemberOrder(2.1)]
[StringLength(100,
  ErrorMessage = "First name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string FirstName { get; set; }
[MemberOrder(2.2)]
[StringLength(100,
  ErrorMessage = "Last name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string LastName { get; set; }
[Range(13, 90, ErrorMessage = "Age must be between 13 and 90")]
[MemberOrder(2.3)]
public virtual int Age { get; set; }

Note that there’s nothing really special about the values themselves—they’re used relative to one another and don’t represent any particular location on the screen. In fact, I could’ve used 10, 21, 22 and 23, if I wanted to. NOF is careful to point out that these values are compared lexicographically—string-based comparisons—and not numerically, so use whichever scheme makes sense to you.

What if users aren’t sure whether Age is in years or in days? It may seem completely obvious to you, but remember, not everybody looks at the world the same way. While it’s probably not a piece of information that needs to be present on the UI overtly, it should be something that you can signal to the user somehow. In NOF, you use the “DescribedAs” attribute to signal how the property should be described, which typically takes the form of a tooltip over the input area. (Remember, though, a given NOF client might choose to use a different way to signal that; for example, if a NOF client emerges for phones, which are touch-centric, tooltips don’t work well for that format. In that situation, that hypothetical NOF client would use a different mechanism, one more appropriate for that platform, to describe the property.)

And Speakers need a bio! Oh, my, how could I forget that—that’s like the one time speakers get to write exclusively about themselves and all the amazing things they do! (That’s a joke—if it’s one thing speakers hate most of all, it’s writing their own bio.) Bio is an easy attribute to add to the class, but most bios need to be more than just a word or two, and looking at the UI generated by NOF so far, all of the other strings are currently single-line affairs. It’s for this reason that NOF provides the “MultiLine” attribute, to indicate that this field should be displayed in a larger area of text entry than the typical string.

But I need to be careful about, in this case, a speaker’s biography, because free-form input offers the possibility for abuse: I might want/need to screen out certain words from appearing lest people get the wrong impression about the conference. I simply can’t have speakers at my show if their biographies include words like COBOL! Fortunately, NOF will allow for validation of input by looking for, and invoking, methods on the Speaker class that match a Validate[Property] convention, like so:

[MemberOrder(4)]
[StringLength(400, ErrorMessage = "Keep bios to under 400 characters, please")]
public virtual string Bio { get; set; }
public string ValidateBio(string bio)
{
  if (bio.IndexOf("COBOL") > -1)
    return "We are terribly sorry; nobody wants to hear that";
  else
    return "";
}

Wrapping Up

NOF has a pretty wide variety of options available to describe a domain object in terms that make it easier to automatically render the appropriate UI to enforce domain limitations, but thus far the model here is pretty simple. In the next piece, I’ll examine how NOF can handle a more complicated topic, that of relationships between objects. (Speakers need to be able to specify Topics they speak on, for example, and most of all, the Talks they propose.) But I’m out of space for the month, so in the meantime … Happy coding!


Ted Neward is a Seattle-based polytechnology consultant, speaker and mentor. He has written a ton of articles, authored and co-authored a dozen books, and speaks all over the world. Reach him at ted@tedneward.com or read his blog at blogs.tedneward.com.

Thanks to the following technical expert who reviewed this article: Richard Pawson
Richard Pawson’s career in computing began in 1977 when he went to work for a company making pocket calculators; three weeks after he joined, the company announced the world’s first personal computer: the Commodore PET. In the intervening years, Richard has been a technology journalist, robotics engineer, electronic toy designer, management consultant, and software developer.  Naked Objects started as his PhD thesis in 2003, and Richard has managed the development of the open source Naked Objects framework since then. In his ‘spare time’ he is restoring a 1939 Daimler Drop Head Coupe that originally belonged to England’s King George VI.


Discuss this article in the MSDN Magazine forum