in
Forums
Blogs
Files
Devexpress.Com
ClientCenter
Support Center
DevExpress Channel

Gary's Blog

XAF RWA Refactor #1

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:-

  1. The fields in the GUI need to be re-ordered to be more “sensible”.
  2. A Course name must be mandatory and unique.
  3. A CourseDiary name must be mandatory and unique.
  4. Start and End dates are mandatory on CourseDiary and Outage.
  5. The date range on an Outage must be with the range of the CourseDiary dates.
  6. Where an object has a start date and an end date; end date must be greater than start date.
  7. An Outage is related to a specific CourseDiary and should not be displayed in the navigation bar.
  8. 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:

image 

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.

Technorati tags: ,
Digg This
Published Oct 14 2008, 05:17 PM by Gary Short (Developer Express)

Comments

 

Alain Bismark said:

Out off topic??

Hi Gary, just to be curious..

Can you describe wich is the "optimun hardware architecture" to develop with VS 2008 + XAF + SQL?

What kind of machines use devex or recomend devex to programmers?

RAM size?, Processor?, etc, etc...

Regards

October 14, 2008 1:30 PM
 

Gary Short (Developer Express) said:

We don't have a recommended spec, Alain, but I can tell you what I use. I use a Thinkpad Z61P with Intel Core 2 CPU, 2GHz and 3Gb RAM.

October 14, 2008 2:30 PM
 

drew.. said:

hey Gary, in trying to allow TDD to sink in, in this case with TestCourseNameMustBeUnique, since the uow commits, won't this only work one time? Should you perhaps either run a delete first to ensure it is not there, or wrap the code in a nested uow to not actually commit this? Or would the nested uow catch the conflict with the parent uow prior to commitment? I am butting heads with the uow and its' sessions at the moment, so any thoughts here are welcome.

Second thought.. generally i would timestamp a variable then assign the variable to both start and end dates in the TestOutageEndDateMustBeGreaterThanStartDate test to ensure they are the same, or at least set the end date first, since you are looking to assert a false value.  cheers!

October 14, 2008 6:11 PM
 

Luc DEBRUN said:

After a round of user cases, code and refactoring, I would like to say thanks and encourage more to come.

Just may be a little more details on the XAF code and a tidbit less on the TDD.

Thanks again.

Luc

October 14, 2008 9:29 PM
 

Gary Short (Developer Express) said:

@drew re TestCourseNameMustBeUnique, yes you are correct it will only work once, but then it's only meant to. The test project would have  "build up" and "tear down" sections which preps for the tests and then tidies up afterwards, so the database would get dropped at the end of each test run, so the test starts from a "clean slate" each time.

If you have downloaded the code and looked at it, you will see that there is no "tear down" section in my tests. :-) This is becuase the test project uses raw XPO (the ORM layer under XAF) and, by default, XPO persists to an Access database. I tried writing code to delete the file at the end of the tests but VS has a "hold" of the file so it can't be deleted. In future refactoring I'm going to force XPO to use the local SQL server, I just didn't want to add too much information at once as it were. So, for now, I delete the Access database manually at the end of the test. I should have mentioned this, but couldn't think of a good way to explain it without getting lost in all the details, so thanks for surfacing this. As I say, now that we are comfortable with the process, I'll change the test database in the next round.

As for your suggestion re TestOutageEndDateMustBeGreaterThanStartDate, isn't that what I'm doing?

October 15, 2008 5:30 AM
 

Gary Short (Developer Express) said:

@Luc More TDD! Less TDD! More architecture! Less architecture! Tell us more about the components! Never mind the components, show us real world apps! Jeez, you guys are hard to write for! :-)

October 15, 2008 5:33 AM
 

Luc DEBRUN said:

Sorry Gary ... I did not mean to make it hard on you. Just reminded you that the focus of this is XAF, not TDD. And I think I said "tidbid less" not "none."  

How about I say more XAF then and you can keep all your TDD in there?

:)

Luc

October 15, 2008 8:27 AM
 

Bernard Simmons said:

Gary,

#1. Keep up the good work, the balance is fine for me as I have learned that  spending more time in TDD the lower my support has become.

#2. The download link does not work.

Bernard

October 15, 2008 11:54 AM
 

Gary Short (Developer Express) said:

@Bernard thanks for the heads up re the link. I've fixed it, it was the stupid hash character causing problems.

October 15, 2008 12:13 PM
 

drew.. said:

hey Gary, perhaps TDD needs its' own cleanup functional UnitOfWork or equivalent. The uow issue if a wee bit frustrating as i was recently told by SC to not use it in favour of "native XAF" ...

But back to my second point, but issuing two separate Now's you run the risk of  create two distinct values.. if you are simply looking to ensure xaf is saving properties (which is about 90% of TDD it seems), then i would have created one variable, set to Now, then assign this value to both properties.

I will be adopting this TDD approach, but will be trusting CodeRush and XAF for the 90% activity of simple property storage.. This make more sense to me at this stage..  Thanks for the feedback to my thoughts, it is very much appreciated..

October 15, 2008 12:14 PM
 

Gary Short (Developer Express) said:

@Luc, don't worry I was just pulling your leg, I'll try and keep the balance, though you'll note that a lot of the test have a fair amount of XAF code in them too. :-)

October 15, 2008 12:15 PM
 

Gary Short (Developer Express) said:

@Drew "i would have created one variable, set to Now, then assign this value to both properties."

But that's what I did...

"DateTime now = DateTime.Now;

Outage outage = new Outage(uow) { StartDate = now, EndDate = now };"

As for not using UOW in "native XAF" this is quite correct, however, we don't have that option from within our test project.

October 15, 2008 12:20 PM
 

drew.. said:

holy crap.. my bad ;).. how the heck did i miss that? Let's blame it on Luc! i take back all my mutterings   ..  

October 15, 2008 4:29 PM
 

Luc DEBRUN said:

@Gary: Yes I know you were pulling my legs. But I was not pulling yours (wink i.e. I am now). I was trying to explain that I am ok with you providing MORE of EVERYTHING (XAF first, and TDD if you want).

More it is then, and in exchange, as a faithfull UN staff, I will accept ALL blame for ALL actions by ANY third party as Drew mentioned. That's what we are here for anyway.

Luc

October 15, 2008 8:57 PM
 

Alberto Cortes said:

Gary, Will you continue with your XAF RWA exercises? (I hop yes because very ilustrative).

Thanks

December 2, 2008 7:17 AM

Leave a Comment

(required)  
(optional)
(required)  
Verification code: Required
   
Add
Copyright © 1998-2008 Developer Express Inc.
ALL RIGHTS RESERVED