Blogs

Gary's Blog

December 2009 - Posts

  • Santa Gets it Done With XPO

         

    After my last post I said we’d take a look at one to many associations next time, and that’s what we are doing to do now. With it coming up to Christmas I thought we would write a little application to give Santa a hand with his deliveries – I mean the poor old guy’s got a lot of work to do so I figure he could use all the help he can get, right?

    So we are going to write an application that will provide him with delivery instructions, detailing which presents to deliver to which house. To do that, the first thing we need is a class to define a house:

    using System;
    using DevExpress.Xpo;
    
    namespace XPOChristmas
    {
        public class House : XPObject
        {
            public House(Session session)
                : base(session)
            { }
    
            private string address;
            public string Address
            {
                get
                {
                    return address;
                }
                set
                {
                    SetPropertyValue("Address", ref address, value);
                }
            }
    
            private Country country;
            public Country Country
            {
                get
                {
                    return country;
                }
                set
                {
                    SetPropertyValue("Country", ref country, value);
                }
            }        
    
            [Association("House-Presents")]
            public XPCollection<Present> Presents
            {
                get
                {
                    return GetCollection<Present>("Presents");
                }
            }
    
            public void ShowDeliveryInstructions()
            {
                Console.WriteLine(String.Format("Santa, deliver to {0} the following: ", Address));
                foreach (Present p in Presents)
                {
                    Console.WriteLine(String.Format("   {0}", p.Description));
                }
                Console.WriteLine();
            }        
        }
    }

    As you can see, this class extends XPObject just like you’ve seen in all the XPO example so far. It also has a property detailing the address of the house. This is a simple string property that we’ve seen many times before, so we need not say any more about that.

    We need not say too much about the country property either, it simply holds an enum describing the country in which the house is located, the definition of the enum is very simple:

    using System;
    
    namespace XPOChristmas
    {
        public enum Country
        {
            Scotland,
            England,
            Ireland,
            Wales
        }
    }

    The next property however, is a little special. This property holds the child relationship with all the presents that are due to be delivered to this house. The first thing to notice about the property is that it is decorated with the Association attribute. This attribute must be unique in the database and the string name must match on both the parent and child ends of the association. Note the use of the helper function GetCollection<T> which returns all the children in the association.

    The last thing to notice about this class is the ShowDeliveryInstructions() method that displays instructions for the house and then walks the graph of children and displays instructions for them.

    The next thing we need then is a class to describe the presents:

    using System;
    using DevExpress.Xpo;
    
    namespace XPOChristmas
    {
        public class Present : XPObject
        {
            public Present(Session session)
                : base(session)
            { }
    
            private string description;
            public string Description
            {
                get
                {
                    return description;
                }
                set
                {
                    SetPropertyValue("Description", ref description, value);
                }
            }
    
            private House house;
            [Association("House-Presents")]
            public House House
            {
                get
                {
                    return house;
                }
                set
                {
                    SetPropertyValue("House", ref house, value);
                }
            }
    
        }
    }

    This class has a Description property which we need not say too much about and also a property which points to it’s parent house. Note the association attribute decorating the property and pay particular attention to the fact that the name is the same name that is on the parent end of the association.

    Once we have those two classes we just need code to drive it all:

    using System;
    using System.Linq;
    using DevExpress.Xpo;
    
    namespace XPOChristmas
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                //GS - Persist some houses and presents for Santa
                using (UnitOfWork uow = new UnitOfWork())
                {
                    House house = new House(uow)
                    {
                        Address = "27 Cromarty Road",
                        Country = Country.Scotland
                    };
    
                    new Present(uow)
                    {
                        Description = "Little Blue Car",
                        House = house
                    };
    
                    new Present(uow)
                    {
                        Description = "Big Book For Boys",
                        House = house
                    };
    
                    House nextHouse = new House(uow)
                    {
                        Address = "27 Chelsea Lane",
                        Country = Country.England
                    };
    
                    new Present(uow)
                    {
                        Description = "Little Pink Car",
                        House = nextHouse
                    };
    
                    new Present(uow)
                    {
                        Description = "Big Book For Girls",
                        House = nextHouse
                    };
    
                    uow.CommitChanges();
                }
    
                //GS - Get delivery instructions for all houses
                Console.WriteLine("Displaying all houses...");
                Console.WriteLine();
                using (UnitOfWork uow = new UnitOfWork())
                {
                    XPQuery<House> houseQuery = new XPQuery<House>(uow);
    
                    foreach (var house in houseQuery)
                    {
                        house.ShowDeliveryInstructions();
                    }
                }
    
                //GS - Get delivery instructions for the house in Scotland
                Console.WriteLine("Displaying Scottish houses...");
                Console.WriteLine();
                using (UnitOfWork uow = new UnitOfWork())
                {
                    XPQuery<House> houseQuery = new XPQuery<House>(uow);
    
                    var houses = from h in houseQuery 
                                where h.Country == Country.Scotland 
                                select h;
    
                    foreach(var house in houses )
                    {
                        house.ShowDeliveryInstructions();
                    }
                }
    
            }
        }
    }

    In this code we persist some house and present objects and then retrieve them from the database before asking them to display their delivery instructions. There is nothing really special to say about this code, we have seen many such examples. The only thing to note is that the one to many associations can be specified from either end. In this case I specify it from the child side, because I found that most convenient, but either will do.

    Now that I have shown you the code, all that remains is to run it:

    image

    And that is it really, with such good help how could Santa fail to get all his presents delivered on time? That brings this post to a close, so until next time, happy XPOing! :-)

  • XPO Rocks Your World With Earthquakes

         

    In the last post I made, looking at analysing weather information using XPO, one of the commenters asked if we could see more examples using different types of associations between objects, so I thought I’d start off by posting something on one to one associations. To do this we are going to look at earthquakes (well why not?).

    Now the USGS are good enough to provide us with a feed of earthquakes in, pretty much, real time. Since the bigger the better, we’ll look at quakes magnitude 5 and above happening in the previous 7 days. The USGS publishes an atom feed of these at: http://earthquake.usgs.gov/earthquakes/catalogs/7day-M5.xml.

    To use this feed, the first thing we are going to do is to define an Earthquake entity and a GeoPoint entity, these entities will have a one to one relationship:

    using System;
    using DevExpress.Xpo;
    
    namespace EarthquakeFetcher.Model
    {
        public class EarthQuake: XPObject
        {
            public EarthQuake(Session session)
                : base(session)
            { }
    
            private string title;
            public string Title
            {
                get
                {
                    return title;
                }
                set
                {
                    SetPropertyValue("Title", ref title, value);
                }
            }
    
            private DateTime timeStamp;
            public DateTime TimeStamp
            {
                get
                {
                    return timeStamp;
                }
                set
                {
                    SetPropertyValue("TimeStamp", ref timeStamp, value);
                }
            }
    
            private GeoPoint geoPoint;
            public GeoPoint GeoPoint
            {
                get
                {
                    return geoPoint;
                }
                set
                {
                    if (geoPoint == value)
                        return;
    
                    //GS - Store a reference to the former geoPoint.
                    GeoPoint previousGP = geoPoint;
                    geoPoint = value;
    
                    if (IsLoading) return;
    
                    //GS - Remove the previous reference if there is one.
                    if (previousGP != null && previousGP.EarthQuake == this)
                        previousGP.EarthQuake = null;
    
                    //GS - Attach this reference
                    if (geoPoint != null)
                        geoPoint.EarthQuake = this;
    
                    //GS - Signal the change
                    OnChanged("GeoPoint");
                }
            }
    
            //GS - Override ToString() to give us a pretty label
            public override string ToString()
            {
                return Title;
            }
    
            
        }
    }
    using System;
    using DevExpress.Xpo;
    
    namespace EarthquakeFetcher.Model
    {
        public class GeoPoint : XPObject
        {
            public GeoPoint(Session session)
                : base(session)
            { }
    
            private string point;
            public string Point
            {
                get
                {
                    return point;
                }
                set
                {
                    SetPropertyValue("Point", ref point, value);
                }
            }
    
            private int elevation;
            public int Elevation
            {
                get
                {
                    return elevation;
                }
                set
                {
                    SetPropertyValue("Elevation", ref elevation, value);
                }
            }
    
            private EarthQuake earthQuake;
            public EarthQuake EarthQuake
            {
                get
                {
                    return earthQuake;
                }
                set
                {
                    if (earthQuake == value)
                        return;
    
                    //GS - Store a reference to the former EarthQuake.
                    EarthQuake previousQuake = earthQuake;
                    earthQuake = value;
    
                    if (IsLoading) return;
    
                    //GS - Remove the previous reference if there is one.
                    if (previousQuake != null && previousQuake.GeoPoint == this)
                        previousQuake.GeoPoint = null;
    
                    //GS - Attach this reference
                    if (earthQuake != null)
                        earthQuake.GeoPoint = this;
    
                    //GS - Signal the change
                    OnChanged("EarthQuake");
                }
            }
        }
    }

    Note the syntax on the one to one associations. Now then, the next thing we have to do is to read the feed, create objects from it and persist them:

    using System;
    using System.Xml.Linq;
    using DevExpress.Xpo;
    using EarthquakeFetcher.Model;
    
    namespace EarthquakeFetcher
    {
        class Program
        {
            static void Main()
            {                      
                //GS - Fetch the feed
                XDocument quakeFeed = 
                    XDocument.Load(@"http://earthquake.usgs.gov/earthquakes/catalogs/7day-M5.xml");
    
                //GS - Add the required namespaces
                XNamespace nsAtom = "http://www.w3.org/2005/Atom";
                XNamespace nsGeoRSS = "http://www.georss.org/georss";
    
                //GS - Persist the quake information
                using (UnitOfWork uow = new UnitOfWork())
                {
                    foreach (var quake in quakeFeed.Descendants(nsAtom + "entry"))
                    {
                        new EarthQuake(uow)
                         {
                             Title = quake.Element(nsAtom + "title").Value,
                             TimeStamp = DateTime.Parse(quake.Element(nsAtom + "updated").Value),
                             GeoPoint = new GeoPoint(uow)
                             {
                                 Point = quake.Element(nsGeoRSS + "point").Value,
                                 Elevation = Convert.ToInt32(quake.Element(nsGeoRSS + "elev").Value)                             
                             }
                         };
                    }
                    uow.CommitChanges();                
                }
            }
        }
    }

    There isn’t anything in the above code worth commenting on I think, just note the addition of the namespaces for atom and GeoRSS. Running this code will cause the present list of quakes to be persisted. Note, this code is for demonstration and instructional purposes only and has had “guard” code removed for clarity. If you were to put this code into production you would have to handle the case of duplicate quake entries being persisted if you were to run this code more than once in any 7 day period.

    Now that we have our persisted entities, it’s time to use them. First, we’ll create a view to display them:

    image

    The code behind this form looks like this:

    using System;
    using System.Windows.Forms;
    using EarthquakeFetcher.Model;
    using System.Diagnostics;
    
    namespace EarthquakeViewer
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                FillListBox();
                SelectFirstItemInListBox();
            }
    
            private void FillListBox()
            {
                QuakeListBox.Items.AddRange(EarthquakeHelper.GetAllEarthquakes.ToArray());            
            }
    
            private void SelectFirstItemInListBox()
            {
                if(QuakeListBox.Items.Count > 0)
                    QuakeListBox.SelectedIndex = 0;
            }
    
            private void ShowOnMapButton_Click(object sender, EventArgs e)
            {
                EarthQuake quake = QuakeListBox.SelectedItem as EarthQuake;
                ShowQuakeOnMap(quake);
            }
    
            private static void ShowQuakeOnMap(EarthQuake quake)
            {
                if (!String.IsNullOrEmpty(quake.GeoPoint.Point))
                    Process.Start(String.Format(@"http://maps.google.com/?q={0}", quake.GeoPoint.Point));
            }
    
            private void MapMaxElevationButton_Click(object sender, EventArgs e)
            {
                ShowQuakeOnMap(EarthquakeHelper.GetHighestElevatedQuake);
            }
    
            private void MapMinElevationButton_Click(object sender, EventArgs e)
            {
                ShowQuakeOnMap(EarthquakeHelper.GetLowestElevatedQuake);
            }
        }
    }

    So, from the code we can see that we have a helper class, EarthquakeHelper, that gives us a few helper methods, let’s take a look at those now:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using DevExpress.Xpo;
    using EarthquakeFetcher.Model;
    
    namespace EarthquakeViewer
    {
        public class EarthquakeHelper
        {
            //GS - Return all quakes. This is safe as there will not be too many in a 7 day period.
            public static List<EarthQuake> GetAllEarthquakes
            {
                get
                {
                    XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                    return (from q in quakeQuery select q).ToList<EarthQuake>();
                }
            }
    
            public static EarthQuake GetHighestElevatedQuake
            {
                get
                {
                    XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                    return (from q in quakeQuery
                        orderby q.GeoPoint.Elevation descending
                        select q).First<EarthQuake>();
                }
            }
    
            public static EarthQuake GetLowestElevatedQuake
            {
                get
                {
                    XPQuery<EarthQuake> quakeQuery = new XPQuery<EarthQuake>(XpoDefault.Session);
                    return (from q in quakeQuery
                            orderby q.GeoPoint.Elevation ascending
                            select q).First<EarthQuake>();
                }
            }
        }
    }

    You can see that the GetAllEarthquakes property does what is says on the tin, it returns all of the quakes in the database. Again all “guard” has been removed for clarity and we are assuming that the database only contains the last 7 days worth of data. The other two properties use linq to XPO to return the highest and lowest earthquakes by elevation. These three properties return quakes that are then used by the three buttons on the form to show the location, via Google maps, of the required earthquakes:

    image

    This post is not only interesting from an XPO point of view, but also from an earthquake point of view. I mean, who of you knew there were so many earthquakes happening right now? Anyway, that’s it for this post, in the next one we’ll take a closer look at at one to many and many to many associations. Until then, happy XPOing!

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.