Define the Logical Data Model and Relationships

In this lesson, you will personalize the application by defining the business model and the business logic in the shared module. Note that the code you write will be abstracted from the database management systems (DBMS) and the data store technologies (with the help of ORM tools).

Since eXpressApp Framework (XAF) supports both the Entity Framework (EF) and eXpress Persistent Objects (XPO) data models, both of these ORMs are demonstrated in this tutorial. The marketing activity (Customer and Testimonial) will be described using EF classes. eXpress Persistent Objects classes will be used for the planning portion of the business model (Projects and Tasks). With both ORMs, you will use the Code First data modeling approach.

Microsoft ADO.NET Entity Framework

Follow the steps below to add Customer and Testimonial entities.

  1. In the Solution Explorer, right-click the SimpleProjectManager.Module\BusinessObjects folder and select Add | Class… from the context menu. Set the name to "Marketing" and replace the auto-created code with the following code.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations.Schema;
    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    
    namespace SimpleProjectManager.Module.BusinessObjects.Marketing {
        [NavigationItem("Marketing")]
        public class Customer {
            public Customer() {
                Testimonials = new List<Testimonial>();
            }
            [Browsable(false)]
            public int Id { get; protected set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string Email { get; set; }
            public string Company { get; set; }
            public string Occupation { get; set; }
            [Aggregated]
            public virtual IList<Testimonial> Testimonials { get; set; }
            [NotMapped]
            public string FullName {
                get {
                    string namePart = string.Format("{0} {1}", FirstName, LastName);
                    return Company != null ? string.Format("{0} ({1})", namePart, Company) : namePart;
                }
            }
            [ImageEditor(ListViewImageEditorCustomHeight = 75, DetailViewImageEditorFixedHeight = 150)]
            public byte[] Photo { get; set; }
        }
    
        [NavigationItem("Marketing")]
        public class Testimonial {
            public Testimonial() {
                CreatedOn = DateTime.Now;
            }
            [Browsable(false)]
            public int Id { get; protected set; }
            [FieldSize(FieldSizeAttribute.Unlimited)]
            public string Quote { get; set; }
            [FieldSize(512)]
            public string Highlight { get; set; }
            [VisibleInListView(false)]
            public DateTime CreatedOn { get; internal set; }
            public string Tags { get; set; }
            public virtual Customer Customer { get; set; }
        }
    }
    
  2. Edit the SimpleProjectManagerDbContext.cs (SimpleProjectManagerDbContext.vb) file that was added to your solution by the wizard. Add the Customer and Testimonial properties to the SimpleProjectManagerDbContext class to register Customer and Testimonial entities within the DbContext.

    using SimpleProjectManager.Module.BusinessObjects.Marketing;
    
    namespace  SimpleProjectManager.Module.BusinessObjects {
        public class SimpleProjectManagerDbContext : DbContext {
            //... 
            public DbSet<Customer> Customer { get; set; }
            public DbSet<Testimonial> Testimonial { get; set; }
        }
    }
    
  3. Open the Module.cs (Module.vb) file (press the F7 key while Module.cs (Module.vb) is highlighted in Solution Explorer) in the code editor. Specify the database initializer for EF by uncommenting the following code (the database will be recreated each time you make changes in your entities):

    public sealed partial class SimpleProjectManagerModule : ModuleBase {
        // Uncomment this code to delete and recreate the database each time the data model has changed.
        // Do not use this code in a production environment to avoid data loss.
        #if DEBUG
        static SimpleProjectManagerModule() {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SimpleProjectManagerDbContext>());
        }
        #endif
    
        // ...
    }
    

DevExpress eXpress Persistent Objects

Follow the steps below to add Project and ProjectTask XPO persistent classes.

  1. In the Solution Explorer, right-click the SimpleProjectManager.Module\BusinessObjects folder and select Add | Class… from the context menu. Type the new name ('Planning') and click Add.
  2. Replace the auto-generated file content with the following code.

    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl;
    using DevExpress.Xpo;
    using System;
    
    namespace SimpleProjectManager.Module.BusinessObjects.Planning {
        [NavigationItem("Planning")]
        public class ProjectTask : BaseObject {
            public ProjectTask(Session session) : base(session) { }
            [Size(255)]
            public string Subject { get; set; }
            public ProjectTaskStatus Status { get; set; }
            public Person AssignedTo { get; set; }
            public DateTime StartDate { get; set; }
            public DateTime EndDate { get; set; }
            [Size(SizeAttribute.Unlimited)]
            public string Notes { get; set; }
            [Association]
            public Project Project { get; set; }
        }
    
        [NavigationItem("Planning")]
        public class Project : BaseObject {
            public Project(Session session) : base(session) { }
            public string Name { get; set; }
            public Person Manager { get; set; }
            [Size(SizeAttribute.Unlimited)]
            public string Description { get; set; }
            [Association, Aggregated]
            public XPCollection<ProjectTask> Tasks {
                get { return GetCollection<ProjectTask>("Tasks"); }
            }
        }
        public enum ProjectTaskStatus {
            NotStarted = 0,
            InProgress = 1,
            Completed = 2,
            Deferred = 3
        }
    }
    

Take special note that classes forming the business model contain not only data properties that define the structure of our data, but also some simple business logic (for instance, the FullName property of the Customer class). Surely, you can extend these business classes with more logic methods as your needs dictate. You can use ActionAttribute to create an action directly from a business class method. In addition to logic, data model classes can contain annotations with various code attributes that may affect the database structure, UI look and feel, and behavior. Learn more on these and other recommended practices to customize database and application UI metadata in the Customize the Application UI Metadata section of the next lesson.

Important

In this simple demo, business object properties are auto-implemented (declared with empty get and set accessors). However, in most cases, it can be required to trigger the PropertyChanged event from set accessors (see PropertyChanged Event in Business Classes).

Populate the Database with Initial Data

You can add business objects to your XAF application database on the first run. Predefined data will make it easier for an end-user to understand your application and become familiar with it. Open the Updater.cs (Updater.vb) file, located in the SimpleProjectManager.Module project's Database Update folder. Override the ModuleUpdater's ModuleUpdater.UpdateDatabaseAfterUpdateSchema method, as shown below.

using SimpleProjectManager.Module.BusinessObjects.Marketing;
using SimpleProjectManager.Module.BusinessObjects.Planning;
// ...
public class Updater : ModuleUpdater {
    //...
    public override void UpdateDatabaseAfterUpdateSchema() {
        base.UpdateDatabaseAfterUpdateSchema();
        if(ObjectSpace.CanInstantiate(typeof(Project))) {
            Project project = ObjectSpace.FindObject<Project>(
                new BinaryOperator("Name", "DevExpress XAF Features Overview"));
            if (project == null) {
                project = ObjectSpace.CreateObject<Project>();
                project.Name = "DevExpress XAF Features Overview";
            }
            ObjectSpace.CommitChanges();
        }
        if(ObjectSpace.CanInstantiate(typeof(Customer))) {
            Customer customer = ObjectSpace.FindObject<Customer>(
                CriteriaOperator.Parse("FirstName == 'Ann' && LastName == 'Devon'"));
            if (customer == null) {
                customer = ObjectSpace.CreateObject<Customer>();
                customer.FirstName = "Ann";
                customer.LastName = "Devon";
                customer.Company = "Eastern Connection";
            }
            ObjectSpace.CommitChanges();
        }
    }
    //...
}

The ObjectSpace object used here to create initial data is one of the main framework abstractions allowing you to perform all data manipulations. It will be detailed further in the Define Custom Logic and UI Elements section of the next lesson (Customize the Application UI and Behavior). For now, just copy the code above and see how it works.

Note

You can refer to the Supply Initial Data (XPO) tutorial for details about initially populating the database.

Automatic DB and UI Scaffolding Results

Now you can run both the WinForms and ASP.NET applications, and see what they look like. By default, the WinForms project is set as the startup project. To run the ASP.NET Web application, right-click the SimpleProjectManager.Web project in Solution Explorer, and select the Set as StartUp Project item from the context menu. Then, press Start Debugging or the F5 key. As a result, the application will be opened, as shown in the images below.

WinForms

SPM_Win

ASP.NET

SPM_Web

The database and corresponding tables are automatically created based on your data models. Below is the Object Explorer window from the SQL Server Management Studio.

SPM_DB

As you can see in the application screenshots, items corresponding to database tables are added to the navigation control. These navigation items allow a user to navigate to a list of records, and then open details from the list. Create-read-update-delete (CRUD) and other capabilities (search, filter, print, etc.) are automatically available for list and detail data forms. In the detail form, various editors (text box, memo, drop-down box, image and date picker, etc.) are used for appropriate types of business class properties. Refer to the Data Types Supported by built-in Editors section to learn more about built-in editors for various data types.

Take a look at the auto-created lookup and collection editors that are difficult to implement in a regular non-XAF application. For instance, the Project and ProjectTask classes participate in a One-To-Many relationship (see the Tasks collection property). XAF recognizes this relationship and automatically provides the UI for adding, editing, removing and even exporting tasks. Next, take a look at the Manager property, which displays a drop-down list of persons in the UI. XAF not only provides a corresponding lookup editor for this property, but also creates a foreign key column pointing to the Person table in the database.

Note that persistence settings (such as field size set via attributes) were also taken into account, and columns of the specified size were created in the database.

Note

If you experience any problems while following these steps, or you just want to let us know what you think, contact our support team (full and free technical support during the evaluation period is included).

Next topic: Customize the Application UI and Behavior

See Also