in
Forums
Blogs
Files
Devexpress.Com
ClientCenter

Gary's Blog

  • You say Potato I say Potahto: Localization in the eXpress Application Framework

    On a recent film making trip to LA, we were out for dinner one night when the waiter asked me what kind of salad I would like, I replied that I would like tomato. Ray, good humoredly, corrected me and said it was tomayto not tomato. Of course he is right, not because he is an expert in the Americanization of the English language, but because he pays my salary and that trump card is enough to hold sway in most arguments. However, it did reminded me of Churchill’s old adage that the U.K. and the U.S. are two countries separated by a common language.

    However much that may or may not be true, it is a fact that in our industry, language can be a separator separating countries (and therefore people) into the haves and the have nots depending on whether a particular piece of software has been localized for their country or not. To solve this problem an industry has grown up around internationalization and localization, with books on the subject and tools to help the developer with the process; there are also a number of industry standards to aid collaboration.

    When it comes to the localization of software, and that is the only kind of localization we are dealing with in this post, there is a fairly straight forward workflow to follow:-

    Workflow

    So does XAF support this localization workflow? Well the answer is yes but not entirely. It is clear that applications created with XAF are internationalized, that is to say, the up front work of designing and creating an application to be localizable, has been done for you. It is also clear that the step of extracting localizable content into a central data store has been facilitated through the use of the Model and because the Model Editor can be used as a stand alone product, a developer could use that mechanism in order to allow a translation agent to work with the localizable content. So, to quite a considerable degree, this workflow is supported within XAF.

    However, XAF is not perfect (what software product is?) and it is clear that the area in which it lacks, is in its ability to export the localizable content, in a standards compliant medium, for onward transmission to a translation agent; nor does XAF have a mechanism by which to import the translated content back into the Model. Now we do plan to fully support this workflow at some point in the future, it’s just that the opportunity to include it has not presented itself yet.

    Although I believe this to be a minor deficit in the productivity gains that XAF brings to the developer, I feel the time has come to rectify this deficit; so, in the coming days I am going to design, build, test and publish a tool that will allow the export and import of localizable content. This will allow XAF to fully support this localization workflow. I will be blogging my progress in future posts, so those of you who have an interest in this topic can follow along at home.

    In the meantime, you can read more about XAF’s support for localization in our documentation.

    Digg This
  • New video: Too little pink in your business applications? Customize them with the eXpressApp Framework!

    This final video in the series of "things we did in LA" demonstrates various levels on which you can customize your XAF applications and thereby make them much pinker. Or of course you could choose to make them prettier, but we're no good at that, so we decided to go for pinker. Well, not only that - have a look and see for yourself. Including a cameo appearance by a DevExpress employee code named "The Marble". Watch out for him (hint: he's the one with the weird accent!)

    Click the image or this link to watch the video!

  • New video: Custom Validation with the XAF Business Application Framework

    This video is about implementing a custom validation provider for XAF, or if you believe Gary (this is still Oliver writing here), it's about GIGO - Garbage In, Garbage Out. In any case, XAF provides the means to keep users from entering more garbage than is good for them, and in many cases this means extending the validation system to support checking for conditions that depend on your business logic.

    It should be said that this video was created on the basis of XAF 8.1, and there are some changes in the validation code for 8.2, so if you're trying to copy the sample code from screen, your mileage will vary. I'll create an updated sample for 8.2 and post it later - my apologies for this inconvenience. Anyway, the information is the same and it's still fun to watch!

    Click the image or this link to watch the video!

  • New video: Data Analysis with the XtraPivotGrid and XtraCharts in XAF 8.2

    Another great new XAF feature in 8.2 is the "PivotChart" module. It combines the functionality of our XtraPivotGrid and the XtraCharts to allow the end user to create data analysis views in the format of an interactive Pivot table and/or a chart. If you haven't installed the 8.2 RC yet, be sure to have a look at this to see what's in store for you!

    Click the image or this link to watch the video!

  • New video: Ribbon support in XAF 8.2

    Here we go, a video about one of the new features in XAF 8.2: support for Ribbon style toolbars in the Windows Forms application. It was recorded with a development build, so you will see a few rough edges, but it offers a nice preview especially for those of you who haven't taken the time to download and install 8.2 RC so far. Click the image or this link to watch the video!

  • Models and XAF

    This is Oliver here, posting to Gary's blog. I thought it would be good to keep further video announcements on this blog in case somebody is following Gary exclusively - he's on vacation (well... he wasn't working for DevExpress yet when he planned it <g>), so somebody has to do the announcing.

    So here it is, a video about models and XAF. Have fun!

  • Business Logic in XAF

    XAFBusLog

    At the end of the previous video Oliver and I were off to try and sell our super dooper blogging system to the highest bidder, tune in to this video to see how that worked out for us (hint: we’re here doing another video!). Anyway, in this video we look at where business logic should be implemented in an XAF application and where that logic should be called from.

    Technorati tags: ,
    Digg This
  • Blogging with XAF

    XAFBlogging

    If you’ve been following the videos that Oliver and I have been doing you will, by now, have a good introduction to the XAF product. In this video we are going to take things to the next level by showing you the development of a simplistic, but functioning blogging engine. Having done that, Oliver and I will quietly slip away from Developer Express and try to make our fortunes selling our new product to the Silicon Valley VCs; stick around and watch the next video in the series to see how that works out for us. :-)

    Technorati tags: ,
    Digg This
  • Persistent Classes in XAF

    XAF1

    Having looked at XPO in previous videos, it’s time to have a look at XPO’s big brother, or XAF as we like to call it. XAF is built upon XPO but takes things to the next level by not only allowing you to create and persist business objects, but by having XAF interrogate the structure of those objects, it can create a UI (in both winforms and webforms) to support basic create, read, update and delete functionality. In this video we introduce you to XAF and demonstrate the creation of a persistent object and it’s supporting UI.

    Technorati tags: ,
    Digg This
  • XPO Demonstration Part 2

    XPODemo2

    If you watched the first part of this demonstration you’ll obviously be interested in how our plan, to have an army of “Garys” do our work for us, is coming along. If so, check out the latest video here. Um, I suppose there’s also a chance that you are interested in creating one to many relationships and how to change the default persistence database in XPO, if that happens to be true, you should check out the video too.

    Technorati tags: ,
    Digg This
  • XPO Demonstration Part 1

    XPODemo1

    Continuing our series of videos on XPO and XAF, Oliver and I look at how to persist a class to the database and how to read it back, and just in case that’s not exciting enough for you, we also hatch a plan to populate the world with “Gary” clones that work whilst we sip Kamikazes on a sun soaked beach. To see how that works out for us, view the latest video here.

    Technorati tags: ,
    Digg This
  • XPO the Philosophy

    ORMPhilPart1Currently Oliver and I are across in the Glendale office working with our videographer Jeff (hey how come Jeff doesn’t have a blog?!) creating some videos around XPO and XAF. In the first of the videos we’ve produced, I’m chatting to Oliver about the philosophy behind ORMs in general and XPO in particular. If you are not put off by the thought of looking at Oliver and me for 15 minutes then you can watch the video here.

    Technorati tags: ,
    Digg This
  • Application Scalability Using XPO #2

    In my previous post I introduced the topic of database scalability using XPO. In that post we looked at a simple method of partitioning customers between two database servers, one covering customers in the north and the other covering customers in the south. In that post we had the simplistic scenario of clients only being interested in customers either in the north or the south and so they were set up (via connection string) to look only at those servers.

    Whilst being sufficient to introduce the topic, there will only be a few scenarios whereby you can partition your data and your client applications in the same way. In most situations your client applications will want to access both servers to store and fetch customer information, and they will have to know which server to access based on information only available at runtime. So how is this achieved?

    Looking at the following graphic you can see that Customers have been horizontally partitioned into two databases (CustomersA-M and CustomersN-Z). You will also see that there is an Index server who’s purpose it is to serve a connection string to the required server based on the last name of the customer.

    Servers

    In the above schema the Index database has a CustomerIndex table with the following structure:-

    Structure

    Whereby the StartLetter column holds the first letter of the partitioned data and the StopLetter column holds the last letter of the partitioned data. The connection string for the partitioned server is held in the ConnectionString column. So now, when we want to know the connection string of the server for a particular customer we simply take the first letter of their last name and execute:-

    select connectionstring 
    from CustomerIndex 
    where @letter between StartLetter and StopLetter

    Using this schema, it is easy for further partition the data in the future, should that be necessary. It is just a case of provisioning another server, splitting the data evenly across all servers and then updating the Index above.

    Okay, so let’s see an example. The first thing we have to do is to abstract this away from the application developer because they don’t want to know anything about our database scalability solution.  To do that, let’s build a helper class:-

    class DBHelper {
        public static string GetConnectionStringForCustomer(string customerName){
            //GS - Guard against empty strings
            if (customerName == String.Empty) { return String.Empty; }
    
            //GS - Get the first letter of the Customer's last name
            string firstLetter = 
                customerName.Split(" ".ToCharArray())[1].Substring(0, 1);
    
            //GS - Guard against null or empty returns
            if (String.IsNullOrEmpty(firstLetter)) { return String.Empty; }
    
            /* Now that we have the first letter of the customer's last name
             * we can access the index server and find out the connection
             * string of the server to which this customer's details
             * should be saved */
            const string CONN_STRING = 
                "Data Source=.;Initial Catalog=Index;Integrated Security=True";
            string targetConnectionString;
            using (SqlConnection conn = new SqlConnection(CONN_STRING)) {
                using (SqlCommand cmd = new SqlCommand()) {
                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = "SELECT ConnectionString FROM " +
                        "CustomerIndex WHERE @Letter between " +
                        "StartLetter and StopLetter";
                    cmd.Parameters.AddWithValue("@Letter", firstLetter);
                    cmd.Connection.Open();
                    targetConnectionString = 
                        Convert.ToString(cmd.ExecuteScalar());
                    cmd.Connection.Close();
                }
            }
            return String.IsNullOrEmpty(targetConnectionString) ? 
                String.Empty : targetConnectionString;
        }
    }

    Now let’s create a function to call that abstraction:-

    private static void SetDataLayerForCustomer(string custName) {
        //GS - Get the connection string
        string conn =
            DBHelper.GetConnectionStringForCustomer(custName);
    
        //GS - Guard against empty connections string
        if (conn == String.Empty) { 
            throw new Exception("No connection string found!"); 
        }
    
        //GS - Explicitly set the datalayer
        XpoDefault.DataLayer = XpoDefault.GetDataLayer(
            conn, AutoCreateOption.DatabaseAndSchema);
    }

    Having done that, we can use XPO in the normal way:-

    static void Main(string[] args) {
        string custName = "Gary Short";
        CreateCustomerAndOrders(custName);
        ShowOrdersForCustomer(custName);
    }
    private static void CreateCustomerAndOrders(string custName) {
        //GS - Set the datalayer for the customer
        SetDataLayerForCustomer(custName);
        
        //GS - Create a Customer and an Order and persist them
        using (UnitOfWork uow = new UnitOfWork()) {
            Customer customer = new Customer(uow);
            customer.Name = custName;
    
            //GS - Create a few orders
            new Order(uow) { Customer = customer, OrderNumber = 1 };
            new Order(uow) { Customer = customer, OrderNumber = 2 };
            new Order(uow) { Customer = customer, OrderNumber = 3 };
            new Order(uow) { Customer = customer, OrderNumber = 4 };
    
            uow.CommitChanges();
        }
    }
    private static void ShowOrdersForCustomer(string custName) {
        //GS - Set the datalayer for the customer
        SetDataLayerForCustomer(custName);
    
        //GS - Fetch the customer
        using (var uow = new UnitOfWork()) {
            BinaryOperator criteria = new BinaryOperator("Name", custName);
            Customer customer = uow.FindObject<Customer>(criteria);
    
            //GS - Write out data
            Console.WriteLine("Customer: " + custName + 
                " has the following order numbers: ");
            foreach (Order order in customer.Orders) {
                Console.WriteLine("Order Number: " + order.OrderNumber);
            }
        }
    
    }

    If you run this code you will find tables created in the CustomerN-Z database; changing the customer name from “Gary Short” to “Gary Adams” will cause tables to be created in the CustomerA-M database, with no change to the client code. For demonstration purposes I have only attached an Order to the Customer, and a very simple Order at that, but you can see the principle.

    Now that I’ve shown how to work with individual customers and their related data, the question is, how do you work with groups of Customers? How do you answer questions like “show me the customers with orders placed between 2006 and 2007 where the combined value of orders placed is greater than 3M GBP or where a single order value is greater than 120K GBP”? Well, the short answer is, you don’t!

    Okay, so that’s not much help; the slightly longer answer is that if you have so many customers that you are considering a scalability solution then you would never query your OLTP system for report data. The data in the OLTP system would be archived off (perhaps instantly using triggers or in an over night batch job) into a reporting system where you would run queries using your particular reporting solution, Sql Server Reporting Services perhaps.

    So, to sum up then, in this post I’ve shown you how to horizontally partition customer data and how to abstract that away from the application programmer so that they are using XPO in a familiar manner.

    Technorati tags: ,
    Digg This
  • Application Scalability Using XPO

    Let’s say we have an application with the following (highly simplified) object model:-

    ObjectModel

    As you can see we have a Customer and a Customer’s Orders which we wish to persist to the database using XPO. This will work perfectly with 10, 100, 1,000, 10,000 orders say; however, somewhere alone the orders of magnitude your database is going to start to struggle with the numbers of Customers and their Orders. You can scale up your database server of course, but there comes a time when even that will not help. When dealing with database scalability I like to use the sharding pattern. In this pattern a large database is broken up into ever smaller pieces and clients access the required piece or shard.

    In the non scalable solution the following code would be used to serialize the object model:-

    namespace XPOScalabilityExample {
        class Program {
            static void Main(string[] args) {
    
                //GS - Explicitly set the datalayer
                string conn = MSSqlConnectionProvider.GetConnectionString(
                    "(local)", "Customers");
    
                XpoDefault.DataLayer = XpoDefault.GetDataLayer(
                    conn, AutoCreateOption.DatabaseAndSchema);
    
                //GS - Create a Customer and an Order and persist them
                using (UnitOfWork uow = new UnitOfWork())
                {
                    Customer customer = new Customer(uow);
                    customer.Name = "Gary Short";
                    customer.Address = "The Adress";
    
                    Order order = new Order(uow);
                    order.OrderedItem = "A widget";
                    order.OrderedQuantity = 4;
                    order.Price = 342.29M;
    
                    customer.Orders.Add(order);
                    order.Customer = customer;
    
                    uow.CommitChanges();
                }
            }
        }
    }

    To implement sharding, in the first instance, we will partition the database geographically, placing customers in either the north or the south. The code to persist the Customer now looks like the following:-

    namespace XPOScalabilityExample {
        class Program {
            static void Main(string[] args) {
    
                //GS - Explicitly set the datalayer
                string conn = MSSqlConnectionProvider.GetConnectionString(
                    "(local)", "North");
    
                XpoDefault.DataLayer = XpoDefault.GetDataLayer(
                    conn, AutoCreateOption.DatabaseAndSchema);
    
                //GS - Create a Customer and an Order and persist them
                using (UnitOfWork uow = new UnitOfWork())
                {
                    Customer customer = new Customer(uow);
                    customer.Name = "Gary Short";
                    customer.Address = "The Adress";
                    
                    Order order = new Order(uow);
                    order.OrderedItem = "A widget";
                    order.OrderedQuantity = 4;
                    order.Price = 342.29M;
    
                    customer.Orders.Add(order);
                    order.Customer = customer;
    
                    uow.CommitChanges();
                }
            }
        }
    }

    For demonstration purposes I have simulated the switch between servers by switching between databases on the same server, but I’m sure you get the idea. To access Customers in the south you would replace the word “North” with the word “South” in the connection string. Now that you have the idea of sharding we will look at a more advanced example next time.

    Technorati tags: , ,
    Digg This
  • TechEd 2008 - Stories from the Trenches

    I'm back in the hotel after the first day working the booth at TechEd. It was a pretty interesting day chatting to some of our customers and the attendees in general. I also managed to catch up with a familiar face in the guise of Dustin Campbell (late of Dev Express now with Microsoft) who came round to the booth to say hi along with Karen Liu and some of the IDE team. Dustin tells me he's really enjoying the challenges of his new job, and doesn't he look sweet in his smurf outfit (sorry, I mean his blue Microsoft shirt) :-)

    The evening session was even more interesting. Mark was at the booth demonstrating Coderush when Scott Hanselman turned up to heckle give Mark some advice on the content of his presentation. We got chatting and it turns out that Scott's grandparents come from the same small Scottish town where I was brought up. Small world eh? So after today I'm really looking forward to what tomorrow will bring down at the coal face; see you down there!

    Technorati tags:
More Posts Next page »
Copyright © 1998-2008 Developer Express Inc.
ALL RIGHTS RESERVED