In my last post we created the code to satisfy the first use case and JIT design document. Now that our users have had the application for a while, they have sent us a number of issues that need to be addressed, they are:-
- The fields in the GUI need to be re-ordered to be more “sensible”.
- A Course name must be mandatory and unique.
- A CourseDiary name must be mandatory and unique.
- Start and End dates are mandatory on CourseDiary and Outage.
- The date range on an Outage must be with the range of the CourseDiary dates.
- Where an object has a start date and an end date; end date must be greater than start date.
- An Outage is related to a specific CourseDiary and should not be displayed in the navigation bar.
- The error message for the CourseDiary reference rule is the default one and has not be inverted like the rule itself.
Okay, let’s take these issues in order. To fix the ordering of the fields in the GUI, open the model editor, expand the “views” node, expand the “Course_DetailView node”, select the “layout” node and in the property pane right click and select “Customize Layout”. Now, drag the elements into the order you feel happy with. Repeat these steps for the CourseDiary and Outage detail views.
Next, let’s deal with making the Course and CourseDiary name mandatory and unique. This is done with the use of two rule attributes, RuleRequiredFieldAttribute and RuleUniqueValueAttribute. So let’s go ahead and add these attributes to the Course and CourseDiary classes:
private string _Name;
[RuleRequiredField("Course name required",DefaultContexts.Save,"A name is required!")]
[RuleUniqueValue("Course name must be unique",DefaultContexts.Save,"Course name already exists!")]
public string Name {
get {
return _Name;
}
set {
SetPropertyValue("Name", ref _Name, value);
}
}
Having done that, let’s add tests for these rules:
[TestMethod]
public void TestCourseMustHaveName() {
using (UnitOfWork uow = new UnitOfWork()) {
Course course = new Course(uow);
CourseDiary diary = new CourseDiary(uow);
course.Diary = diary;
RuleSet ruleSet = new RuleSet();
RuleSetValidationResult rsvr =
ruleSet.ValidateTarget(course, DefaultContexts.Save);
Assert.AreEqual(false, rsvr.IsValid);
}
}
[TestMethod]
public void TestCourseNameMustBeUnique() {
using (UnitOfWork uow = new UnitOfWork()) {
Course course = new Course(uow) { Name = "Test" };
CourseDiary diary = new CourseDiary(uow);
course.Diary = diary;
uow.CommitChanges();
}
using (UnitOfWork uow = new UnitOfWork())
{
Course course = new Course(uow) { Name = "Test" };
CourseDiary diary = new CourseDiary(uow);
course.Diary = diary;
RuleSet ruleSet = new RuleSet();
RuleSetValidationResult rsvr =
ruleSet.ValidateTarget(course, DefaultContexts.Save);
Assert.AreEqual(false, rsvr.IsValid);
}
}
Add similar tests for CourseDiary, then add the attributes and tests to ensure the start and end dates are mandatory on the Outage and the CourseDiary. Having done that, we can now add tests to ensure that the start date is greater than the end date. To do this decorate the class with the RuleCriteria attribute, like so:
[RuleCriteria(
"Outage end date must be greater than start date",
DefaultContexts.Save,
"EndDate > StartDate")]
And create a test to go with it:
[TestMethod]
public void TestOutageEndDateMustBeGreaterThanStartDate() {
using (UnitOfWork uow = new UnitOfWork()) {
DateTime now = DateTime.Now;
Outage outage = new Outage(uow) { StartDate = now, EndDate = now };
RuleSet ruleSet = new RuleSet();
RuleSetValidationResult rsvr =
ruleSet.ValidateTarget(outage, DefaultContexts.Save);
Assert.AreEqual(false, rsvr.IsValid);
}
}
Follow the same pattern to create rules and tests to ensure that the Outage start date is after the CourseDiary start date, to ensure that the Outage start date is before the CourseDiary end date and to ensure that the Outage end date is before the CourseDiary end date.
To ensure that the Outage object does not appear in the navigation bar, decorate the Outage class with the NavigationItem attribute as follows:
[NavigationItem(false)]
public class Outage : BaseObject
Lastly, we need to fix the error message for the CourseDiary by providing a specific message in the Rule:
[RuleIsReferenced(
"No delete if referenced",
DefaultContexts.Delete,
typeof(Course),
"Diary",
"A Course Diary cannot be deleted if it is being used!",
InvertResult=true)]
Right, now that we’ve done all the refactoring work, let’s make sure our tests still pass:
They do! Well that about wraps it up for this post, in the next post we’ll start the next use case, in the meantime you can find the refactored solution here.