This is our first user supplied EntitySpaces blog post. We hope to do more of these in the future as we have some customers doing some pretty cool stuff with the EntitySpaces architecture. A special thank you goes out to Robert Campbell of ecomment ltd. See Robert's bio at the end of this post. - The EntitySpaces Team.
In this article we’ll develop an application for a Windows Mobile ("WM5") platform device. While the application is just an example, and some would argue an incomplete one at that, we will be able to see all the necessary steps to get started with EntitySpaces (“ES”). We’ll also see how to use data source binding on a couple of 3rd party controls. You can also see a FLASH DEMO as well.
Prerequisites: What you need
In Visual Studio (“VS”), Clicking ‘File’ > ‘New’ should produce the New Project dialog below . I’ve selected ‘Windows Mobile 5.0 Pocket PC’, ‘Device Application’, and changed the Name to ‘Demo’.
Clicking OK at this point should result in a new project called Demo with a single blank form.
The interesting things to notice here are the default references VS implements because we’ve targeted the WM5 platform.
Creating a database file for use in our application is relatively simple if MS SQL Server has been installed properly.
In VS, Click the project icon in the solution tree (shown above as ‘Demo’), Right-Click, and Click ’Add’ > ’New Item...’. Select ‘Database File’ in the Add New Item dialog.
I changed the Name of the database file to Demo.sdf.
Click ‘Add’, and the standard VS Data Source Configuration Wizard pops up.
We’ll not need this as we’re going to use ES!!
Click ‘Cancel’ instead.
Double-Click the database file that appeared in the Project Solution Explorer tree.
The Server Explorer pane should now appear in your VS IDE.
If the connection to the database is closed (indicated by the little red ’x’ next to the Demo.sdf database icon), Right–Click and Clicking ’Refresh’ should restore it.
We can now build the database proper.
Right-Click on the Tables icon. Click ‘Create Table’. This will present a familiar New Table Dialog.
In this screenshot I’ve created a table called Survey and added the 5 columns.
Note the Id column is a Unique Identifier, is set as the Primary Key, is a ‘RowGuid’, and it’s practical to set the default value for the column as (newid()).
Create a second table which will be used as a related table. In this demo we’re going to use a table called HTypes (house types) for our survey.
We need to relate the two tables with some foreign key constraints. In SQL server ‘proper’ this is easily done with the relationship diagram tool in VS. However, with SQL CE or ‘Compact Edition’ as it’s now called, that’s not possible. We’ll have to do this programmatically with a TransactSQL Query.
SQL Reference (SQL Server Compact Edition)
http://msdn2.microsoft.com/en-us/library/ms173372.aspx
I usually have to resort to the Microsoft SQL Server Management Studio to run this query as the VS component doesn’t support the ‘ALTER’ command (Anyone have any idea why? Or, indeed, why the Management Studio doesn’t support the ability to ‘Open’ tables?)
To add a constraint to the Survey table, we need to run this query:
ALTER TABLE [Survey] ADD CONSTRAINT [FK_Survey_HTypes] FOREIGN KEY([HouseType]) REFERENCES [HTypes] ([Id])
Note, in this simplified demo, I have just taken the defaults for the behavior for this constraint. A lot of this will depend on your real application. I’m no DB expert, and this is an ES demo.
At this point we have:
Let’s add two folders to our VS project for the files generated in the next section. Click the Project icon in the solution tree. Right-Click and Click ’Add New Folder’. Add a folder called ‘Generated’ and another called ‘Custom’.
Note the highlighted Project Folder property. You’ll need this information in the next section. The next step is to produce the ES generated classes.
Start MyGeneration, and select ‘Edit’ > ‘Default Settings...’.
Click ‘Microsoft SQL CE’ from the ‘Driver’ dropdown. The easiest place to get the Connection String is by copying it from the properties dialogue of the Demo.sdf file in VS, and pasting it in the text box here. Click ‘SqlServerCe’ in the ‘DbTarget’ dropdown. Optionally, click your preferred language in the ‘Language’ dropdown, in this case, ‘C#’.
Click ‘Test Connection’ to confirm a valid connection can be established.
Click the Templates Tab of the dialogue.
All we really need to change here is the Default Output Path.
Copy the Project Directory path that was noted in the previous screenshot, and paste it to the Default Output Path here. The template will automatically find the Generated and Custom directories.
Click OK. This should start the connection to your database.
You should now be able to select and expand the MyMeta tree to show the database we have created in all its glory.
Select the Template browser from the icons at the top of the left hand side of the MyGeneration IDE. Expand the EntitySpaces folder and the C# folder below that.
We’re going to run two templates:
We’ll Double-Click a template to load it, and click the green ‘go’ arrow to generate. Let’s start with the Custom Classes.
Note the template has automatically selected the \Custom\ folder.
The Namespace defaults to ‘BusinessObjects’. Change this to ‘Demo’.
Ctrl-Click both tables to select them in the Select Tables window. Click the OK button/bar.
If everything goes according to plan, the output will be shown in the output tab / window.
Double-Click on the Generated Classes Master template and Click the green ‘go’ button again.
This dialogue has two tabbed sections.
For the Basic Information tab, make the same edits as for the Custom Classes template. Again, the Output Path should be automatically filled in for you.
Click the Advanced Options tab.
On this tab we need to check the options as shown:
Important: The Ignore Schema, Ignore Catalog, and Target Compact Framework boxes need to be checked.
Click OK.
Again, if it’s worked, the output should show in the pane on the right.
We’re done with MyGeneration, either close or minimize the application.
Go back to VS, and Click the ‘Refresh’ icon. Click the ‘Show All Files’ icon.
We now can see our generated files in both directories but grey’d out. As yet, they’re not part of the project.
Highlight them all using the usual Shift-Click and/or Ctrl-Click techniques. Right-click one of the highlighted files, and Click ‘Include In Project’.
Hey! Just before you go clicking ‘Build’, we need to do one more thing. Add the ES references to the project.
To do so, Click the References node in the solution tree, Right-Click and Click ‘Add Reference...’.
At this point, the current documentation suggests that the ES DLLs will be there ready to select. In my case, they were not, so Click the Browse tab and drill down to the CE directory in the place where EntitySpaces is installed. The default, and in my case, is:
C:\Program Files\EntitySpaces\Redistributables\CE
The Add Reference dialogue should look like this:
We need to Ctrl-Click Core.Ce, Interfaces.Ce, LoaderMT.Ce, and SqlClientProvider.Ce. Then, Click OK.
Your Solution tree should now look like this:
Quick! Click ‘Build’ before you collapse in anticipation!
‘Build Succeeded’
“Ah”, you cry, “It looks great, but doesn’t do anything!”
Wait… there’s more....
The application needs to have added the medium trust loader, and, because we need a configless implementation in WindowsCE, Program.cs needs to look something like this.
using System; using System.Collections.Generic; using System.Windows.Forms; using EntitySpaces.Interfaces; using EntitySpaces.LoaderMT; namespace Demo { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [MTAThread] static void Main() { // --- Manually register a connection (DO THIS ONE TIME ONLY) --- esConnectionElement conn = new esConnectionElement(); conn.ConnectionString = @"Data Source=\program files\Demo\Demo.sdf;"; conn.Name = "Demo"; conn.Provider = "EntitySpaces.SqlClientProvider.Ce"; conn.ProviderClass = "DataProvider"; conn.SqlAccessType = esSqlAccessType.DynamicSQL; conn.ProviderMetadataKey = "esDefault"; conn.DatabaseVersion = "2005"; // --- Assign the Default Connection --- esConfigSettings.ConnectionInfo.Connections.Add(conn); esConfigSettings.ConnectionInfo.Default = "Demo"; // --- Register the Loader --- esProviderFactory.Factory = new EntitySpaces.LoaderMT.esDataProviderFactory(); Application.Run(new Form1()); } } }
Our ‘spec’ calls for a form that displays a grid with all the survey results, so we should add a grid. We’re going to use a 3rd party specialist grid, the Resco AdvancedList, and a panel to display the record with all its detail. Here, I’ve used the Resco DetailView.
Of the many things to do to the form, the first is to add the two ES collections by dragging the icons from the toolbox to the form. Using this method, all the necessary defaults are created and end up in the designer.cs file.
I’ve also done a similar thing with the two Resco controls; the advanced list and the detail view.
I've created a method to initialize the form:
private void init() { // The Grid is populated with the collection of surveys // just get everything; if (surveys.LoadAll()) { this.advancedList1.DataSource = surveys; this.advancedList1.Refresh(); } else { MessageBox.Show("can't load survey data"); } }
The record detail is displayed in a control called DetailView, We need to populate the combobox on the related field, load the collection, and bind it to the ComboBox
private void init() { // The Grid is populated with the collection of surveys // just get everything; if (surveys.LoadAll()) { this.advancedList1.DataSource = surveys; this.advancedList1.Refresh(); } else { MessageBox.Show("can't load survey data"); } if (types.LoadAll()) { //This sets the DataSource of the ComboBox to the collection ((Resco.Controls.DetailView.ItemComboBox)this.detailView1.Items["HType"]).DataSource = types; ((Resco.Controls.DetailView.ItemComboBox)this.detailView1.Items["HType"]).DisplayMember = HTypesMetadata.PropertyNames.Htype; ((Resco.Controls.DetailView.ItemComboBox)this.detailView1.Items["HType"]).ValueMember = HTypesMetadata.PropertyNames.Id; this.detailView1.Refresh(); } }
By the time we exit this method, we've done everything to populate the grid, and related fields, in the record detail display... in 12 lines of code... and NO SQL or recordset/dataset stuff to remember.
Using the AdvancedList ("AL") to allow a user to select a record to display, we have a method showDetails. This method uses the Id of the record as its parameter. This is usually called by a row select event handler on the AL.
private void showDetails(Guid rowToEdit) { s = new Survey(); s.LoadByPrimaryKey(rowToEdit); this.detailView1.DataSource = null; this.detailView1.DataSource = s; this.detailView1.Refresh(); }
The method simply creates a new Survey entity, and loads it with the record data using the ES LoadByPrimaryKey() method. This binds the resulting object to the DetailView control through the assignment of the DataSource.
The ES entity ‘s’ is visible across the form.
If the user selects another row in the grid, we need to detect if they have made any changes to the current record, and save it.
private void askSave() { if (s.es.IsDirty) { DialogResult result = MessageBox.Show("Save changes?", "ES Test App", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result == DialogResult.Yes) { s.Save(); this.surveys.LoadAll(); this.advancedList1.DataSource = this.surveys; this.advancedList1.Refresh(); } } }
The 1st thing the askSave method does is to determine if the data has been altered using the property of the underlying entity. if (s.es.IsDirty)
Having determined it is, and that the user indeed wants to save it, it’s a simple matter to call the Save() method on the entity itself. After which, a call to reload the surveys collection and a refresh of the AdvancedList grid is all that’s required.
Adding a new record is easy, too. I have an event attached to an AddNew menu item that looks like this.
private void AddNew_Click(object sender, EventArgs e) { Survey s = new Survey(); s.AddNew(); showDetails((Guid)s.Id); }
The ES AddNew() method is called to create a new entity and the Guid is passed to showDetails() to display it for editing.
Note: The AddNew() method is overloaded in the Custom Class to generate the Guid immediately rather than relying on the default mechanism in the database.
public partial class Survey : esSurvey { public override void AddNew() { base.AddNew(); this.Id = Guid.NewGuid(); } }
And... there you have it; a VS WM5 solution, an SQL Ce database, some easily generated ES BusinessObjects, a couple of Resco controls, and a few lines of code, all blended into a working application. After you've done it once, you can probably do your next project in less time than it took you to read this. Enjoy!
Robert Campbell was educated in Newcastle Upon Tyne University in Agricultural Engineering, worked in manufacturing for 15 years before moving into IT in the mid 80's, and worked for a variety of consulting organizations before setting up ecommnet in 1998 at the start of the dot.com boom to specialize in security and web based applications development. ecommnet is now specializing in mobile applications development and security, Web 2.0 and identity management. ecommnet are NOKIA Enterprise Solutions Certified Mobility Partners.
ecommnet ltd., Bewick House, Horsley Business Centre, Horsley, Newcastle Upon Tyne, NE15 0NYDDI: +44 8451-740-633, T:+44 1661 854 492 F :+44 1661 854 632 M:+44 7801 270 264
www.ecommnet.co.uk www.offexploring.com www.autobestbuy.co.uk
Page rendered at Tuesday, February 09, 2010 7:55:42 AM (Eastern Standard Time, UTC-05:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.