I’ve been particularly fond of using Visual Studio with a dark color scheme and the Consolas font and I wanted to use the same settings in Eclipse. It took a bit of Googling and hunting as the settings for fonts and colors are a bit more scattered in Eclipse than they are in Visual Studio.
To change the default editor font in Eclipse, select “Preferences” from the “Window” menu. From the tree on the left side of the Preferences dialog, select General->Appearance->Colors and Fonts. In the right pane tree select Basic->Text Font then click “Edit…” Select your desired font (I use Consolas 9pt.) After you’ve selected the font, several other font settings in the Preferences dialog will default to using the text font. If you expand the Java node you will see that “Java Editor Text Font” and “Properties File Editor Text Font” have both changed to match the basic text tont while “Javadoc display font” has not. You can override each font setting and have each one use a different font if you really want. Several bloggers have mentioned their preference for using the Dina font for the Console window in Visual Studio 2010; you can use it in Eclipse too for the device & emulator log (called ‘”LogCat.”) You can download the Dina font from the author’s website. To change the device & emulator log font, select Android->LogCat in the Preferences dialog, click Change and select the Dina font you downloaded.
Most of the color preferences for Java coding are under Java->Editor->Syntax Coloring and General->Editors->Text Editors but the rest are spread throughout the Preferences dialog. The easiest way I found to work with them is to start editing something (Java code, or an XML file,) then right-click the editor and select “Preferences” from the context menu. What will appear is a Preferences dialog that has been filtered to only show the entries that are relevant to the current editor. For a quick start using pre-made color themes, see this post and this post.
One other set of changes you may want to make is to change brace handling to be more like C#. The convention in Java is to place opening braces on the same line as the structure they open:
public static MainClass {public static void main(String[] args) {...}}rather than the C# style:
public static MainClass{public static void main(string[] args){...}}That said, I’m a believer in using the conventions of the language rather than trying to force one language to act like another.
These settings are located in the Preferences dialog under Java->Code Style->Formatter. Within this settings page you can create multiple sets of formatting options (each with their own settings) and even have different formatting sets for different projects. In fact, the level and degree of code formatting options for Java far surpasses Visual Studio for C#.
I encourage you to review the other Java preference settings. There are code style analysis settings under Java->Compiler and several CodeRush-like settings under Java->Editor. The ? icon in the lower left corner of the Preferences dialog will open context-sensitive help about the current panel (most dialogs in Eclipse have this.)
You can download my tweaks and color settings in the zip file at the bottom of this post. It includes a readme file that details how to install it into Eclipse. It’s not completely necessary but without it your work with Eclipse won’t look like the screenshots. Up to you. Now let’s move on to some code.
When we last left the coding we had a Java project called JRERestletServiceRefGenerator with a package called com.droidbaseball.svcgenerator and a class with just template code called MainClass.java:
At the top is the package declaration. Again, think of this like a .NET namespace. In fact if you hover the mouse over the main method the full name of the method is com.droidbaseball.svcgenerator.MainClass.main.
The main method is decorated with a Javadoc block (signaled by the /** comment) and includes the @param Javadoc tag. This is similar to the XML comment construct in C# signaled by the /// comment. For details about Javadoc see here. This automatic decoration of the generated code can be controlled in the Preferences dialog under Java->Code Style->Code Templates->Comments. The “TODO” comment in the method body is a task tag and performs a similar function as the task list in Visual Studio. You can control the generation of these generated comments in Preferences->Java->Code Style->Code Templates->Code and you can add additional task tags under Preferences->Java->Compiler->Task Tags.
The little marks to the left and right of the TODO line are annotations. The left margin is the vertical ruler and the right margin is the overview ruler. When there is an error, for example, that prevents the code from compiling it will show up in the overview ruler as a red mark; hovering over it will show a summary of the error and clicking it will jump to that line in the source file. There are several annotations; they can be controlled in Preferences->General->Editors->Text Editors->Annotations.
We’re going to add some code to the main method to call the OData service generator. Since this is pretty much a throw-away app we’ll hard code it. Remove the TODO line from the main method and add one line to the main method:
Generator.main(new String[] {
"http://173.46.159.199/baseballstats/BaseballStats.svc",
"C:\\projects\\BaseballService"
});Save the file. It will look like this:
There are a couple of things to notice about this code. First, the Generator.main static method accepts one parameter: an array of String (arrays in Java are zero based just as in C#.) The syntax for creating an array “on-the-fly” is identical to C#. The first array element is the URL of the service and the second array element is the destination directory for the generated code (this example assumes you have the path “C:\projects\BaseballService” created. If you don’t, change this parameter as desired.) Notice the double backslashes. In Java the backslash is the escape character same as it is in C#. But there’s one important difference. In C# you could write this line like this:
Generator.main(new string[] {"http://173.46.159.199/baseballstats/BaseballStats.svc",
@"C:\projects\BaseballService"
});The @ sign in front of the destination directory indicates a verbatim string in C#. Java has no equivalent syntax; you need to use the double-backslash syntax.
Second, notice there’s a red underline beneath Generator, and red annotations in the vertical ruler, overview ruler, and the title tab. These are all error indicators. Hovering over either ruler annotation will display the error but hovering over the red underline will show a “quick fix” window. This is a context-sensitive list of suggestions by Eclipse to fix the error (you can also display this window by putting the caret in the underlined section and pressing Control + 1.) The quick fix window for this problem is this:
Usually the first one in the list is the most appropriate. In this case it is complaining that it cannot resolve the reference to “Generator” and is suggesting to import the Generator class from the org.restlet.ext.odata package. If this sounds familiar, it should. It’s the same as Visual Studio complaining that the “type or namespace name could not be found” and suggesting adding a using directive. Pick the first item in the list: Import ‘Generator’ (org.restlet.ext.odata) but don’t save the file yet. It will add the line “import org.restlet.ext.odata.Generator” to the top of the source file. Notice that the error indicator in the overview ruler has disappeared, but the error indicator in the vertical ruler has simply grayed out while the error indicators in the editor’s tab and in the package explorer are still displayed in red. This shows a subtle difference between background compiling in Eclipse and Visual Studio. If you perform the equivalent steps in Visual Studio, the error annotation will go away as soon as you add the using line. This shows that background compilation occurs as soon as you stop typing for more than a moment or two. Now in Eclipse save the file. Notice now that all the error indicators disappear. This shows that, in Eclipse, the compilation will not occur until you save the file. If there is a setting in Eclipse to make it work like Visual Studio I haven’t found it yet. But I actually prefer the Eclipse way.
Now that we’ve added the import and fixed our error we can run it. By default Run is Control + F11 and Debug is F11; if you want to remap that (and the debug Step, Step-Over, and Step-Into keys) to match Visual Studio you can do that in Preferences->General->Keys. Press F11 to run it in Debug mode.
Crap! We crashed. Why? Well, here’s what we got in the console window:
Exception in thread “main” java.lang.NoClassDefFoundError: freemarker/template/Configuration
at org.restlet.ext.odata.Generator.generate(Generator.java:236)
at org.restlet.ext.odata.Generator.main(Generator.java:134)
at com.droidbaseball.svcgenerator.MainClass.main(MainClass.java:14)
Caused by: java.lang.ClassNotFoundException: freemarker.template.Configuration
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
… 3 more
There’s a “ClassNotFoundException” in there. Basically that’s Java runtime saying “there’s a reference to a class in here that the compiler didn’t know about and I don’t know where it is.” There is a reference within the Generator.main() method to the “freemarker.template.Configuration” class. If you look in the Restlet lib directory (C:\lib\restlet-jse-2.0rc3\lib) there is a readme file that details the dependencies. According to the readme when using the OData extension we need a reference to org.freemarker_2.3. To add it, right-click the project in the Package Explorer, select Build Path->Configure Build Path. Select the Libraries tab and add the external Jar C:\lib\restlet-jse-2.0rc3\lib\org.freemarker_2.3\org.freemarker.jar. After clicking OK re-run the app using F11 and it will run to completion.
So what did we end up with? If you look in C:\projects\BaseballService you’ll see BaseballStatsOdataOrcswebService.java and a directory named baseballstatsmodel that contains 18 .java files. BaseballStatsOdataOrcswebService.java is essentially the service proxy; this is equivalent to what Visual Studio’s “Add service reference” feature would generate. The 18 files in the baseballstatsmodel directory are the entity objects.
Next time we’ll setup the Android project and review its parts. Then we’ll setup the layout of the first screen.
In part 1 I introduced the pieces needed to start developing for Android. For the remaining parts I’m going to make a few assumptions:
- You installed the 32bit version of the JDK to the default location.
- Eclipse is installed in C:\eclipse. There is no installer, just unzip the distribution archive into the rot of the C: drive.
- The Android SDK was installed in C:\android-sdk-windows.
- You followed the directions here to install the ADT Plugin, set the Android preferences to point to the SDK install location, and added the Android platforms to the SDK.
- You downloaded the Restlet Java extension for OData and unzipped the Android version into C:\lib\restlet-android-2.0rc3 and unzipped the JSE version to C:\lib\restlet-jse-2.0rc3. I found it easier while learning Java and OData to use the JSE version rather than the Android version. Release Candidate 3 is the current release recommended for new development (and version 2.0 is the one with OData support.)
- The first time you started Eclipse you accepted the default location for the workspace. You can have multiple workspaces even though the default for Eclipse is to put everything in the same workspace.
(Note: I actually completed this post a few days ago but as I was getting ready to post it Restlet posted Release Candidate 3 so of course I had to stop and test the sample with the new version. We now resume our regularly scheduled post.)
I’ll start with a few user requirements for the baseball application:
- The user should be able to enter a player’s last name. She should also be able to specify that she wants to search only for active players or for all players that ever played Major League Baseball.
- The user should then be able to select the player she is interested in from a list of all the players whose last name matched. The list should show enough information about the matching players to let her determine the correct one (you would be surprised how many players with the same name have played decades or even a century apart. Yes, the data goes that far back.)
- After the user selects the player we should show her more details about the player: where he played, where he went to college, where he was born, his batting statics, fielding statistics, and pitching statistics.
And some technical requirements:
- The statistics generally will not fit on a standard Android screen in portrait mode. This means we will need to react to and intelligently handle a device orientation change. We may even want to force landscape mode and not permit changing to portrait mode.
- We will be querying a live server over the network (either wireless or WiFi.) Obviously network queries can be time-consuming and Android has similar issues as .NET with respect to long running, blocking tasks running on the UI thread. Android also has similar restrictions as .NET with respect to updating the UI from a background thread.
- We would like to use good, solid software engineering principles. Separation of concerns, good testability, DRT, etc.
So without any further delay here are some prototype screen shots:
First row left to right: start up screen, search results list, player details.
Second row left to right: batting stats, fielding stats, pitching stats (if none found.)
OK, so it’s obvious where I fall on the whole designer vs. developer spectrum.
To get started our first job will be to create a project to generate the OData references and code for the baseball stats service. The Restlet library can query the service’s metadata and generate the entity classes automatically but that functionality isn’t in the Android libraries; it’s only in the JRE libraries. So within Eclipse click the File menu and select New->Java Project (not Android Project.) In the “New Java Project” dialog enter “JRERestletServiceRefGenerator” as the Project name and leave all the other settings at their default. The dialog should look like this:
Click “Next". On the second page (Java Settings) we’re going to add the additional libraries we need (this is comparable to Visual Studio’s “Add Reference” dialog except in Eclipse you can specify the external libraries at the same time you create the project.) Select the “Libraries” tab, click “Add External JARS…” and navigate to C:\lib\restlet-jse-2.0rc3\lib. Select “org.restlet.jar” and “org.restlet.ext.odata.jar” and click Open. It should look like this:
Click the triangle next to “org.restlet.ext.odata.jar” to expand the node. Select “Source attachment: (None)” and click the Edit button. In the Source Attachment Configuration dialog click the External Folder button, select “C:\lib\restlet-jse-2.0rc3\src” and click OK twice. Then select “Javadoc location: (None)”, click the Edit button, select “Javadoc URL” (which should be selected by default,) click the Browse button, select “C:\lib\restlet-jse-2.0rc3\docs\ext” and click OK twice. These settings tell Eclipse where to find the source code and the Javadoc for the Jar file (Javadoc is roughly equivalent to Intellisense.) We’re going to repeat the process for “org.restlet.jar” except the Javadoc location is “C:\lib\restlet-jse-2.0rc3\docs\api”. When done, the library settings tab should look like this:
Click “Finish.” Eclipse will chug for a second or two and then drop you into the Package Explorer with the newly created project.
If you expand JRE System Library you will see some of the Jar files that make up the Java runtime. If you expand Referenced Libraries you’ll see the two Restlet libraries we added plus a few more that were automatically added due to references:
“src” is where our source will go. Right click src and select New->Package. Think of a Package like a .NET namespace. We’ll use “com.droidbaseball.svcgenerator” as the package name.
In straight Java the package name can be just about anything you want but for Android the package name must follow a specific pattern. This is because on Android every application must have a different name so the OS can tell them apart and the package is part of the unique name. Click Finish to create the new package.
The final step for this post is to create our first source file. Right click the package we just created and select New->Class. This brings up the New Class Wizard:
Most of the fields are already filled in for us; we only have to add a couple of things:
- Give the class a Name; we’ll use “MainClass”
- Since this is our main class, we need an entry point; check the box next to “public static void main(String[] args)” – this should look familiar to C# developers.
Like .NET, everything in Java is an object that ultimately derives from java.lang.Object. One other point before we leave this dialog: many dialogs in Eclipse have content assist in various fields. Click in the Superclass field and notice a tiny light bulb show up to the left of the field:
That’s an indicator that content assist is available. Blank out the field, then type Obj and press Control+Space:
Every class that Eclipse currently knows about and that matches what you started entering in the field will be displayed in a popup list that you can choose from. This will come in handy later when we start creating Android classes.
For now, be sure the superclass is “java.lang.Object” and click Finish. Eclipse will think for a second then open our new class file.
Next time we’ll look at customizing Eclipse to make it a little more like Visual Studio. We’ll also add the code to our generator class (there isn’t much) and generate out service classes. Then we’ll set up the Android project.
No tags
At the end of 2009 I found myself with a little too much free time and started looking for something new to experiment with. I’ve always been interested in mobile development but I don’t own a Mac or an iPhone nor do I have any interest in purchasing one. I had read an article about Android and was starting to think about a new phone so I started looking at the Android SDK. There’s no fee to download the SDK or any of the tools.
Developing for Android requires writing code in Java and designing screens (called Layouts) using XML (although layout elements can be created all in code if you wish.) I’ve never programmed in Java before but Java and C# are so similar in syntax and there is such a wealth of information on the Internet about Java that I found it very easy to learn the basics. Also, the concept of designing layouts using XML is very similar to designing WPF and Silverlight UI’s in XAML.
To start Android developing you need a few things (all the details are at http://developer.android.com/intl/fr/sdk/requirements.html and http://developer.android.com/intl/fr/sdk/installing.html.)
- A PC running Windows XP, Vista, or Windows 7 (32bit or 64bit); Mac OS X 10.5.8 or better; or Linux. I’ve been using 64bit Vista and 64bit Windows 7.
- The Eclipse IDE version 3.4 or 3.5
- The Eclipse JDT plug-in.
- The Java JDK 5 or JDK 6 (the JRE isn’t enough, you need the full dev kit.)
- The Android Development Tools (ADT) plug-in. This isn’t strictly required but it does make life easier.
- Apache Ant (http://ant.apache.org) if you want to be able to build from the command line or if you want to use a continuous integration server. Yes, Ant is where the .NET tool Nant came from.
- The Android SDK itself.
- SDK Add-ons (mainly the Google Maps API) and the USB driver for Windows. The driver allows you to run and debug an application on an actual Android device but isn’t required if you don’t have a device or are using an OS other than Windows.)
You need 500MB of additional disk space for the SDK tools, platforms, samples, and offline documentation plus additional space for each virtual device you create. Each platform targets a different version of the Android OS and you must install at least one platform. If you don’t have an Android device you’ll need to create at least one Android Virtual Device (AVD). When you create an AVD you’ll specify which version of the Android OS it will run and how large the SD card will be. If you create an AVD with an 8GB SD card the emulator will create an empty 8GB image file that represents the SD card so plan disk space accordingly.
I won’t go into the details of installing all the pieces because the web site has excellent step-by-step directions.
So what have I been doing with it? Well, I started with the tutorials of course. The ubiquitous “Hello, World" tutorial does a good job of walking you through all the steps needed to build an app. The “Hello, Views” tutorial has several examples that highlight the different base layouts and views. “Hello, Localization” talks about using resources to localize an application and “Notepad Tutorial” goes step-by-step into writing a notepad application.
In March 2010 at the Microsoft MIX conference one of the announcements was about the Open Data Protocol and one of the partners was Restlet and their Java extension for OData. Restlet has a version of the library for Android. One of the things it will do is query the metadata from a .NET WCF service and generate all the service refs and POCO, excuse me, POJO objects similar to using “Add service reference” in Visual Studio. I started using Restlet to query the Netflix OData feed that was demonstrated at MIX but discovered some problems with the Restlet library that made working with the Netflix feed difficult (Restlet is in the process of fixing the problems.) Then I saw a tweet from Chris Woodruff (Woody from Deep Fried Bytes) that he had made a database of baseball statistics available via an OData feed. For the last couple of weeks I’ve been working on a demo Android app that can search this database by player last name and display player details, batting, fielding, and pitching stats. In the next post I’ll start showing the application and talk about what mistakes I made and what I learned while building it.
No tags
I just upgraded my source control server. I’ve been using SourceGear’s Vault for several years and while I don’t keep up with every version I don’t like to let too much time go by between upgrades. Their current version is 4.1.4 and was released in November 2008. I was still running 4.0.6 and since I’ve started using CruiseControl.NET I wanted to use SourceGear’s own Vault plugin.
I always hold my breath a little bit when upgrading Vault not because I’ve had any problems (I haven’t) but just because, hey, it’s my source code repository! It’s backed up regularly but if I ever had a problem it would be a major hassle to straighten it out. Luckily everything worked this time as well.
This will also be my first time using the enhanced client that uses the VSIP api instead of the MSSCCI api.
No tags
While setting up a CruiseControl.NET build server I added a project that uses ClickOnce deployment. I didn’t want to install Visual Studio on the build server and I don’t want to install the test certificate into the ccservice account so I wrote a plugin for CC.NET that takes the files from the project’s working folder, copies them to a deployment folder, creates the manifests, then signs them (I’ll blog about the plugin later.) But I ran into some weirdness while testing the deployment that I haven’t been able to resolve.
It works fine on XP Pro and IE 7. It works fine on XP Pro and IE 8. It works find on Vista 32 bit and IE 7. It doesn’t work so fine on Vista 64 bit and IE 8. IE 8 displays a dialog prompting me to download and install the .NET framework on a computer that already has the full version of Visual Studio2008 and the framework installed. If I cancel out of the dialog the whole process just stops. If I let the install continue, the framework installer comes up in maintenance mode (so it’s detecting that the framework is already installed.) If I log into the Vista 64 computer with a user that never logged in before then it works. So something in the main profile is corrupt. I can’t find it and Google is no help (too many entries about how to install IE 7 on a hacked Windows.)
No tags
I had been a little remiss in updating the blog (both posts and administrative stuff.) Updating a WordPress site is not particularly difficult but can be a little tedious because after backing up the site you either have to delete a bunch of files (but only certain files) from several different directories (but not all) then copy in the new files. I’ve done that in the past but I’ve also updated just by copying new files over top of the old ones. It’s not the recommended way to do it but I’ve never had a problem. Bottom-line is either way it’s a task that needs to be scheduled and with all the other tasks I need to do it usually gets pushed off.
So while looking for an anticipated release date of version 2.7 I stumbled over a reference to an automatic upgrade installer plugin (you can find it here.) Very simple to use, takes only a few minutes, and still follows the “best-practices.” Only glitch I ran into is Vista refuses to open the .ZIP files that the upgrade creates when it backs up the existing files and database. WinZip on WindowsXP has no problem with it.
While I was at it I added the new encryption salts to the configuration file (this is a feature added to WordPress 2.5 and enhanced in 2.6.) Hey, a little more security never hurt anyone especially if it’s transparent to the visitor or user.
No tags
Since the dashboard project reached a new milestone last month I’ve had time to start planning the next two projects. The first is a private Internet site for customers to place orders, check on order status, check on payment status, etc. The second, and much larger, is a total rewrite of the order entry, shipping, inventory, receivables, payables, and general ledger system from the mish-mash of legacy languages and data storage to a .NET / SQL Server based system.
The dashboard uses a combination of the patterns & practices Enterprise Library and naked ADO.NET. The client is deployed via ClickOnce and talks to the server via ASPX Web Services. It’s built using the 2.0 framework and that was about it for data access options (I’m ignoring things like NHibernate, Subsonic and LLBLGen for the moment.) Now with the new projects I’m reviewing the current crop of options, starting with Entity Framework (which RTM’d when SP1 for VS2008 was released) and LINQ to Entities. I built the database and then created the entity model:
I’m starting with some simple CRUD operations for the salesman, route, chain, and customer tables:
For the moment the data model closely matches the legacy data model; that’s why the orders table references the customer table and the chain table while the customer table also references the chain table. This will most likely change going forward as I refine the model.
Then I created an ugly-as-sin, break-every-rule form for the chain table:
Here’s the FormLoad:
1: void HandleFormLoad(object sender, EventArgs e)
2: {
3: InitializeForm();
4: LoadStatesDropdown();
5: TransitionToMode(FormMode.KeyMode);
6: txtChainCode.Focus();
7: }
InitializeForm() simply sets the MaxLength of each TextBox; it’s hard-coded now but I’d like it to come from the model’s meta-data.
LoadStatesDropdown() does exactly what it says; it uses a small piece of LINQ to populate an Infragistics combobox with the states:
1: private void LoadStatesDropdown()
2: {
3: var q = from s in ctx.states orderby s.abbrev select s;
4: cboChainState.DataSource = q;
5: cboChainState.ValueMember = "abbrev";
6: cboChainState.DisplayMember = "abbrev";
7: }
TransitionToMode() enables or disables controls and sets up or clears data binding depending on the form’s mode (KeyMode, EditMode, or AddMode.)
Here’s the data binding code:
1: private void SetDataBinding(bool setIt)
2: {
3: if (setIt)
4: {
5: txtChainCode.DataBindings.Add(new Binding("Text", currentChn, "chaincode", false, DataSourceUpdateMode.OnPropertyChanged));
6: txtChainName1.DataBindings.Add(new Binding("Text", currentChn, "chainname1", false, DataSourceUpdateMode.OnPropertyChanged));
7: txtChainName2.DataBindings.Add(new Binding("Text", currentChn, "chainname2", false, DataSourceUpdateMode.OnPropertyChanged));
8: txtChainAddr.DataBindings.Add(new Binding("Text", currentChn, "chainaddr", false, DataSourceUpdateMode.OnPropertyChanged));
9: txtChainCity.DataBindings.Add(new Binding("Text", currentChn, "chaincity", false, DataSourceUpdateMode.OnPropertyChanged));
10: cboChainState.DataBindings.Add(new Binding("Value", currentChn, "chainstate", false, DataSourceUpdateMode.OnPropertyChanged));
11: txtChainZip.DataBindings.Add(new Binding("Text", currentChn, "chainzip", false, DataSourceUpdateMode.OnPropertyChanged));
12: txtChainPhone.DataBindings.Add(new Binding("Text", currentChn, "chainphone", false, DataSourceUpdateMode.OnPropertyChanged));
13: }
14: else
15: {
16: txtChainCode.DataBindings.Clear();
17: txtChainName1.DataBindings.Clear();
18: txtChainName2.DataBindings.Clear();
19: txtChainAddr.DataBindings.Clear();
20: txtChainCity.DataBindings.Clear();
21: cboChainState.DataBindings.Clear();
22: txtChainZip.DataBindings.Clear();
23: txtChainPhone.DataBindings.Clear();
24: }
25: }
I spent a lot of time searching online for databinding examples. All the ones I found were in the context of binding a grid or a list. None of them illustrated binding other controls to entities. After I figured it out it was pretty simple (note that the chain table contains no foreign keys; more on that later.)
Here’s the code behind the Submit button:
1: void HandleSubmitClick(object sender, EventArgs e)
2: {
3: if (!String.IsNullOrEmpty(txtChainCode.Text))
4: {
5: string findChain = txtChainCode.Text.Trim().PadLeft(3, '0');
6:
7: currentChn = ctx.chains.Where(c => c.chaincode.Equals(findChain, StringComparison.OrdinalIgnoreCase)).FirstOrDefault<chain>();
8:
9: if (currentChn == null)
10: {
11: if (MessageBox.Show(this, String.Format("Chain '{0}' was not found; would you like to add it?", findChain), "Chains",
12: MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
13: {
14: currentChn = new chain();
15: currentChn.chaincode = findChain;
16: TransitionToMode(FormMode.AddMode);
17: txtChainName1.Focus();
18: }
19: else
20: {
21: txtChainCode.Text = String.Empty;
22: txtChainCode.Focus();
23: }
24:
25: }
26: else
27: {
28: TransitionToMode(FormMode.EditMode);
29: }
30: }
31: }
In the legacy system, things like a chain code are numeric only, right-justified, zero-filled. In the original system they were the primary key; I plan on changing that to use a Guid as the primary key and use the human-readable code as a surrogate key.
It took my a little while to figure out how to handle foreign key constraint violations and I’m still not sure it’s the right way to do it:
1: void HandleDeleteClick(object sender, EventArgs e)
2: {
3: // only edit mode
4: if (DialogResult.Yes == MessageBox.Show(this, "Are you sure you want to delete this chain?", "Delete Chain",
5: MessageBoxButtons.YesNo, MessageBoxIcon.Question))
6: {
7: try
8: {
9: ctx.DeleteObject(currentChn);
10: ctx.SaveChanges();
11: currentChn = null;
12: TransitionToMode(FormMode.KeyMode);
13: txtChainCode.Focus();
14: }
15: catch (UpdateException ex)
16: {
17: // referential integrity exception
18: ctx.Refresh(RefreshMode.StoreWins, currentChn);
19: MessageBox.Show(this, "Unable to delete chain; the chain is currently referenced by another part of the system.",
20: "Delete Chain", MessageBoxButtons.OK, MessageBoxIcon.Information);
21: }
22: }
23: }
Again, I couldn’t find any mention of how to handle foreign key constraint violations. There are plenty of example on how to handle optimistic concurrency collisions; that’s where RefreshMode.StoreWins comes in and it seems to work correctly here as well. Without the ctx.Refresh() line, the failed DeleteObject() stays in the object context and the framework will try to execute it each time you call SaveChanges().
One thing I’m still trying to figure out is how to databind related objects. In the diagram above there are foreign keys from the customer table to the route table, salesman table, and chain table. In other words, a customer is in a route, has a salesman, and is in a chain. Here’s a portion of the code in the customer form’s SetDataBinding() method:
1: Binding b;
2: txtAccount.DataBindings.Add(new Binding("Text", currentCust, "account", false, DataSourceUpdateMode.OnPropertyChanged));
3: umeChain.DataBindings.Add(new Binding("Value", currentCust.chain, "chaincode", false, DataSourceUpdateMode.OnValidation));
4:
5: b = new Binding("Value", currentCust, "wkscredit", true, DataSourceUpdateMode.OnPropertyChanged, 0);
6: b.Format += new ConvertEventHandler(HandleFormatWeeksCredit);
7: b.Parse += new ConvertEventHandler(HandleParseWeeksCredit);
8: spnWeeksCredit.DataBindings.Add(b);
9: umeSalesman.DataBindings.Add(new Binding("Value", currentCust.salesmen, "smancode", false, DataSourceUpdateMode.OnPropertyChanged));
umeChain is a masked edit control that displays the customer’s chain code.
If the user changes the value for the chain control I want to retrieve the matching chain entity, attach it to the customer entity, and update the display. If the user entered an invalid chain, I want to whine at them and change it back to what it was. Pretty basic stuff that’s been done since the year one.
Here’s the code attached to the chain control’s Validating event:
1: void umeChain_Validating(object sender, CancelEventArgs e)
2: {
3: // Did it change?
4:
5: if (!String.IsNullOrEmpty(umeChain.Text))
6: {
7: string tempChn = umeChain.Text.Trim().PadLeft(3,'0');
8: if (tempChn.Equals(currentCust.chain.chaincode)) // if the chain didn't change, don't do the query.
9: return;
10:
11: // Have to return a chains entity here so we can attach it to the customer.
12: chain chn = ctx.chains.Where(c => c.chaincode.Equals(tempChn)).FirstOrDefault<chain>();
13: if (chn == null)
14: {
15: MessageBox.Show(this, "Invalid chain", "Customers", MessageBoxButtons.OK, MessageBoxIcon.Warning);
16: e.Cancel = true;
17: umeChain.Value = currentCust.chain.chaincode;
18: }
19: else
20: {
21: if (!currentCust.chain.Equals(chn))
22: {
23: currentCust.chain = chn;
24: umeChain.Value = currentCust.chain.chaincode;
25: }
26: }
27: }
28: }
This is where I’m currently having trouble. If I change the chain to one that actually exists (for example, 079 instead of 078) it throws an exception on SaveChanges():
A duplicate key error. Opening a quick watch on the context object shows a ModifiedEntityCount of 1 and an AddedEntityCount of 1. Nothing should have been added; this example is editing an existing entity. Drilling down into the ObjectStateManager shows one item in the _addedRelationshipStore (the EntityKey for the chain we changed to,) one item in the _deletedRelationshipStore (the EntityKey for the chain we changed from) and one item in the _modifiedEntityStore (the EntityKey for the chain we changed from not the EntityKey for the customer we changed.) If this was a bug I can’t believe it would have gone undiscovered so obviously I’m doing something wrong.
More to come…
No tags
OK so maybe there won’t be blood but it’s still interesting that Vlad is turning out to be better at playing Microsoft’s game than Microsoft is.
He’s already offering the type of hosted services that Microsoft has only been talking about and is hosting them in privacy-friendly Europe.
Note to self: look into his backup services. Have a couple of clients that have more data than they can cost-effectively back up to tape.
Tomorrow he will talk about what he’s “got in store for her majesty’s prisoners.” Since it used to be a penal colony for England I’m guessing he means Australia.
No tags
During SMB Summit 2008 in Dallas Microsoft recorded the presentations on the first two days. The first day was Small Business Server 2008 while the second day was Essential Business Server 2008 (both have since shipped public betas.)
The videos have been posted the the private partner portal (you have to be at least a registered partner and sign in to view them.) Easiest way to find them is from the SMB Summit page.
No tags
I make it a point to follow Vlad’s blog. Even though he tends to use a sledge hammer where a more “delicate” approach might be called for you usually end up thinking about his posts for days. The best ones are where he holds up a “business mirror” and you don’t necessarily like the reflection you see.
So the other day he posted this. As StaceyC said in the comments, time to make some popcorn (cheddar for me please.)
No tags