Wednesday, April 16, 2008
Data Load Controllers
We have recently introduced a fully-automatic way of creating external data loading mechanisms. This is achieved by means of so-called "data load controllers". So what is the basic idea behind these? Well, I am glad you asked! :-)
Milos provides great ways to load data in all kinds of data edit forms, from Windows to the Web, in all kinds of variation. For instance, one can simply use the SetEntityType<>() method to automatically load an entity of a certain type in a window or a web form. It is also possible to override LoadContents() and LoadSecondary() data methods to load additional data, or load it in slightly different ways.
This makes for easy ways of loading data. However, in some scenarios, we want to go even further. (We will always continue to support this approach as it provides the basis for all operations, but we want to provide more options). There are a few scenarios we want to simplify. Here are those considerations:
- This approach generally relies on inheritance. That is OK in many scenarios, but in some scenarios (such as ASP.NET web forms or WPF/Silverlight XAML), there are scenarios where one just wants to use an object instance for data loading. For instance, one might want to put a data panel into a web form without explicitly subclassing it. Those scenarios are currently supported by means of an event model. That is not a bad approach, but it is slightly different from the inheritance model, and the written code is not IL-compatible, since method signatures are differently, and so forth.
- Different interfaces may have a need to load the same data. For instance, 10 different forms of a windows application may need to load certain lookup tables. Or, an ASP.NET web form may need to load the same exact data as the WinForms or WPF equivalent. Neither the inheritance, nor the event model allows for simple code reuse in these scenarios.
- It is very hard to unit test data loading for UIs if the code is embedded in, or attached to the user interface classes.
We now provide an additional abstract option: All the data loading (and data handling) code can be put in an abstract class. Here is an example of such a class:
public class NameEditLoader : DataLoadController
{
public override void Configure()
{
this.SetEntityType<NameBusinessEntity>();
}
public override object LoadSecondaryData()
{
NameBusinessObject names = NameBusinessObject.NewInstance();
DataSet dsNames = names.GetList();
names.Dispose();
return dsNames;
}
}
If you are familiar with current Milos loading code, then you will recognize all of this, because the code is identical to code that would go into a Windows Forms class as well as other types of UI classes. The main difference is that this code now sits in an external class. This class can now be used in an interface class. For instance, it could be used in a Windows Form or a WPF window like so:
public MyWindow() // Constructor
{
InitializeComponent();
this.DataLoadControllers.Add(new NameEditLoader());
}
The same exact object can also be used on a web form:
protected void Page_Load(object sender, EventArgs e)
{
this.dataEditPanel1.DataLoadControllers.Add(new NameEditLoader());
}
The same object could also be used in XAML, or in a mobile app, and so forth. Also, the object can now be used stand-alone in unit test scenarios, which is extremely important in test driven development (TDD) and other scenarios where unit tests are needed. (And most applications need unit tests these days).
So we think this is a cool change. It is completely optional and everything that was there before is still available. But going forward, this is clearly the recommended approach.
Posted @ 4:08 AM by Egger, Markus (markus@code-magazine.com) - Comments
Thursday, April 10, 2008
Milos Web Development supports jQuery
Have you ever used jQuery? (www.jQuery.com). It is a very cool JavaScript library that makes all kinds of client-side HTML development much easier. The most recent version of Milos web control (ASP.NET) now use jQuery for internal stuff and it is also available for you to use in your own client-side efforts. Whenever you include SimpleWebControls.dll in one of your projects, you automatically get access to jQuery stuff. All you have to do is include a script reference, as well as the Milos resource HTTP handler in the web.config (which you should always have anyway). So the web.config needs to have this:
<
httpHandlers>
<!-- Other stuff here... -->
<add verb="*" path="Milos_simplewebcontrols_*.aspx"
type="EPS.Web.SimpleControls.ResourceRequestHandler,
SimpleWebControls"/>
</httpHandlers>
With this in place, you can simply add a <script> element to your page to get the jQuery features:
<script
src="milos_simplewebcontrols_getresource.aspx?resourcetype=Script&resourcename=JQuery"
type="text/javascript"></script>
Note: This aspx page does not really exist. Whenever this URL gets hit, Milos intercepts the call and returns a dynamically generated script stream to the client.
With this in place, you can now add jQuery stuff like this:
$(document).ready(function() {
alert("Main document content loaded!");
});
For more information on jQuery syntax and features, visit www.jscript.com, or get one of the many jQuery books out there (the jQuery site lists several).
Note: Milos also uses jQuery features for some of its advanced scripting. For this reason, it could happen that the script reference gets included multiple times, which could lead to unexpected results. For that reason it is better to add the script reference in the Load() event of the page (or a control) using this code:
if (!Page.ClientScript.IsClientScriptIncludeRegistered("JQuery"))
{
page.ClientScript.RegisterClientScriptInclude("JQuery",
"milos_simplewebcontrols_getresource.aspx?resourcetype=Script&resourcename=JQuery");
}
This automatically adds the script reference from above, but it only adds it if the reference isn't there already.
There is a lot of cool stuff that can be done with jQuery, from simplified event handling to relatively advanced (for HTML) visual effects. Milos uses this for a number of things, such as the cool new overlay dialogs. But that is a topic of a different post... :-)
Posted @ 1:03 PM by Egger, Markus (markus@code-magazine.com) - Comments
Friday, April 04, 2008
Lessons learned - Little new ones, big old ones
We’ve got an in-house application that we’re working on as a training exercise. Like most projects of this type we find ourselves relearning old lessons.
We wanted to extend the functionality of the standard windows DateTimePicker control by creating a new composite control with a DateTimePicker and adding spinners for hours/minutes. We started out with the DateTimePicker and two spinners (pretty obvious first step) and gave the class a DateTime property to hold the core DateTime value. We then data bound the DateTimePicker and the two spinners to the DateTime property. The DateTimePicker was bound to the DateTime property and the spinners were hooked up to the hour or minute components of class’ DateTime property.
We wired everything up and tried running the shell application. Every time we touched the composite control we got exceptions revolving around one of the constituent controls (dtPicker or spinner) having a null value. Mike Yeager wandered in and made the fateful observation that we had “grossly over engineered” the composite control by having each constituent control separately data bound to the same property. After all, the entire DateTime value was stored within the dateTimePicker control.
We removed the databinding of the two spinners to the DateTime property and created properties for hours and minutes that were based on the existing hour/minute components of the dateTimePicker. The getter of the hours/minutes properties returned the hour or minute component of the DateTime value in the dateTimePicker. The setter of the properties called a method that concatenated the year/month/day components of the dateTimePicker with the values of the hour and minute spinners into a new DateTime value. The spinners were hooked up to the hour and minute properties.
It took about 15 minutes to scrap the old code, reassemble the controls and properties, and then bind everything together. It worked on the first try!
- The “new” lesson learned was specific and almost trivial: When extending the functionality of a multi-faceted control, use that control’s internal value as the basis for your work rather than reinvent the (DateTime value) wheel by pushing three separate controls at one value.
The “old” lesson is: “Think about what you’re going to do before you even touch the keyboard. You should have a clear vision of your objective before writing the first line of code."
Claudio will be amplifying on this in his blog, complete with code samples.
Posted @ 4:13 PM by Chichester, Thom (tchichester@eps-software.com) - Comments
Tuesday, February 05, 2008
Exception Handling and Logging Framework
Milos has always provided features around exception handling. In this upcoming build however, we have formalized a lot of the functionality (and added to it as well) to create two distinct sub-frameworks:
- An Exception Handling framework
- A Logging framework
The idea is the exception logging framework is pretty simple: Whenever an exception occurs that remains unhandled, that exception can be "published" to the exception framework via a so-called "Exception Mediator" object. This object receives the exception and then passes it on to all kinds of interested listeners that can be registered with the mediator. Such listeners could be UI components to show exception information to the user (listener as well as UI components are provided by default and can be extended), or the information can be emailed to an administrator, or perhaps the information needs to be logged. The framework also includes components that can be used to retrieve detailed exception information such as an exception hierarchy or the current call stack.
The logging framework can be used to log various information ("events") to different destinations. For instance, it is possible to log events to text files or XML files of various formats. It is also possible to log to the Windows system log on the local machine or a remote machine. Furthermore, it is possible to log to email recipients or the Console or message boxes, or... And of course, it is also possible to easily combine all these loggers and even extend them with your own.
In addition to the class reference that includes examples for all these features, we have also written 2 whitepapers that explain the use of these frameworks.
Posted @ 4:55 PM by Egger, Markus (markus@code-magazine.com) - Comments
Thursday, January 17, 2008
Generic Sub Item Collections in Business Entities
The latest Milos version has a pretty neat enhancement in the business entity model: Sub item collections are now available as generics. Here's why that's cool:
So far, in order to create collections of sub items in business entities ("1-to-many relationships"), one had to create the individual item objects, as well as a collection class that could hold those items. The trouble with that is that it is a lot of work to create the collection class, and the only benefit of this class is a strongly typed interface. (The strongly typed interface is needed so one can access parent.Children[x] and then get all the properties on that object).
Using the new generic version of the sub item collection, one usually does not have to create a subclass. Instead, one just creates the class for the individual items, and then instantiates the generic version of the collection, with the generic being the item type.
Example: An invoice entity with a line item collection could simply instantiate an appropriate collection like so:
protected override void LoadSubItemCollections()
{
this.colLineItems =
new GenericEntitySubItemCollection<LineItem>(
this,
"PK_LineItem",
"FK_Invoice",
"Invoices",
"PK_Invoice",
this.GetInternalData().Tables["LineItems"] );
}
For more details and a detailed explanation, consult the "How-To: Creating a Collection of Child Items" topic in the Milos documentation.
Of course, the old way is still supported as well. The main benefit of using the generic version is in creating collections by hand. When using any of the code generator tools, one isn't exposed to the collection anyway (in most cases), so the benefit is minimal. So far, the code generator tools do not use the generic collection yet. There are considerations around backward compatibility. However, we are considering to change the tools in the future...
Posted @ 4:05 PM by Egger, Markus (markus@code-magazine.com) - Comments
Wednesday, December 05, 2007
IBindingList Support on Sub Item Collections
Here is a small feature I really hope everyone appreciates, considering how hard it was to support it in a nice way: All sub item collections in Milos now implement IBindingList, which is a fancy way of saying that they can now be bound to pretty much anything that supports data binding.
In the past, most of the Milos controls supported binding to sub-item collections, but with third party controls, it was hit and miss. Adding support for this interface (in addition to the interfaces we supported previously) pretty much covers the whole range now.
Posted @ 11:39 AM by Egger, Markus (markus@code-magazine.com) - Comments
Saturday, October 13, 2007
'Split-Table' Table Updated
Here is a new feature that only some people will need, but those who need it really do... ;-)
The Milos BusinessObject class features a SaveTable() method, with takes a DataTable as a parameter (among other things) and can then (by means of using various DataService features) simply persist all the changes in the data table back to the database server (whether that's SQL Server or some other database technology). This feature is used extensively by the BusinessEntity infrastructure (among other things). So for instance, when you load a business entity that has a master table and several child tables, each of the tables inside the entity is saved using the SaveTable() method.
However, it has been brought to our attention that people sometimes like to load tables into entities or DataSets that are not just simple tables, but they may be created by joins that incorporate multiple tables. In that case the SaveTable() method was not able to save the table, since every record in the DataTable really mapped to different tables in the database. We have now added a feature to Milos that can handle this scenario in many (although not all!) cases. The basic idea is this: We provide a new SaveTable() overload with parameters that allow you to indicate which fields to include in the save operation, and what the table name maps to, and even field maps in case some of the fields are renamed.
So let's say you created a join that returned a table that has fields from a Contacts table, such as ContactID (maps to ID), FirstName, LastName, and also fields from a 1:1 joined CustomerInformation table like CustomerID (maps to ID), CustomerNumber and CreditLimit. You could save this information back to the database in a two-step operation, like so:
string[] contactFields =
new string[] { "ContactID", "FirstName", "LastName" };
Dictionary<string, string> contactFieldMaps =
new Dictionary<string, string>();
fieldMaps.Add("ContactID", "ID");
bool retVal = this.SaveTable(
existingDataSet.Tables["CustInfo"], "ID",
"Contacts", contactFields, contactFieldMaps, false);
if (retVal)
{
string[] contactFields = new string[]
{ "CustomerID", "CustomerNumber", "CreditLimit" };
Dictionary<string, string> contactFieldMaps =
new Dictionary<string, string>();
fieldMaps.Add("CustomerID", "ID");
bool retVal = this.SaveTable(
existingDataSet.Tables["CustInfo"], "ID",
"CustomerInformation", contactFields,
contactFieldMaps, true);
}
In this example, the first SaveTable() operation is limited to the ContactID, FirstName, and LastName fields. Also, the primary key for the Contacts table is really ID, so we create a map for that. (This works for all fields, not just primary keys). If that save is successful, we perform a second save operation for the remaining fields. Not that in the second operation, we pass a true as the last parameter, which flags the data table as unchanged in memory (this is not done after the first save, since other changes still exist).
So this works nicely, but there are a few things you need to know. First, this works best if the table contains 1:1 relationship data. In one-to-many scenarios, the parent "record" exists multiple times, and thus really shouldn't be updated. You can still use the same functionality to update the information from the second part of the table, but you should be very careful updating the parent information, since it will potentially result in multiple updates that can each override each other. Another potential issue is that in memory, each record only has one record state. So the record is either flagged as unchanged, deleted, modified, or added. However, if the parent-part is modified, but the child-part should be considered added, then that would not work and the generated update statements would be incorrect and potentially fail, or - worse - cause incorrect data to be written to the database. So be careful with that.
One aspect of this that isn't immediately obvious is that this new functionality can also be used in scenarios where you just query a single table, but the update needs to map field names, or limit the update to certain fields. For instance, if an empty string array of field names is passed to this method and a mapping dictionary is passed, then all fields will be saved, but they can be mapped. Or, you can pass an empty dictionary, but pass a limiting list of field names, to exclude certain fields from update. Both scenarios can be very useful in a number of scenarios.
Posted @ 11:48 AM by Egger, Markus (markus@code-magazine.com) - Comments
Friday, October 12, 2007
Minor String-Array Helper Method
This is a pretty small item, but I find it useful in a lot of scenarios: There now is a new ArrayContainsString() method on the StringHelper object. It provides a quick way to check whether a certain string array contains a certain string, and it can do so in a case insensitive fashion:
string[] testArray = new string[] { "One", "Two", "Three" };
bool result1 = StringHelper.ArrayContainsString(testArray, "one", true); // returns true
bool result2 = StringHelper.ArrayContainsString(testArray, "one"); // returns false
bool result3 = StringHelper.ArrayContainsString(testArray, "One"); // returns true
bool result4 = StringHelper.ArrayContainsString(testArray, "Four"); // returns false
As I said, this is not an earth-shaking feature, but it is kinda nifty...
Posted @ 2:26 PM by Egger, Markus (markus@code-magazine.com) - Comments
Sunday, September 09, 2007
Automatic Window Title Updates on Navigation
Here is a small but nice tweak: When using navigation UIs, in particular the Office application forms, or the Developer Express application forms, Milos now supports automatic updates of the windows title (the secondary title, actually), from the pane's caption (the pane the user navigates to). This behavior is optional, and can be controlled by means of the UpdateWindowSubTitleFromNavigation property, which can be found on all app form classes.
Note: Related to this change is an update to the INavigationHost interface, which now features a NavigationCompleted event. If you implement this interface somewhere, you simply need to add that event to your implementation as well. The code required to do so is simple:
/// <summary>
/// Occurs after a navigation.
/// </summary>
public event System.EventHandler NavigationComplete;
Whether you actually use the event or not is up to you. However, we recommend you call if after navigating, which is done in the Navigate() method that goes with the same interface. Here is the new standard implementation for that method:
/// <summary>
/// Triggers a navigation based on the specified URI
/// </summary>
/// <param name="uri">URI to navigate to</param>
/// <param name="ignoreNavigationHistory">Defines whether the current navigation needs
/// to be added to the navigation history. For instance, a go-back operation is already
/// in the history and thus does not have to be added, while a navigation to a new location
/// has to be added to the history.</param>
/// <returns>True if navigation is successful</returns>
public bool Navigate(string uri, bool ignoreNavigationHistory)
{
bool retVal = NavigationHelper.Navigate(uri, this.History, this, ignoreHistory,
this.ForwardControls, this.BackwardControls, this);
if (this.NavigationComplete != null)
{
this.NavigationComplete(this, new EventArgs());
}
return retVal;
}
That's it! Small change, but nice to have.
Posted @ 3:09 AM by Egger, Markus (markus@code-magazine.com) - Comments
Saturday, September 08, 2007
Query Simplification
As some of you may know (although not all... which is what we are trying to address), the Milos DataService classes provide a simple way to query multiple records by multiple field names. For instance, if you want to query all customer with a last name of "Pronger" and a first name of "Chris" and active status, then you have been able to simply do this (in a business object):
string[] fieldNames = new string[] { "FirstName", "LastName", "IsActive" };
object[] parameters = new object[] { "Chris", "Pronger", true };
IDbCommand command = this.DataService.BuildQueryCommand("Customers", "*", fieldNames, parameters, DataRowProcessMethod.IndividualCommands);
DataSet customers = this.ExecuteQuery(command);
This is nice, but a lot of people do not know that this functionality exists. Besides, there really is no reason why people would have to know about this somewhat hidden method on the DataService. For this reason, we added a few convenience methods right on the business object that are easier to discover:
string[] fieldNames = new string[] { "FirstName", "LastName", "IsActive" };
object[] parameters = new object[] { "Chris", "Pronger", true };
IDbCommand command = this.GetQueryCommand("Customers", "*", fieldNames, parameters);
DataSet customers = this.ExecuteQuery(command);
Or, to make things simpler yet:
string[] fieldNames = new string[] { "FirstName", "LastName", "IsActive" };
object[] parameters = new object[] { "Chris", "Pronger", true };
DataSet customers = this.Query("Customers", "*", fieldNames, parameters);
There really is no particular reason for using GetQueryCommand() over Query(), unless you want to first retrieve the command object and then make further modifications on the object (which is hardly ever done). So just use Query().
The values in the field and filter list are all handled as "and" parameters. Also, all fields are used "as is" with no added wild cards. For instance, passing in "Pron" as the last name will not include "Pronger" in the result set. However, it is possible to pass in "Pron%" which will include "Pronger"s, assuming the database understands that wildcard character.
Posted @ 1:02 PM by Egger, Markus (markus@code-magazine.com) - Comments
Wednesday, July 18, 2007
Milos and O/R Mapping: Stored Procedure calls
It's been a while, but let's continue with the series on Milos and O/R Mapping. This time around, discussing Stored Procedure calls.
On the last installment of this series we've seen how to execute queries by running individual SQL statements (dynamic SQL). Executing Stored Procedures is not that much different: the main difference is that instead of using the ExecuteQuery method, we use the ExecuteStoredProcedureQuery method. For example, the following method in a business object would execute a stored procedure named GtPaymentsList:
public DataSet GetPaymentsList()
{
IDbCommand command = this.NewDbCommand("GetPaymentsList");
command.CommandType = CommandType.StoredProcedure;
return this.ExecuteStoredProcedureQuery(command);
}
In case the stored procedure only processes some data and does not return anything to the caller, a method such as the one below will do the job of calling the stored procedure:
public void ProcessPayments()
{
IDbCommand command = this.NewDbCommand("ProcessPayments");
command.CommandType = CommandType.StoredProcedure;
this.ExecuteNonQuery(command);
}
Another aspect of using stored procedures is that we may want all data access in our business objects to go through stored procedures. By default, when we use business objects and entities, Milos uses "individual commands" (dynamic SQL), which means it creates the SELECT, UPDATE, INSERT, and DELETE commands on the fly, depending on what the business object needs. Alternatively, the object can be set so that instead of creating SQL on the fly, Milos can look for Stored Procedures that follow a naming convention (we have code generators that create the necessary stored procedures). In order for that to work, all the developer needs to work is to code-generate the stored procedures, and configure the business object class, by adding the following line to the Configure method:
this.SetDataAccessMethod(EPS.Data.DataRowProcessMethod.StoredProcedures);
Milos can also be set so that the only data access method permitted is through stored procedures (which means that anything going through the data access layer has to be a Stored Procedure call). This can be set by adding the following configuration setting:
<add key="AllowedDataMethod" value="StoredProcedures"/>
That is it for now. :)
Posted @ 4:45 PM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Wednesday, June 06, 2007
Recent Grid Enhancements
This upcoming build of Milos has some enhancements to all our grid controls that I know some of you have been waiting for:
- The grid now supports resorting of bound sortable collections.
- The grid now automatically (and optionally) remeasures grid row heights when columns are resized. (This is only important in scenarios where grid rows have dynamic heights).
- The ListBase class (the "mother of all Milos grids") had a problem with owner drawn rows that used variable (non-standard) row-heights. In some special scenario with very few rows shown, scrolling the grid via the mouse wheel caused an exception to be thrown. This has been fixed.
- As listed in the previous post, there were a number of databinding enhancements.
- Some people reported problems using the ScrollBottom() method. This has been fixed.
- The grid now provides better access to the grid row collection. The collection now is enumerable and strongly typed. Both the column and row collections have also been performance optimized.
- In some rare scenarios, reassigning a data source of the grid caused row selections to go all out of whack. This has been fixed.
In general, quite a few optimizations have been applied to the grid.
Note that these things apply to all Milos grids, including ListBase, the OfficeGrid, the TouchGrid, and even controls such as the list of binary document attachments (among others).
Posted @ 4:17 PM by Egger, Markus (markus@code-magazine.com) - Comments
Wednesday, June 06, 2007
More Databinding Stuff
Today or tomorrow, we will have a new Milos build. It includes a number of improvements (and some fixes) for WinForms databinding. (Some of which have been discussed before).
Here is a list of a few other things that are included:
- Both our listbox and combobox classes had some issues binding to data in certain scenarios (basically, our enhancements only handled certain scenarios, but people were left with basic .NET functionality in others). In particular, some scenarios that bound these controls to DataSets or other collections had refresh problems. Enhancements have been made to handle those scenarios properly.
- There was a problem with the Office Toolbar control that caused people to tab out of the active control before they hit save, otherwise the last edited control was not bound properly. This is fundamentally a .NET issue, so it may still exist in interfaces that do not use these controls (mainly things like third party components), but it has been fixed for Milos controls (Office controls).
- The Milos date dropdown control (office look and feel) had a few data binding bugs (no way to blame this one on MS or a third party, I am afraid :-)). These are now fixed.
- This is not related to data binding, but it is worth mentioning that this control now also supports a more lenient data entry. For instance "7/5" is interpreted as July 5th of the current year. (This also works culture specific, so "7.5." in a German app would be interpreted as May 7th of the current year).
- Standard Milos WinForms radio buttons were not bindable before. They have been enhanced to support data binding.
- While we were at it, we also added a radio button group container control that makes it quite a bit easier to use and bind radio buttons.
- All Milos grids have been enhanced quite a bit. Some changes are databinding related (the others I will discuss separately):
- In the past, editable grids could only bind to data types that matched the controls the column used. So a textbox based edit column could only bind to string properties/fields. This mechanism has now been enhanced to allow automatic parsing for byte, int, decimal, float, double, and datetime fields.
- Grids now support re-sorting when bound to sortable collections.
- There also was an issue with bound editable controls that were scrolled horizontally (causing inaccurate placement of the editable areas). This has been fixed.
Not quite databinding, but still related: Milos data edit classes have been cleaned up a bit and optimized. We also added quite a few convenience methods that make it easier to reload data and the like.
Posted @ 4:08 PM by Egger, Markus (markus@code-magazine.com) - Comments
Wednesday, May 23, 2007
Milos Training: many videos are now available online!
We've created a bunch of videos to help us training new team members on Milos, and we figured these videos should also be helpful for our clients who are using Milos, as well as for those who'd like to check out what kinds of things are part of Milos. Below is a list of the videos currently available, in the respective order they should be watched. We are going to have more videos coming up soon. Stay tuned!
If there are things that you really would like to see explained in videos, please send us an email, and we will definitely consider it.
Posted @ 1:48 PM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Wednesday, May 23, 2007
Improvements to the Business Object / Entity generator
One of our main tools in Milos has always been our Business Object / Entity generator, that's available within the EPS Developer Services tool. That feature had some crucial limitations, though, so we've revamped it, including a number of fixes and improvements that we are sure a lot of users are going to appreciate. I'll list some of them here:
- Integration with Visual Studio.
- Previously, the tool worked stand-alone, and the files generated had to be manually included in the project in VS. The current version works as a plugin, running within VS, and that way the files generated get immediately added to the project.
- Generation of more than one collection at a time.
- Previoulsy, one could only create one child collection and one xlink collection at a time. Now any number of collections can be added at a single pass.
- Creation of entities that aren't modeled from a SQL Server database.
- Previously, one could only generate entities based on a table/view in SQL Server. Now we support the creation of an entity from scratch, and underlying data for this entity is stored in XML files. This works great for prototyping modules and creating unit tests without having a need to create tables on the database, which means the developer can concentrate first on the classes to create, and only worry about how to persist that on the backend later. We'll have another post dedicated to this subject soon. It's also worth pointing out that the underlying engine on the generator has gone through a big refactoring to decouple it from SQL Server so to support the entities based on XML data sources. This means that now we can more easily work on adding support to create entities based on other backends such as Oracle, MySql, etc.
- Classes can be regenerated.
- Previously, if the developer needed to add things to a class (say, more properties to a business entity), she'd need to run the tool again, creating the entity as if it were the first time, and then manually move pieces of code from the newly generated files into the existing ones. Yup, painful. The new tool saves the definitions of the objects created, which allows for the tool to recreate the entities without forcing the developer to remember to recreate it as it was done before.
- Separation of class in individual files and partial classes.
- Previously the tool created one file for the business object class, and one file for the business entity class along with its collection and business item classes (collection items). The new tool creates a subfolder in the project, named after the business object (such as CustomerBusinessObject), and put the new files in that folder. Every class goes into its own separate file, and every class is also separated in two files (as a partial class): one for the code-generated content, and one for the custom code developers add to it. This means that the developer can regenerate code for business entities and objects without wiping out the custom code. Yeah, it was about time, right? ;)
The main point is: the new generator gives us everything we used to have, plus a LOT more, so this is definitely the way to go moving forward. There is only one thing we don't have on the new tool yet: it doesn't auto-update yet. Since it's a VS plug-in, we couldn't just use ClickOnce out of the box. So, if you're creating and working with business objects and entities, send me an email soliciting a copy of the plug-in, and I'll send it to you (of course, you have to be a Milos user to request it. My email is my first name at eps-software.com). I'll put you on my mailing list to receive updates, until we get the auto-update feature into the plug-in.
Also, the installation and usage of this plug-in has been documented in videos. I'll post a blog entry about this shortly. Make sure to check it out!
Posted @ 1:31 PM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Thursday, May 17, 2007
Data binding simplification
Milos controls support native data binding. It has been like that since the beginning of (Milos-) time. So for instance, if you want to bind a textbox to a property on a business entity, you can simply set up the binding by specifying the name of the entity and property you want to bind to, using the ControlSource property of the textbox.
This is pretty straightforward, but there is one somewhat tricky part to this: The member the control binds to is specified from the control's point of view. So for instance, to bind a textbox on a form to a business entity's "LastName" property (assuming the business entity is a member of the same form as is the case with the default Milos data entry forms), the ControlSource has to be set to "this.Parent.MyEntity.LastName". Or, one can also use the VB version: "Me.Parent.MyEntity.LastName". Both these versions are interchangeably and work regardless of the language you use. In fact, the "this/Me" part is completely optional. You could as well set the ControlSource to this: "Parent.MyEntity.LastName".
So far, so good. The tricky part is that if the textbox is inside another container, the hierarchy changes. For instance, if the textbox exists inside a tab page inside a tab control, the ControlSource needs to be set to this: "this.Parent.Parent.Parent.MyEntity.LastName". That is because the textbox exists inside the tab page (Parent), which exists inside a tab control (Parent.Parent), which exists inside the form (Parent.Parent.Parent), which has the entity (Parent.Parent.Parent.MyEntity).
It is perfectly logical, but it is also a common source of frustration, since it is a) easy to get the exact number of parent references wrong, and b) when the control moves into a different container, the path changes. We have now added a simplification to the expressions used by the ControlSource property. The approach described above is still perfectly valid, but the whole "parent" chain is now optional. So the ControlSource can now be set to "MyEntity.LastName", regardless of where the textbox (or any other control with a ControlSource property) lives.
The way this works is that Milos auto-discovers the hierarchy. It starts out with the current control. If it has a "MyEntity" member, then that is used for binding. If not, Milos goes to the parent control and looks for it there. It then continues up the chain until there is no parent anymore (typically at the window level). If no member of the specified name is found, a null reference exception is thrown.
So this is nice, because it simplifies things a lot. There are two downsides though: 1) This discovery is done at runtime and thus introduces runtime overhead. It is pretty fast, but if you have a form with hundreds of controls, you should probably consider specifying the full path (at least that is an optimization option in case you run into form startup performance issues). 2) The expression may be ambiguous. If you want to bind to the MainEntity on the form, but there happens to be another object in the chain that has a member of the same name, you may accidently bind to the wrong object. In those cases, one also has to specify the full path.
Some people say it is a bit odd to just bind to "MyEntity.LastName", because one cannot tell where MyEntity lives. To make things a bit clearer, you can use other pointers, such as "thisform", "class", or "container". They all mean the same exact thing, which is "diddly-squat". In other words: These keywords are ignored completely. But some think it is more readable to set the control source to "thisform.MyEntity.LastName", "Container.MyEntity.LastName", or "class.MyEntity.LastName". So go ahead and use these keywords if you feel they make things clearer. Be aware that they do not actually do anything. "thisform" for instance does not necessarily reference a form. It can also be used in panes for instance. It also does not solve the ambiguity problem, as the auto-discovery mechanism may still pick up "in between" object references from the hierarchical chain. (What do you think about this actually? I'd be interested in feedback on this...).
Anyway: Hopefully, this will simplify the task of specifying the control source expression.
Note: This feature has been implemented and has undergone almost complete testing, so it will most likely show up in the next Milos build...
Posted @ 11:48 AM by Egger, Markus (markus@code-magazine.com) - Comments
Saturday, May 05, 2007
Milos and O/R Mapping: Static and Dynamic Queries
Following the series about Milos ORM capabilities based on this article, up next is the support for Static and Dynamic Queries. Milos does support both.
Essentially, every business object class inherits an ExecuteQuery method. This method takes a Command object. The SQL statement can either be defined during compile-time, with only the parameters filled in at runtime, or any other part of the statement could be built on the fly (like the WHERE clause, or the ORDER BY clause). Below are some ways one could execute queries from within a Milos Business Object.
Simple Query
IDbCommand command = this.NewDbCommand("SELECT * FROM Customers");
DataSet results = this.ExecuteQuery(command);
Simple Query with Parameter
A few things to notice so far:
Helper methods for simple queries
For some simple queries, one can use some of the helper methods provided by the Milos Business Object. The benefit of using such methods is that we let Milos decided whether to build the SQL statement to be sent to the database, or to call Stored Procedures instead (depending on how the business object is set to process commands). That makes it also easier to swap between using direct SQL statements and stored procedures, or even swap between using different backends, such as SQL Server or MySQL. See some examples below:
Getting all records from a given table
If the business object is set to process commands using "IndividualCommands", Milos will create the CommandText as SELECT * FROM Customers. On the other hand, if the object is set to use "StoredProcedure", Milos creates a CommandText such as "milos_getCustomersAllRecords". The prefix ("milos_") is configurable, and the other parts of the name of the stored procedure follow a naming convention so that Milos knows what store procedure to look for (we have tools that generate those stored procedures for us!).
The QueryAllRecords method also allows for some customization of what gets retrieved:
Milos would create the following statement for the call above:
SELECT PK_Customer, CustomerName FROM Customers ORDER BY CustomerName
Of course, if the business object is set to use Stored Procedures, then the list of fields and ORDER BY clause on the method call are ignored, and the query in the stored procedure dictates the results.
Retrieving a Single Record
Milos translates the call above into this:
SELECT PK_Product, ProductName FROM Products WHERE PK_Product = 10
Retrieving Multiple Records by a given Key
The call above is translated into this:
SELECT CustomerName FROM Customers WHERE State = "TX"
This helper method is also great to get all the children rows for a specific parent, like this:
that translates into SELECT OrderDate FROM Orders WHERE FK_Customer = someGuid
On the next post, we'll talk a little more about Stored Procedures. Stay tuned!
Posted @ 11:37 AM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Wednesday, April 18, 2007
EPS CodeRush Custom Templates
I have personally be using CodeRush for about 2 years now, and I've been creating custom templates for some things that I do all the time, both for .NET in general and for Milos things. Since we're now getting other EPS developers to use CodeRush too, and I know there are many of our clients also using CodeRush and Milos, I'll be sharing here my custom templates. This is important for both beginner Milos users learning how to use the framework, as well as for advanced users who already know what to do and want to do it quickly.
You can download our custom templates here. There is a 6-minute training video on how to share templates among developers here. A brief how to goes here:
- In Visual Studio, select DevExpress on the menu, and then Options;
- Select the Editor -> Templates node on the tree;
- Right click the area where the templates are organized by folders;
- Select Import Templates...
- Navigate to the folder where you've unzipped the custom templates and pick the CSharp__EPSCustom.xml file.
- Click OK or Apply, and you're done!

Here's a list of some the templates we currently have, and what they expand to (I will not list all of them, since some I may still be tweaking or may end up droppng it from the list). Notice that everything in italic on the expansion indicates the pieces that the developer has to key in (the template will usually create stubs for it if possible):
Template: eq (as in Execute Query)
Usage: from with a method on a business object class, use this template when you need to execute a SQL statement against the database. If you already have a SQL statement into the clipboard, the template will paste it on the right place (as a parameter to the NewDbCommand method).
Expansion:
Template: eqp (as in Execute Query with Parameter)
Usage: same as "eq" template, except that this one will add the necessary call to add a parameter to the query.
Expansion:
Template: esp (as in Execute Stored Procedure), and espp (as in Execute Stored Procedure with Parameter)
Usage: same as "eq" and "eqp", but it expands to a call a Stored Procedure instead.
Template: ndbc (as in New DB Command)
Usage: within a method on a business object class, expands to the line necessary to a get new DB command using Milos (pastes content from the clipboard, such as s SQL statement into the code).
Expansion:
Template: acp (as in Add Command Parameter)
Usage: within a method on a business object class, expands to the line necessary to add a parameter to a command object.
Expansion:
Template: ubo (as in Using Business Object)
Usage: within a code block, expands to a using block, and all the developer has to do is to type the main name of the business object (such as Customer); the template will take care of properly naming the type and variable with the right casing.
Expansion:
Template: ube (as in Using Business Entity) and unbe (as in Using New Business Entity)
Usage: same as "ubo", except that it will create the using block wrapping a business entity. "ube" expands so that it calls LoadEntity (to load an existing row from the database), whereas "unbe" expands so that it calls NewEntity (to create a brand new row on the database).
Template: nbo (as in New Business Objects), nbe (as in New Business Entity), and lbe (as in Load Business Entity).
Usage: all these templates will declare a variable, name it, and instantiate it, depending on what main name for the class the developer types. NOTE: these templates should only be used in case the objects are not to be disposed by the method creating them. Favor using the "ubo", "ube", "unbe" templates instead, since the using block disposes the object automatically.
Expansion (respectively):
CustomerBusinessObject customerBO = CustomerBusinessObject.NewInstance();
CustomerBusinessEntity customerBE = CustomerBusinessEntity.NewEntity();
CustomerBusinessEntity customerBE = CustomerBusinessEntity.LoadEntity(pk);
Template: cbr (as in Class Business Rule)
Usage: expands to a class definition that inherits from the Milos BusinessRule class.
Expansion:
Posted @ 4:32 PM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Wednesday, April 18, 2007
Milos and O/R Mapping: Object Inheritance
As mentioned in the "Top 10 Must Have Features in O/R Mapping Tools" article, inheritance is one of the core concepts in object-oriented programming, and relational databases don't support such concept. Milos Business Objects do support means for that, though. This can happen in two ways:
- A business object (say ContactBusinessObject), and its MainEntity (main table) is set to "Contacts". A business entity is created to expose the main information such as the contact's name, address, phone numbers, etc. Subclasses of the ContactBusinessObject/Entity are created, and additional behavior is added to it. Both classes use the same table.
- Continuing with the "Contact" business object/entity example. Like on the first example, we make the Contacts table the MainEntity of the business object. Then we create a subclass, say "EmployeeBusinessObject" (and entity). An "employee" is-a "contact" (an employee has a name, addresses, phone numbers, etc.), but also has additional information (payroll info, job title, etc.). In the database, there is an Employees table, with a one-to-one relationship to the Contacts table. The EmployeeBusinessObject will have data from the Contacts table loaded (since that happens at the ContactBusinessObject), and it also loads the related data from the Employees table. The EmployeeBusinessEntity exposes properties that map to the columns off the Contacts table, as well as the ones off the Employees table.
In fact, Milos has a NameComponents module, which has a set of classes to be used for "names" (or contacts) manipulation (that includes business object, entity, and UI controls). That way, we don't have to recreate that functionality over and over again. We then subclass those business object/entity classes, to extend it into special classes such as Employee, Customer, and anything else that is-a specialized version of a contact in the application.
I'd like to mention that I'm keeping the posts on this series short, since my main focus at this point is to mention whether or not (and a little bit of how) Milos supports those "top 10 must have features"; when we're done with the series, I'll spend some more time into actually posting more details on each of those features. Stay tuned!
Posted @ 9:37 AM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments
Friday, April 13, 2007
Milos and O/R Mapping: Relationships and life cycle management
Following the series based on this article, this next installment covers how Business Objects module in Milos deals with Relationships and Life Cycle Management. So here we go (again, basing the contents off of the aforementioned article):
- One-to-One relationship: as I said in another post, we create business entity objects in Milos, and this objects usually have properties that map to column from a table, but, it can also have a property that exposes another entity that models another table. For instance, a database may have a Customers table, and a CustomerSensitiveData table (these tables have a one-to-one relationship). We could then create two business entities, one for each table, and each entity would have a property exposing the other one (i.e., the CustomerBusinessEntity class would have a property of type CustomerSensitiveDataBusinessEntity, and vice-versa, if needed). That way, people using a Customer object, could get to the sensitive data (if authorized to do so) by doing some like Customer.SensitiveData.SomeSecureInfo. By having both entities sharing the save DataContext, both loading and saving operations would be synchronized between the entities. Alternatively, instead of creating a separate entity for the sensitive data, we could just add properties to the Customer business entity that map to the columns in the sensitive data table. Therefore, the syntax from the previous example would be Customer.SomeSecureInfo. Even though the object looks like a single unit, internally it really manages a one-to-one relationship between two tables.
- Many-to-One relationship: I can't really think of a scenario where we've implemented this kind of relationship. For sure, we do a lot of "one-to-many" ones (covered next), but I really can't think of a many-to-one scenario. Well, let me know if you can think of one, and I'll check out how to implement it using Milos.
- One-to-Many: this scenario is very simple in Milos; we create, for instance, an OrderBusinessEntity, which maps to an "Orders" table, and then add an Items collection to the entity, mapping to the "OrderLineItems" table, in a one-to-many relationship. All the loading and saving is handled by the business entity/object. The developer instantiates an Order entity, add items to it through the collection, and when calling Save on the entity, both the data for the Orders and OrderLineItems tables get saved. Likewise, when the developer loads the entity based on an Order PK, both the order and its related line items are retrieved from the database. For the records, we call this kind of collection a "Child Collection" in Milos.
- Many-to-Many relationship: this scenario is also handled nicely in Milos. The bridge table glues the two main tables together. That table is also known as a joint table. In Milos, we call them cross link table, or just XLink for short. So let's think of a Product / Vendor relationship, where a Product can be offered by many Vendors, and one Vendor can offer many Products. One way of looking at this is from the Product's perspective (say we create a product, and associate a vendo with it). We'd define a ProductBusinessEntity, where the master table (or parent table, or MasterEntity, as we call it) is "Products", the XLink table is "ProductVendorsXLink", and the "Vendors" table we call the "target table" (since it is "the other end of the relationship"). Again, all the loading and saving is handled by the entity and business object. For the records, this kind of collection is called "XLink Collection" in Milos. Notice that the bridge (or XLink) table, can come in two flavors, listed below. Notice that Milos works with both case seamlessly:
- Bridge table with only primary key: this means that all the table has is the primary keys from both ends of the relationship (in the example mentioned, that'd be the PKs from both the Products and Vendors table, since that's what links the two together).
- Bridge table with additional columns: in this case, the bridge table would have more information about the relationship between the two ends. For instance, a "banana" product sold by "Mr. Banana Man" vendor would have one price, whereas the same product sold by a "King of Bananas" vendor could have a different price. The Price column would map to a Price property on the XLink collection, so from the developer's point of view, that value could be retrieved like so: SomeProduct.Vendors[0].Price. Notice that, if needed, we could bring some more info about the Vendor, such as the address, which could be accessed like SomeProduct.Vendors[0].Address (notice that from the object's perspective, it's very convenient to use, and internally, we still have everything persisted in a relational fashion).
For all these scenarios, we have code generator (that runs as a plug-in for Visual Studio) that facilitates a lot the creation of the objects, already configured to be used right away. In a couple of days I'll be posting about a new version of our code generator that's coming out soon.
Posted @ 9:33 AM by Lassala, Claudio (lassala@foxbrasil.com.br) - Comments