Blogs

Gary's Blog

October 2008 - Posts

  • XAF 8.3 Screenshots (at last!)

         
    Previously I said I'd post some screenshots of the great looking new web UI in XAF 8.3. Of course, I then got slammed at our PDC booth and didn't have to opportunity to post them. I know there are one or two of our customers who are dying to see them so, now that the PDC rush is over, I have the chance to sit down and and post them up. As you can see from the screenshots, the new UI is sweet and is an improvement on the old styling, I hope you agree. Of course the new UI doesn't just look cool, as of 8.3 we now support themes; with 8.3 this is the theme that will ship "in the box", with more coming in later releases. If anyone has any comments on the new look and feel, post them below!

    webui.jpg

  • The XAF Men

         

    Come to booth #501 at the PDC and meet the XAF Men.

  • DX PDC Prep

         
    At DevExpress we are flat out preparing for our sessions at PDC as you can see:

    DX Prep for PDC Sessions

    DX prep for PDC08

    You should certainly stop by booth #501 to see if these two clowns manage to sort out any sort of demo :-)

    Technorati Tags:

  • XAF at the PDC

         
    As you'll all know by now the DX team will be at Booth #501 at the PDC. Oliver and I will be there demonstrating XAF and XPO as well as talking about the new features coming down the pipe in 8.3 including Domain Components and the new web UI. If you have XAF related questions, or if you just want to say "hi", then stop by booth #501 and look us up.
  • XAF RWA – Not Asleep at the Wheel

         

    Hi, you may be wondering what has happened to this series of posts over the last few days. Never fear, I’ve not abandoned you. Simply, I leave tomorrow for LA for some work in the office and then the PDC and I realised that I didn’t have enough time to complete the next cycle before I left. Not to worry though, there’ll be lots of posts from me during my time in LA and as soon as I get back (November 3rd) we’ll get right back on with the next cycle of the RWA.

    In the meantime, stay tuned for posts from the PDC. Oliver and I also hope to get some videos “in the can” whilst we are in LA. Don’t forget, the fastest way to keep up with what’s new here is to follow @garyshort on Twitter and if you are a Twitterholic you can keep up with general DevExpress news by following @devexpress.

    Digg This
  • Gary's in Edinburgh Today!

         

    Yes I'm in Edinburgh today, firstly for the MSDN event, looking at what's new in Visual Studio SP1. I'll be there making sure our ORM product (XPO) is still ahead of Microsoft's (Entity Framework) :-) You can get full details of the event here.

    Then I'm sticking around for the Scottish Developers User Group meeting in the evening, where I'll be checking to see how the new ASP.Net MVC paradim compares to XAF when it comes to building web form applications. More details of this user group meeting are here.

    As always, if you are attending either event and you want to speak to me about Devexpress products then come and say hi; otherwise, I'll see you tomorrow for the next installment of the XAF RWA. Until then, have fun!

  • 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
  • XAF RWA Code #1

         

    Now that we’ve done our design, it’s time to code it up. So first thing to do is to add an empty solution to Visual Studio. Next, add to that a new test project (yes we’re going to to a bit of TDD). Now we are going to add the following test

    [TestMethod]
    public void TestDiaryHasName() {
        using (UnitOfWork uow = new UnitOfWork())
        {
            CourseDiary diary = new CourseDiary(uow);
            diary.Name = "Test Diary";
    
            Assert.IsNotNull(diary.Name);
        }           
    }

    This test will test whether or not our CourseDiary is capable of storing a name variable. When you type / paste this code then you will notice lots of problems with it in the first instance and the code will not compile. Let’s carry out some steps to ensure that it does build – using the TDD philosophy of doing the least thing that will make the test pass.

    Firstly add an XAF Windows Forms Application and in the Solution1.Module project add a domain object named CourseDiary. In CourseDiary add a string property (CR shortcut xps) named “Name”. Now go back to your test project and add references to the following assemblies:

    • Solution1.Module
    • DevExpress.Data.V8.2
    • DevExpress.Persistent.BaseImpl.v8.2
    • DevExpress.Xpo.v8.2

    Now add any using statements required. Having done that your test should compile, run and pass. If not, then fix any problems that the compiler or test runner throws your way and try again. Once you have gotten this test working we can move on to the next test.

    The next test will test the CourseDiary’s ability to store a description variable and it looks like this:

    [TestMethod]
    public void TestDiaryHasDescription() {
        using (UnitOfWork uow = new UnitOfWork())
        {
            CourseDiary diary = new CourseDiary(uow);
            diary.Description = "Description";
    
            Assert.IsNotNull(diary.Description);
        } 
    }

    Again the test will not compile until you add the Description property to the CourseDiary object. Having done the simplest thing that will make the test pass, your test should now compile, run and pass. Now repeat this process for the StartDate, EndDate and Outages properties, creating the following tests (The CR shortcut for a DateTime property is xpd8):

    [TestMethod]
    public void TestDiaryHasStartDate() {
        using (UnitOfWork uow = new UnitOfWork()) {
            CourseDiary diary = new CourseDiary(uow);
            diary.StartDate = DateTime.Parse("01/04/2009 00:00:01");
    
            Assert.IsNotNull(diary.StartDate);
        }
    }
    
    [TestMethod]
    public void TestDiaryHasEndDate() {
        using (UnitOfWork uow = new UnitOfWork()) {
            CourseDiary diary = new CourseDiary(uow);
            diary.EndDate = DateTime.Parse("31/10/2009 23:59:59");
    
            Assert.IsNotNull(diary.EndDate);
        }
    }
    
    [TestMethod]
    public void TestDiaryHasOutages() {
        using (UnitOfWork uow = new UnitOfWork()) {
            CourseDiary diary = new CourseDiary(uow);
            
            Assert.IsNotNull(diary.Outages);
        }
    }

    The Outages property is a special case because we first have to add a new domain object named Outage and then add a collection property to the CourseDiary (CR shortcut xpcl) like so:

    [Association("CourseDiary-Outages")]
    public XPCollection<Outage> Outages {
        get {
            return GetCollection<Outage>("Outages");
        }
    }

    And an attribute property to the Outage pointing back to the CourseDiary (CR shortcut xpa) like so:

    private CourseDiary _CourseDiary;
    [Association("CourseDiary-Outages")]
    public CourseDiary CourseDiary {
        get {
            return _CourseDiary;
        }
        set {
            SetPropertyValue("CourseDiary", ref _CourseDiary, value);
        }
    }

    Once you have complete this work, place the cursor in the test class name in the editor, right click and select “run tests”. All your tests should now run and pass, like so:

    image

    Now add the following test for the Course Object:

    [TestMethod]
    public void TestCourseHasName() {
        using (UnitOfWork uow = new UnitOfWork()) {
            Course course = new Course(uow);
            course.Name = "Test Course";
    
            Assert.IsNotNull(course.Name);
        }
    }
    
    [TestMethod]
    public void TestCourseHasDescription() {
        using (UnitOfWork uow = new UnitOfWork()) {
            Course course = new Course(uow);
            course.Description = "Test course description";
    
            Assert.IsNotNull(course.Description);
        }
    }
    
    [TestMethod]
    public void TestCourseHasDiary() {
        using (UnitOfWork uow = new UnitOfWork()) {
            Course course = new Course(uow);
            CourseDiary diary = new CourseDiary(uow);
            course.Diary = diary;
            
            Assert.IsNotNull(course.Diary);
        }
    }

    Add the appropriate properties to allow the tests to pass, remembering that CourseDiary has a 1:M relationship with Course which is expressed as follows on the Course:

    private CourseDiary _Diary;
    [Association("CourseDiary-Courses")]
    public CourseDiary Diary {
        get {
            return _Diary;
        }
        set {
            SetPropertyValue("Diary", ref _Diary, value);
        }
    }

    and as follows on the CourseDiary:

    [Association("CourseDiary-Courses")]
    public XPCollection<Course> Courses {
        get {
            return GetCollection<Course>("Courses");
        }
    }

    Now that we have built our Course and our CourseDiary classes, it’s time to look at the Outage that we stubbed out in order to complete the CourseDiary class. Let’s go ahead and create tests and properties for Name, Description, StartDate and StopDate following the pattern above.

    Now we can turn out attention to the constraints we had on our objects. One of constraints we had was that a Course had to have a CourseDiary. So to enforce that we can decorate the Diary property on the Course with the RuleRequiredField attribute, like so:

    private CourseDiary _Diary;
    [RuleRequiredField("Course must have Diary", DefaultContexts.Save)]
    [Association("CourseDiary-Courses")]        
    public CourseDiary Diary {
        get {
            return _Diary;
        }
        set {
            SetPropertyValue("Diary", ref _Diary, value);
        }
    }

    Having added a reference to the appropriate assembly (DevExpress.Persistent.Base.v8.2),  we can write a test to ensure that rule is enforced like this:

    [TestMethod]
    public void TestCourseMustHaveDiary() {
        using (UnitOfWork uow = new UnitOfWork()) {
            Course course = new Course(uow);
            RuleSet ruleSet = new RuleSet();
            RuleSetValidationResult rsvr = ruleSet.ValidateTarget(course, DefaultContexts.Save);
            Assert.AreEqual(false,rsvr.IsValid);
        }
    }

    Another constraint that we had was that we can’t delete a CourseDiary if it is referenced by a Course object. The first thing we have to do to enforce this is to add the RuleIsReferenced attribute to the CourseDiary class, like so:

    [RuleIsReferenced("No delete if referenced",DefaultContexts.Delete,typeof(Course),"Diary",InvertResult=true)]

    Notice the use of the InvertResult parameter which makes this rule “say” IsNotReferenced, which is what we want in this case. Now that we have decorated the class with this attribute, we can go ahead and write a test for it:

    [TestMethod]
    public void TestDiaryCantBeDeletedWhenReferenced() {
        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(diary, DefaultContexts.Delete);
            Assert.AreEqual(false, rsvr.IsValid);
        }            
    }

    Another constraint that we had was that we cannot edit a CourseDiary in such a way as to invalidate a Booking. At this point in time we don’t have the Booking class as this UC does not cover it. In such a scenario the developer, using TDD, would seek to mock out the the Booking class. In this case, however, I feel that to go off into the realms of mocks might cloud what we are doing here (if anyone feels strongly that I’m wrong, feel free to comment and I will consider using mocks in a later post), it would certainly make this post, which is now reaching epic proportion, significantly longer.

    So having decided to leave any dependency on the Booking class until we actually have that class, we are finished with our constraints. All that remains now is for us to add tests to Create, Edit and Delete our Course, CourseDiary and Outage classes (in the appropriate test classes of course) and we are done. I shan’t bother describing the tests here, they follow the pattern already set, I will link to the solution from this post and you can download and examine the these tests at your leisure.

    Having created these tests, let’s run all the test to see if our code is still working

    image

    Seeing that it is, we are now safe to run our solution and see what happens:

    image

    Yep, seems to be working fine!

    Okay so we’ve completed our use case and it’s time to ship our code to the testing server where our users can play with it. Is our code perfect? Well no, there are a lot of things I’ve left for our users to “find” and we will fix those in the refactoring phase. In the meantime, why don’t you leave a comment with the things that you find that need to be fixed? To do that you’ll need the solution of course. Have fun, and I’ll see you next time!

    Technorati tags: ,
    Digg This
  • XAF RWA Just in Time Design #1

         

    Now that we have our first use case, it’s time to do a little design work. The first task we have to complete is to identify object candidates, that is things that might be coded up as objects in our solution. The easiest way, I find, to do that is to list the nouns in the use case, so let’s do that now.

    The nouns are:

    1. Administrator
    2. Diary
    3. Course
    4. Booking
    5. Outage

    Examining the list above it is clear that the nouns 2 through 5 are good candidates for objects but Administrator is not so clear because, at this point, it is not obvious if Administrator should be an object or if it is an attribute of another, as yet undefined, object (Employee say). So, for now, we’ll not include it as an object, safe in the knowledge that if we change our minds, we can fix it during the refactoring phase.

    Booking too, though an obvious candidate, is not really part of this use case. Certainly both Diary and Outage objects have a dependency on Booking, as we’ll see later, but this dependency can be mocked during testing of this use case solution and so, for now, we can afford to ignore it.

    The next task we have to complete is to decide what behaviour our objects are going to have. A good way to get a “first pass” at object behaviour is to look at the verbs in the use case, so we’ll go ahead and do that.

    The verbs are:

    1. Select
    2. Create
    3. Logs on
    4. Edit
    5. Delete

    Now that we can see them clearly, we notice that we don’t have to pay them a lot of notice as the CRUD will be handled by XPO (the orm tool under XAF) and the “logs on” element we can assume we can handle via the security module (again safe in the knowledge that if we are wrong we can fix it in the refactoring phase).

    Now we need to look at what constraints we have on our objects. From the use case we can see that we have a constraint around tee-time bookings with regard to the fact that we can’t create or edit an outage, nor can we edit a diary if it is going to invalidate a tee-time booking. So let’s decide that we are not going to allow those processes to occur and let’s document that decision.

    Business Rule: 001

    Use Case Reference
    001 paragraphs 2.1.4, 2.3.3 and 2.4.4

    Rule
    Tee-time bookings cannot be invalidated by the creation or editing of an outage, nor by the editing of a diary.

    Action to be Taken
    On the aforementioned events a search will be made for affected bookings, if there are any then an error message will be presented to the user and the event cancelled.

    There is also a constraint around the deletion of course diaries in as much as they cannot be deleted if they are assigned to a course, so let’s go ahead and document that decision too.

    Business Rule: 002

    Use Case Reference
    001 paragraph 2.2.4

    Rule
    A course diary cannot be deleted if it is currently assigned to a course.

    Action to be Taken
    On the aforementioned event a search will be made for affected courses and if any are found then an error message will be presented to the user and the event cancelled.

    Having identified the objects, their knowledge (the properties they have) and their behaviour (the functions that can be called on them) and having documented certain business rules, the above post, along with the Use Case, represents a minimal set of documentation that an agile team (in this case us) can use to go ahead and create some code. And that is just what we’ll do in the next post.

    Digg This
  • XAF RWA Use Case #1

         

    It’s time to create our first use case, so where to start? Well it seems clear to me that a top down approach is needed here, after all you can’t book tee times on a course that doesn’t exist and you can’t book tee times on a course when you don’t know when it opens, right? So our first use case has to be for the administration system and has to be around the maintenance (create, read, update and delete functions) of our course diaries. The use case follows below. You will notice that I don’t favour the fully dressed style of use case for this project as I think it is “too much” for blog posting. Readers who wish to find out more about use cases should read the excellent book Writing Effective Use Cases by Alistair Cockburn (Wikipedia). Readers may also wish to read about (the more fashionable) user stories.

    Now I know this is going to upset the architectural fundamentalists, but I’m going to roll up related functionality into a single use case. I’m doing this for purely aesthetic reasons, to enable me to adhere to the four post policy that I described earlier. Just to keep us on the straight and narrow though, it is worth pointing out to our less experienced readers that, under normal conditions, each use case would deal with only one… well, use case :-)

    As an aside, for those of you who are unfamiliar with the business area (and I’m assuming that’s the majority) the course diary describes what day of the year the course opens and closes and the first and last tee times, as well as any “outages” (for competitions etc). This is because it takes time (say 5 hours) to complete a round of golf and the times at which you tee off, and thus the time you are expected to return to the club house, are set up so that you are playing in day light during both winter and summer. Okay, so let’s get on with it.

    Use Case: 001 Maintain Golf Course Diaries

    Primary Actor
    Administrator

    Priority
    High

    Frequency
    2/year

    Trigger
    Course diary maintenance required

    Main Success Scenario
    1. Administrator logs on to the system
    2. Administrator elects to create a new course diary
    3. Administrator creates a new course diary

    Extensions
    2.1.1 Administrator elects to edit a course diary
    2.1.2 Administrator selects a course diary
    2.1.3 Administrator edits a course diary
    2.1.4 Course diary is edited if it does not invalidate future tee time bookings

    2.2.1 Administrator elects to delete a course diary
    2.2.2 Administrator selects a course diary
    2.2.3 Administrator deletes a course diary
    2.2.4 Course diary is deleted if it is not in use

    2.3.1 Administrator elects to create an outage
    2.3.2 Administrator creates an outage
    2.3.3 Outage is create if it does not invalidate future tee time bookings

    2.4.1 Administrator elects to edit an outage
    2.4.2 Administrator selects an outage
    2.4.3 Administrator edits an outage
    2.4.4 Outage is edited if it does not invalidate future tee time bookings

    2.5.1 Administrator elects to delete an outage
    2.5.2 Administrator selects an outage
    2.5.3 Administrator deletes an outage
    2.5.4 Outage is deleted

    Digg This
  • XAF RWA – How we Will Proceed

         

    I just thought I’d drop you a short note to let you know how I propose to proceed with the XAF RWA. Each feature that we add will be done over four posts, the first post will contain the use case for the feature, the second post will contain the architectural decisions (as requested by some readers, more experienced readers may wish to skip this post), the third post will contain the build phase and finally, the fourth post will deal with any refactoring required.

    Digg This
  • The Real World App

         

    As I hinted at before I left for the MVP Open Day, I am now ready to start the “Real World App” (hereafter referred to as RWA). To ensure that it is indeed a real world app. I have decided to re-create a system I have worked on previously, that way everyone can see that indeed it is real word and the scenario has not been artificially constructed in order to show XAF in the best light. So, without further ado, here is the scenario we will be working on:-

    The coastal town of Duncodin-by-the-Sea (referred to as Duncodin from now on) lies in the north east of Scotland, not far from the historic home of golf at St. Andrews. Recently, a wealthy tycoon purchased several hundred acres of coastal property and intends to build three links golf courses there, one of which he hopes will be selected to be part of The (British) Open circuit.

    You have been selected to provide the Tee-time Booking System for this resort, and in particular you are to provide:-

    • A web  based tee-time booking system for use by visitors
    • A win forms based tee-time booking system for use by a small call centre
    • A win forms based administration system

    The requirements for this system are fluid and you are to use an agile approach to constructing this software to more easily facilitate the changes which will be required as the requirements firm up.

    I’ll publish the first use case for this system tomorrow.

    Digg This
  • UK MVP Day and Evangelism the “Old Fashioned Way”

         

    So I’m back from the MVP Open Day, did you miss me? Whadda ya mean no?! LOL. Anyway, on the plane home I was sitting behind a woman from Edinburgh and a gentleman from the US. As they conversed during the journey, it became apparent to them both that they had once lived in the same small town in the US. “Wow, that’s amazing” proclaimed the woman; not really thought I, if you’d come to my “Science of Social Networking” talk then you’d have known that the work of Milgram et al, makes it quite likely actually.

    Speaking of my talk, I turned up running a little late, as the chap before me had over run by 10 minutes, to find that the projector had taken umbrage at the very thought of a Mac being connected to a piece of Microsoft equipment, and point blank refused to speak to it at all. Not to worry, the AV tech. soon arrived on the scene, putting my mind at ease. However, my ease was to be short lived as we had the following conversation:

    Him: “Having problems getting connected mate?”
    Me: “Yeah”
    Him: “Not to worry, we’ll have you set up in a jiffy”
    Me: “Good, as we’re running a bit late and I should have started by now”
    Him: “Umm, hang on, this is a Mac”
    Me: “That’s right, it is”
    Him: “Umm, yeeeeeeeees, well you see…. sorry mate, no idea, bye!”
    Me: “Okay, anyone got a white board marker?”

    I then proceeded to deliver my talk the “old fashioned” way, just chalk and talk (well white board, pen and talk, but you get the idea). As it happens the audience seemed pretty happy with that approach as the day had been a little like “death by Powerpoint” up until then, and I only really needed to “chalk” up my equations. Just to add a little bit of pressure (‘cos otherwise it would have been easy right?) Toby Richards (General Manager, Community Support Services for Microsoft) was in the room. I think it must have gone okay though, as he asked me to send him my slide deck. Of course, he maybe wanted to check that I had one in the first place and wasn’t just conning everyone. :-)

    Digg This
  • UK MVP Open Day

         

    Tomorrow I’m off to the UK MVP Open Day where I’ll be speaking on the topic of “The Science of Social Networking” if you are UK based and going to be there then don’t forget to come and say hello.

    On a different note, I’ve decided what our “Real World Application” is going to be; it’s a real “real world” application, I’ve just changed some of the names to protect the innocent, that way no one can accuse me of making the scenario fit the strengths of XAF. But this is just a teaser of course, there’ll be more information on this front on Monday when I’m back from the Open Day. Until then, happy coding! :-)

    Technorati tags: ,
    Digg This
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.