Friday, 04 January 2008
Genome has been developed with Team Foundation Server (TFS) for some time now, and it might be interesting to know in this context that TechTalk is a Visual Studio Inner Circle partner. TFS has proven to be a good source control system, but there are a few points that could do with a bit of improvement (particularly when compared to Subversion (SVN), with which I have extensive experience; that said, there are some features that both systems lack).
TFS
Friday, 04 January 2008 16:19:22 (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, 15 November 2007
As I am writing this, I am just heading home from TechEd, waiting at the Barcelona airport for my return flight to Vienna. It has been a busy time for the Genome team since September - unfortunately so busy that we couldn’t take time to blog about all the things that are going on. We weren't at TechEd only as attendees, but also exhibiting in the Visual Studio Partner area, with a total of 8 TechTalkers in Barcelona. To catch up with all the things that have happened since September, I’ll start with TechEd, while the memories are still fresh.
Thursday, 15 November 2007 20:02:27 (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, 08 November 2007
Typically, Genome is used to map tables to their data domain objects. But what to do when you have to use a database that is not made for mapping objects and therefore is not in any normalization form etc.?
Thursday, 08 November 2007 16:23:06 (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, 18 September 2007

While documenting/testing Genome 3.3 I stumbled about this strange behaviour, which seems to be a bug of the C# 3.0 beta 2 compiler.

I was trying to compile the following GROUP BY example with Genome:

var ordersPerCountryPerYear1 = from o in Helper.DB.Extent()
                               group o by new { o.Customer.Address.Country, o.OrderDate.Value.Year } into g
                               select new
                               {
                                 Country = g.Key.Country,
                                 Year = g.Key.Year,
                                 OrderCount = g.Count()
                               };

And received the following error from the compiler:

error CS1061: 'System.Linq.IGrouping' does not contain a definition for 'Count' and no extension method 'Count' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)

However, my team insisted that the extension method Count() is provided by Genome. To find out, why the compiler does not find it, they asked me to call it directly in Main():

TechTalk.Genome.Extensions.Linq.InternalIGroupingExtensions.Count(null);

After inserting this call in my code, the program suddenly compiled (including the statement, the C# compiler complained about previously).

We think this is a bug of the compiler. As a workaround I now have the following method on one class in my project to satisfy the compiler :-) :

static void ThisIsNeverCalled()
{
  TechTalk.Genome.Extensions.Linq.InternalIGroupingExtensions.Count(null);
}

Posted by Chris

Technorati Tags:

Tuesday, 18 September 2007 17:24:01 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [5]  | 
 Sunday, 16 September 2007

There are a lot of discussions how to properly mock an O/RM to achieve unit testing.

In this article, I will describe how an application implemented with Genome can be tested by means of unit testing. By stealing (and also modifying) the sample domain from Fowler, I’ll demonstrate this with a simple WebShop application. The use case that we are going to investigate is the ordering process itself.

The sample code used is provided for download at the end of the article. You need Genome 3.3 (beta 1) to execute the sample.

Sunday, 16 September 2007 15:25:25 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Saturday, 15 September 2007
TheServerSide.Net has just published our case study about DataReturn's experiences of using Genome to rebuild website for BMW USA. The site has quite critical performance requiremens of up to 1 million sessions per day.

Posted by Chris

Technorati Tags:

Saturday, 15 September 2007 10:19:10 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, 10 July 2007

Recently we received a question from a customer about how to express a sort criteria based on a condition. As the sort expression in Genome is nothing else than an implicit function (lambda) executed on the candidate element to return the value to sort by, this can be quite easily achieved.

As a simple example, let’s use the Northwind database again. A customer there has a Country and a Region property. Imagine you want to order all customers in the USA by Region and all the other customers by Country. Additionally you want to have USA customers first, then the others.

A Genome set can be sorted using the OrderBy() method. It takes an implicit function as a parameter, which is denoted using [] in OQL. The condition can be expressed using the ? operator, which is translated to a CASE WHEN in SQL. Hence, the order function can be expressed like this:

When the customer is located in the USA, then order by region; otherwise, order by country:

Country=="USA" ? Region : Country

To make sure customers located in the USA are listed first, I am prefixing the Region with a space (I know this is a hack, but it will do for this example). Hence, the final OQL looks like this:

extentof(Customer).OrderBy( [ Country == "USA" ? " " + Region : Country])

which translates to the following SQL:

SELECT … FROM Customers
  ORDER BY
    CASE WHEN (Country="USA")
       THEN (" " + Region)
       ELSE (Country)
    END ASC

Posted by Chris

Technorati Tags: ,

Genome | OQL
Tuesday, 10 July 2007 21:00:41 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, 04 July 2007
The Genome OQL Query Analyzer (QA) is a very helpful tool when you are developing with Genome: it allows you to load a mapped business layer to execute any kind of OQL queries. In this article, I demonstrate how the latest mapped business layer, along with its mapping, can be loaded by starting the Genome QA with a single click in Visual Studio’s solution explorer.
Genome | OQL
Wednesday, 04 July 2007 13:47:14 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, 29 June 2007

In my last post, I described how to use the Genome Web Application starter kit to rapidly create a web application that uses Genome.  To prove that Genome is not just for web applications, I will now show how to create a windows forms application with Genome.  There's no wizard that will create forms automatically (yet?), so there are a couple more manual steps.  As you will see, however, it's not too difficult.

I open visual studio, and reuse the datadomain and mapping projects I created in the first post in this series.  Then I add a new Windows Application project, call it WinForms and designate it as Start Project. I reference the datadomain and mapping projects, and the System.Configuration assembly.  I also reference the TechTalk.Genome assembly, which is located in C:\Program Files\TechTalk\Genome 3.2 Professional Edition for .NET 2.0\Bin\TechTalk.Genome.dll on my pc. I rename the Form1 files to MainForm. I add an App.config file, and paste the connection string from the web project that I created in my last post.  I then add a bindingsource to the form, and call it customersBindingSource.  I configure the bindingsource to use the Genome.DataDomain.Customer class from the DataDomain assembly (1). I add a DataGridView control to the form, dock it to fill the form, and have it use the customer bindingsource.  You can choose which columns you want to display in the grid; not all of them are that interesting to watch ;-)

So far, we used standard Windows Forms development techniques, but we still need to fill the datasource of our customer bindingsource.  This is where it gets more interesting.  We need a bit of plumbing code, but fortunately, we can use that most venerable of code reuse techniques: copy and paste.  I Open up the directory where Genome is installed (C:\Program Files\TechTalk\Genome 3.2 Professional Edition for .NET 2.0 on my pc).  I drill down to Samples -> CSharp -> QuickStart -> Step3-> WinFormsClient.  I Copy the files ContextBoundForm.cs, ContextFactory.cs  and Helper.cs to the directory of my WinForms project, and include them in the project.  I open the files and change the namespaces to WinForms. In Helper.cs, I change the name of the _schemaAssembly field to "01ReverseEngineering.Mapping" (which is the name of the mapping assembly compiled by the mappings project from my first blog post in this series.). I change the name of the connection string to "MyRole" (or whatever you defined as the name of the connection string in your app.config file). Now I open MainForm.cs in code view and make the class inherit from ContextBoundForm.

This code won't compile yet. The reason being that there's a using directive TechTalk.Genome.Extensions.Windows.Forms in ContextFactory.cs.  If you are familiar with the theory behind windows databinding, you will know that  the IBindingList interface is essential when binding lists of data to a datagrid or a listbox.  However, Genome's Set<T> (which is what is returned by any Genome query) does not implement that interface.  The Genome team provided a SetBindingList<T>, which is a wrapper around Set<T> that implements IBindingList.  You can find the code for this class in C:\Program Files\TechTalk\Genome 3.2 Professional Edition for .NET 2.0\Extensions\Windows.Forms\SetBindingList.cs. I add this file to the WinForms project (you can add it either as a copy or as a link). I compile to make sure that I have copied all code I need.

What is all this plumbing code about? One of the central concepts in Genome is the Context.  A idea of a Context in Genome is comparable to a connection to a database.  There can be many connections to the same database, but you need at least one connection in order to do something usefull on your database. The same is true for a Context.  The Genome equivalent of a database is called DataDomain, by the way.  ContextBoundForm inherits from System.Windows.Forms.Form and adds a propery and a couple of methods for using a Context.  ContextFactory does exactly what its name suggests.

Now at last we can populate the custerm binding source. I open MainForm.cs in design view, and double-click on the Form's title bar to create a handler for the load event. Thanks to all our plumbing code, I only need to write customersBindingSource.DataSource = new SetBindingList<Customer>(Helper.DB.Extent<Customer>(Context)); - after adding using directives for Genome.DataDomain and TechTalk.Genome.Extensions.Windows.Forms, that is.  I hit F5 to debug and run the application.  If all goes well, the main form loads and you will see the list of customers in the data grid.  In my case, not all went well at first and I had to make sure that my windows user account had appropriate permissions on the Northwind database.  The result looks something like this:

That's it for today.  This post turned out to be way longer than I anticipated, so I will save up the Orders and OrderDetails part and the editing part for the next post.

(1): If you have never worked with bindingsources in a windows forms application (and even if you have), I can highly recommend Brian Noyes's book Data Binding with Windows Forms 2.0 - Programming Smart Client Data Applications with .NET: it's full of information about working with data binding in Windows Forms, and is an enjoyable read.  If you are interested in the inner workings of data binding, be sure to check out chapter 7, Understanding Data-Binding Interfaces.

Posted by Dirk

Technorati Tags: object relational, getting started

Friday, 29 June 2007 10:59:29 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, 28 June 2007

Mats Helander, whom I have already had the pleasure to meet personally several times, wrote about an O/RM challenge in his blog.

While it is always fun to participate in challenges, I want to criticize the problem Mats describes first, before I show how you can solve it with Genome.

The challenge only concerns how efficiently an O/RM can read up a set of whole tables from the database. This does not make sense for two reasons:

  1. Usually, you don’t want to load up all the data from a database into memory (that’s one of the reasons why you use a database).
  2. If you have special cases, where you cache whole tables from a database (e.g. some lookup data), caching takes place very seldom (e.g. once a day) and thus the efficiency of loading up the data is not of such a big importance.

Mats expresses the challenge in such a way that he demands that the O/RM may not join the related objects when loading from the database to find out about their relationships. Instead, the O/RM should load up all objects at once, and “discover” the relations between the objects afterwards on its own (without using the JOIN from the database). This results in three SELECT statements (SELECT * FROM Customers; SELECT * FROM Orders; SELECT * FROM OrderLines).

An O/RM usually maintains only an identity map to cache object lookup queries. This helps object references mapped through foreign key fields in the database to be followed without extra database roundtrips (given that the related objects are already loaded into memory). This means that following an Order to its related Customer works in memory, if all data is loaded up. To discover the Orders belonging to the Customer, however, the O/RM needs to perform a lookup query.

Some O/RMs, including Genome, allow collections to be preloaded in order to avoid unnecessary roundtrips when traversing object graphs deeply. So you can tell the O/RM to preload all the Orders of the Customers retrieved, and to preload all the OrderLines of the Orders retrieved. In this case, the O/RM builds a map for relating the objects in memory while loading up the data.

Usually you only want to load up the related children of the parent table. It doesn’t make sense to load up all orders from the database only to fulfill the orders of three specific customers. To ensure this, an O/RM typically JOINs the related data to the filtered parent table.

Not filtering the parent table is a very special case. Introducing an optimisation for this case is possible, but would make no sense (for the initial reasoning above). Besides that, I wonder how large the loaded table has to be in order for that additional JOIN to make a significant difference, giving the whole performance optimisation sense at all. I guess in those cases, it is out of the question to cache the results in memory anyway, which is the premise of the scenario. Another drawback of this optimisation I want to point out is that it can turn out to be less efficient very quickly when the parent reference is nullable, as unnecessary data is loaded up again.

Still, this is a challenge and a lot of people interested in O/RM read it; so, let’s solve it with Genome.

Genome provides two infrastructures for retrieving and caching relations: collections and indexing.

The collection infrastructure provides rich support for handling specialised relation types such as 1:n and n:m relations. Usually, I would recommend using Genome’s collection mapping feature to support Mats’ scenario, except that Genome uses a JOIN to limit the related objects loaded up from the database.

Indexing is a Genome infrastructure that automatically detects even complex relationships, based on the loaded data. It is more complex to configure, use and maintain, but it can support Mats’ exotic scenario. Having mapped the business layer with Genome, the following three lines of code will do the trick:

using (Context.Push(LocalContext.Create()))
{
    IndexManager.FillIndex(Context.Current, dd.Extent<OrderDetail>(), 
                           IndexManager.GetIndex(dd.Schema, typeof(OrderDetail), "IdxOrder"));

    IndexManager.FillIndex(Context.Current, dd.Extent<Order>(),
                           IndexManager.GetIndex(dd.Schema, typeof(Order), "IdxCustomer"));

    Set<Customer> customers = dd.Extent<Customer>().ToArray();

   Dump(customers);

}

Inside the using block, the first two lines of code load up all OrderDetails and all Orders. Additionally, they saturate the indexes for the relationships Order->OrderDetail and Customer->Order. The third line of code loads up all customers. When Dump(customers) traverses through the object graph, all relationships are served from memory.

Note that this feature is not limited to simple 1:n and n:m relationships. It works for more complex relationships as well, such as retrieving pending orders of a customer etc.

Posted by Chris

Technorati Tags: object relational, challenge

Thursday, 28 June 2007 15:17:52 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |