Anda di halaman 1dari 18

In my previous post, I listed all Visual Studio templates that speed-up XAF application development.

The goal of this post, however, is to demonstrate how to create a desktop XAF application without using any XAF templates and designers. We will look under the XAF application hood, so to speak, to see that it is a regular .NET WinForms application. Although, I can't suggest that you use this approach in your daily development (because templates speed up your progress greatly), I do believe that it is good practice to try building an application from scratch at least once to better understand XAF architecture. I will also demonstrate how to add extra modules, controllers and security in code - you may find that it is much quicker to type several lines than wait for the designer to load and toolbox to populate. Of course, I don't want to underestimate the value of XAF design-time tools. They are great for beginners, but when you become an XAF expert, you may find that it is handier to make many of the "designable" tasks in code.

Create an XAF Application Project


Let us begin with the Empty Project template supplied with Visual Studio. The Windows Forms Application template is not suitable for our task because it contains the Form1 class and unneeded references. So, start the Visual Studio and execute FILE | New... | Project command. In the Templates | Visual C# | Windows category, choose the Empty Project template, and specify project and solution names (e.g.,MyXafApplication and MyXafAppplcationSolution and click OK).

Open the newly added project properties - right-click the project in the Solution Explorer and choose Properties. The project's Output typeis Console Application by default, change it to Windows Application.

For now, the project's References list is empty. In the beginning, we will require the following assemblies. 1. 2. 3. 4. 5. DevExpress.ExpressApp.v12.1.dll - contains the base XAF functionality (the XafApplication class in particular). DevExpress.ExpressApp.Win.v12.1.dll - contains the WinForms XAF functionality (the WinApplication class in particular). DevExpress.ExpressApp.Xpo.v12.1.dll - provides XPO support; we will use XPO to create and access the application database. System.dll - we are making a .NET application, aren't we? System.Data.dll - our application is data-aware (XPO uses this assembly).

Now all initial preparations are done and we can start coding (XAF team recommends that you use CodeRush to speed-up). Add theProgram.cs file with the following code.
using System; namespace MyXafAppplication { static class Program { [STAThread] static void Main() { } } }

The application can be launched already, but it does nothing. We need an instance of the WinForms XAF application to be started from ourMain method. So declare the following MyXafApplication class.
using DevExpress.ExpressApp; using DevExpress.ExpressApp.Win;

using DevExpress.ExpressApp.Xpo; //... public class MyXafApplication : WinApplication { protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) { args.ObjectSpaceProvider = new XPObjectSpaceProvider(ConnectionString, Connection); } protected override void OnDatabaseVersionMismatch(DatabaseVersionMismatchEventArgs args) { args.Updater.Update(); args.Handled = true; } }

In the overridden CreateDefaultObjectSpaceProvider method, we state that we will use XPO as an ORM tool - all Object Spaces in our application will be of the XPObjectSpace type. In the overridden OnDatabaseVersionMismatch, we instruct XAF to always update the application database when the version mismatch occurs. Now, we can instantiate MyXafApplication, configure it, and run it. The minimal required configurations are the ApplicationName and theConnectionString (we connect to the local instance of the Microsoft SQL Server here).
static void Main() { MyXafApplication myXafApplication = new MyXafApplication(); myXafApplication.ApplicationName = "MyXafApplication"; myXafApplication.ConnectionString = "Integrated Security=SSPI;Pooling=false;Data Source=(local);Initial Catalog=MyXafApplication"; myXafApplication.Setup(); myXafApplication.Start(); }

Although our solution contains a single application project and no module projects at all, the empty main window is shown and certain basic functionality is available (e.g., Model Editor) when the application is launched.

Note: The default XAF application project created from the template has the WinApplication descendant implemented in a separate file, so the Application Designer can be used. It the designer, the ApplicationName and ConnectionString properties can be initialized in the Properties window.

Add the Module Project With Business Model


Let us add some functionality to our application. Add another Empty Project and call it MyXafModule. The XAF Module is simply a class library that contains the ModuleBase descendant class. Reference the DevExpress.ExpressApp.v12.1.dll assembly and add the followingMyModule class.
using DevExpress.ExpressApp; namespace MyXafModule { public class MyModule : ModuleBase { } }

Then change the project's output type to Class Library and specify the assembly version as it is shown in the image below.

With the asterisk sign, we instruct Visual Studio to increment the build and revision numbers automatically. This is required for correct database updating by XAF. Now we can reference the MyXafModule project in MyXafApplication and add our new module to the application's Modules collection.
using MyXafModule; // ... static void Main() { MyXafApplication myXafApplication = new MyXafApplication(); myXafApplication.ApplicationName = "MyXafApplication"; myXafApplication.ConnectionString = "Integrated Security=SSPI;Pooling=false;Data Source=(local);Initial Catalog=MyXafApplication"; myXafApplication.Modules.Add(new MyModule()); myXafApplication.Setup(); myXafApplication.Start(); }

If we run the application, invoke the Model Editor and click the Loaded Modules button, we will see that our module is on the list.

Note that the SystemModule and SystemWindowsFormsModule are on the list as well - they were added in our applications ancestor classes (XafApplication and WinApplication respectively). Now we can define a business model within the module in a regular fashion - by adding business classes to the module project. For instance, we can add the following Contact class.
using DevExpress.Xpo; using DevExpress.Persistent.Base; using DevExpress.Persistent.BaseImpl; namespace MyXafModule { [DefaultClassOptions, ImageName("BO_Contact")] public class Contact : BaseObject { public Contact(Session session) : base(session) { } private string name; public string Name { get { return name; } set { SetPropertyValue("Name", ref name, value); } } private string email;

public string Email { get { return email; } set { SetPropertyValue("Email", ref email, value); } } } }

To follow our "from scratch" concept, you can add this class without using a special XAF template - simply choose Add | New Class.... As the ancestor BaseObject class resides within the DevExpress.Persistent.BaseImpl.v12.1.dll assembly, a reference to this assembly is required to compile the code above. Since we use XPO, the DevExpress.Xpo.v12.1.dll and DevExpress.Data.v12.1.dll references are required as well.

Supply Initial Data


To add several Contact records to the database, let's implement a Module Updater class.
using System; using System.Collections.Generic; using DevExpress.Data.Filtering; using DevExpress.ExpressApp; using DevExpress.ExpressApp.Updating; //... public class MyModuleUpdater : ModuleUpdater { public MyModuleUpdater(IObjectSpace objectSpace, Version currentDBVersion) : base(objectSpace, currentDBVersion) { } public override void UpdateDatabaseAfterUpdateSchema() { base.UpdateDatabaseAfterUpdateSchema(); Contact contactJane = ObjectSpace.FindObject<Contact>( new BinaryOperator("Name", "Jane Smith")); if (contactJane == null) { contactJane = ObjectSpace.CreateObject<Contact>(); contactJane.Name = "Jane Smith"; contactJane.Email = "jane.smith@example.com"; } Contact contactJohn = ObjectSpace.FindObject<Contact>( new BinaryOperator("Name", "John Smith")); if (contactJohn == null) { contactJohn = ObjectSpace.CreateObject<Contact>(); contactJohn.Name = "John Smith"; contactJohn.Email = "john.smith@example.com"; } } }

The image below illustrates the result.

In the current version of XAF (12.1), the ModuleUpdater descendants are automatically collected via the reflection. In the upcoming 12.2 release, we have added an option to explicitly register Module Updater classes that should be used by the module in the overriddenGetModuleUpdaters method. It is recommended to use this method to improve performance. So, if you are reading this article when 12.2 is already published and you have it installed, modify the MyModule class as follows.
public class MyModule : ModuleBase { public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB) { return new ModuleUpdater[] { new DatabaseUpdate.Updater(objectSpace, versionFromDB) }; } }

Use Extra Modules


You can plug extra modules supplied with XAF in the same manner as our custom MyModule. For instance, to enable reporting, reference theDevExpress.ExpressApp.Reports.Win.v12.1.dll assembly and add an instance of ReportsWindowsFormsModule class to the application'sModules collection.

using DevExpress.ExpressApp.Reports.Win; //... static void Main() { MyXafApplication myXafApplication = new MyXafApplication(); myXafApplication.ApplicationName = "MyXafApplication"; myXafApplication.ConnectionString = "Integrated Security=SSPI;Pooling=false;Data Source=(local);Initial Catalog=MyXafApplication"; myXafApplication.Modules.Add(new MyModule()); myXafApplication.Modules.Add(new ReportsWindowsFormsModule()); myXafApplication.Setup(); myXafApplication.Start(); }

With a single line of code, we can design and print reports!

Note: In the default XAF application project, the Application Designer is used to populate the modules collection. It is convenient for novices to browse the toolbox and pick the required modules, but experienced XAFers can do the task in code more quickly.

Secure Everything
To enable the security system, reference the DevExpress.ExpressApp.Security.v12.1.dll assembly, instantiate the Security Strategy and Authentication, and initialize the application's Security property. Additionally,

references to the DevExpress.Xpo.v12.1.dll andDevExpress.Persistent.Base.v12.1.dll assemblies that are used by the Security System are required.
using DevExpress.ExpressApp.Security; using DevExpress.ExpressApp.Security.Strategy; //... static void Main() { MyXafApplication myXafApplication = new MyXafApplication(); myXafApplication.ApplicationName = "MyXafApplication"; myXafApplication.ConnectionString = "Integrated Security=SSPI;Pooling=false;Data Source=(local);Initial Catalog=MyXafApplication"; AuthenticationActiveDirectory authentication = new AuthenticationActiveDirectory() { CreateUserAutomatically = true}; myXafApplication.Security = new SecurityStrategyComplex(typeof(SecuritySystemUser), typeof(SecuritySystemRole), authentication); myXafApplication.Modules.Add(new MyModule()); myXafApplication.Modules.Add(new ReportsWindowsFormsModule()); myXafApplication.Setup(); myXafApplication.Start(); }

Two lines of code are added, and the application is secure! No need to wait for the designer to load and search for the security components in the toolbox. This is a very quick approach, when you know what code to type, of course.

Add a Controller with Action


Adding a Controller and Action can be easily done in code, without the use of a template and designer. Let's add the following class to our module project.
using System; using System.Diagnostics; using DevExpress.ExpressApp; using DevExpress.ExpressApp.Actions; using DevExpress.Persistent.Base; //... public class SendMessageController : ObjectViewController<ListView, Contact> { public SendMessageController() { SimpleAction sendMessageAction = new SimpleAction(this, "SendMessage", PredefinedCategory.View); sendMessageAction.ImageName = "BO_Contact"; sendMessageAction.SelectionDependencyType = SelectionDependencyType.RequireSingleObject; sendMessageAction.Execute += delegate(object sender, SimpleActionExecuteEventArgs e) { string startInfo = String.Format(

"mailto:{0}?body=Hello, {1}!%0A%0A", ViewCurrentObject.Email, ViewCurrentObject.Name); Process.Start(startInfo); }; } }

This Controller invokes the email client to compose a message addressed to the chosen contact. Don't forget to declare the Controller aspublic. Also, note that we use the generic ObjectViewController class here. It is convenient to pass a target view and object types as generic parameters - no need to initialize the corresponding controller's properties. The Controller's ViewCurrentObject property simplifies access to the current object. Since the target object type (Contact) is known, there is no need to cast the current object value to the Contact type. Below is the implemented SendMessage Action.

Enable the Design-Time Model Editor


A big drawback of our hand-made solution is that the Model Editor, a great tool for the Application Model browsing and customizing, is unavailable. Fortunately, we can fix this easily. Just add the Model.DesignedDiffs.xafml file to the MyXafModule project.

The next required step is to open this file in an XML editor and add the following code (the Model Editor won't start on the empty file).
<?xml version="1.0" ?> <Application/>

Now, the Model Editor can be used to change the model differences for the MyXafModule module.

By default, an XAF module uses the ResourcesModelStore model differences storage. As this class name implies, it reads application model customizations from the module assemblys resource files (*.xafml and *.bo). If the model differences file isnt built as an embedded resource, it will be ignored. So, to apply changes at runtime, change the XAFML file's Build Action to Embedded Resource.

Analogously, the Model Editor can be enabled for the MyXafApplication project. The required file name for the application project isModel.xafml. The file's Build Action property should be set to Content, and Copy to Output Directory - to Copy Always. In WinForms XAF applications, the FileModelStore model differences storage is used, and the application-level customizations are loaded from the Model.xafmlfile located in the applications working folder.

As a result, we have a fully functional WinForms XAF solution. In this article, I have omitted web application implementation intentionally. It is not really easy to create an ASP.NET XAF application from scratch due the volume of code required to create XAF web pages (Default.aspx,Login.aspx, etc.). Meantime, in web, you can use lightweight modules and controllers demonstrated here. P.S.: You can download the complete source code demonstrated here from the Code Central.

Anda mungkin juga menyukai