EurekaFach .Net is a Windows desktop application used in specialised courts in Germany (primarily Arbeitsgerichte, Finanzgerichte, Sozialgerichte und Verwaltungsgerichte) to assist judges and judicial specialists responsible for the procedural management of lawsuits and other court procedures.
One of the many features of EurekaFach is keeping track of the current status of lawsuits, managing dates, appointments, and addresses, organizing all the documents involved in a case, calculating costs for procedural aids and compensation for honorary judges, and providing a comprehensive "Schreibwerk" to generate decrees, summons and court-related correspondence using parameterized text building blocks, Microsoft Word templates, macros, and XLST.
What EurekaFach does not do is replace a judge by rendering judgements.
As of April 2022, EurekaFach .Net is used by 250 courts in 14 of the 16 German federal states, serving approximately 12,000 users.
EurekaFach .Net is the .Net version of the original EurekaFach, which was developed starting in 1998 and released with its first official version in 1999.
A brief history of the application:
- The original EurekaFach was developed in 1998 using VO, bBrowser, VO2Ado, and ReportPro, with the first version released in 1999.
- Over the years, the user base grew rapidly.
- The development team consists of six developers.
- Migration to .Net started in 2018 and was completed in 2020.
- EurekaFach .Net ist based on .NET Framework 4.62 and uses X# 2.11.
- Third-party components used include DevExpress WinFormsUI Components, Office File API, Tx TextControl, Codejock, OracleManagedDataAccess, and Devart PostgreSQL.
- Supported Databases: MS SQL Server, Oracle, and PostGreSQL.
- All development is done in Visual Studio.
Why did specialised German courts need a Windows Application in 1998?
- It was already decided that the existing Unix system would be replaced by Windows PCs.
- There was a general demand for new software that would improve the efficiency and user-friendliness of handling court procedures.
- Changing laws and requirements created a need for more flexible software.
- And last but not least, there was the imminent Y2K problem (anybody remember ;) )
Using Visual Objects was an obvious choice at the time and proved to be the right decision over the years.
The first official version of EurekaFach was released in the 1998. Remarkably, 20 years later, this version is still in use at some courts, although these installations are gradually being replaced by the .Net version.
Fast forward to 2018, the development of EurekaFach .Net began as a migration project and was completed two years later.
Fig: 1: EurekaFach .Net shows (faked) core details of all legal documents currently in the database
Since Visual Objects was no longer developed at this time, X#, being a VO compatible language that was based on the .Net runtime, was an obvious choice for several reasons:
- One reason for the trust in X# was, besides the highly experienced development team with Robert, Chris and others, the fact that X# is based on the Roslyn compiler platform. This means that the X# compiler uses the same code generator that Microsoft's own languages C# and Visual Basic use. Therefore, one could assume that all the language features of C# would also be available with X# sooner or later (most of the time sooner).
- Although migrating a large VO application developed in the 1990s to a new platform 20 years later might seem like the perfect opportunity to redesign the application's architecture, it was decided to keep the 2-tier design with a GUI frontend and a database server backend. This meant no middleware, no MVC or MVVM, and no dependency injection and other design patterns to think (and worry) about.
By keeping the same old architecture for the new application, the migration project could be finished on time and within budget. Otherwise, the migration project would have turned into a complete new project with the likely probability of not ending with a happy end.
Nevertheless, there were still a couple of important decesions that had to be made:
- How to access the database? Since VO2Ado for .Net was not ready when the migration started, an alternative was needed.
- How to deal with the completely offline nature of database access in .Net?
- How to keep the datalayer agnostic to a certain DBMS?
- WinForms or WPF for the GUI?
- Keep the VO runtime functions, or replace them with .Net runtime functions if possible?
- How to replace VO2Ado, bBrowser and Report Pro?
Long story short, all these questions were answered in a good way. The following short descriptions of the answers to each challenge should help other developers in the community in their decision process if they are about to migrate a large VO application to .Net.
.NET classes instead of VO2Ado
Since using VO2Ado for database access was not an option, the entire database layer (DAL for short) was written from scratch, and put in a separate "DAL classes", as well as in a "DAL project" used by several other projects too. This was not nearly as much work as it might sound. The DAL classes consist of several methods that, for example, open a connection to a database, run select queries that return either a DataReader, a DataTable, or a single value, or executen a SQL command that updates the database.
All databinding with the UI components is done through the datasource property of each component. The datasource property is assigned either a DataTable or a BindingSource component, which has its own datasource property.
Query and forget - keep the data in the database
One of the major differences, if not the major difference, between the old datacentric world of VO and the world of the .Net Framework is the completely offline nature of the latter. This means that once data has been queried by a Select statement through a DataReader or a DataAdapter, there is no longer a connection to the database. Query and forget.
This is not a problem if the data is only used in a read-only or lookup scenario. However, it can become challenging if the data is updated inside the UI, for example in a data grid, and needs to be written back to the database.
Writing the data back is just a matter of generating the necessary Insert-, Update-, and Delete-statements every time, which is easy to solve. With a few exceptions, EurekaFach .Net completely relies on SQL statements as strings. Parameters are included in the SQL string either by string concatenation, or with the help of the new string interpolation feature of X#, which is very convenient. Another great feature of the recent X# compiler versions, is how easy it has become to include aliases inside a SQL query (see Listing 1).
sqlText := "Select Nummer ""Nummer"",Bez ""Bez"",AzStamm.Buchstabe ""Buchstabe"","
sqlText += "Maske ""Maske"",Zaehlkarte ""Zaehlkarte"", Verteilart ""Verteilart"","
sqlText += "Turnus ""Turnus"",Verfart ""Verfart"" "
Self:taAzStamm := DbFunctions:InvokeSelect(sqlText)
Self:bsAzStamm := BindingSource{}
Self:bsAzStamm:DataSource := Self:taAzStamm
Self:gridControl1:DataSource := Self:bsAzStamm
Listing 1: SQL Select query and databinding to a Grid Control
However, it can get complicated when data that should be updated with a SQL update statement has been modified by another user in the meantime. Executing the update command would overwrite the other user's changes since the last quiery. Usually, this is not the behavior users want.
The way .Net handle this situation is called optimistic concurrency, which is explained in detail in the official ADO.Net documentation and in countless articles online. Essentially, for each field, both the old and new values are included in the Where clause of the update statement. If another user has modified the data, the Where clause will fail because the old value no longer matches the current value in the database. As a result, the update statement will fail with a DBConcurrencyException, leaving the data in the database is unchanged.
This approach shifts the problem back to the developer, who must handle the exception and decide whether the value in the database should be kept or overwritten.
To make another long story short, EurekaFach .Net uses a simple solution for this problem: it assumes that such situation will not occur, as any changes made in the UI are immediately written back to the database, ensuring that the last update always wins. While this might not be the ideal solution for every case, it has proven to be reliable in the years EurekaFach .Net has been in use.
One set of classes to rule them all
One of the main requirements of EurekaFach .Net was that it had to remain DMBS-agnostic, likes its predecessor. This was achieved by using the classes in the namespace System.Data.Common. These classes use a factory pattern to provide provider-agnostic database access. The only requirement is that the database provider must be configured in either the application's config file or the machine.config file of the .Net runtime (see Listing 2).
var adapter := EFDbFactory:CreateDataAdapter()
adapter:selectCommand := (DbCommand)selectCommand
adapter:selectCommand := sqlText
var ta := DataTable{}
adapter.fill(ta)
Listing 2: Filling a data table with a query result by using provider independent classes
Using the classes in System.Data.Common does not mean the .Net runtime handles DMBS-specific differences. This remains the responsibility of the developer. In EurekaFach .Net, there were two occasions that required provider-specific adjustments: when fetching long values from an Oracle database, and explicit type conversion between bool and int types for a PostgreSQL database. Everything else works perfectly.
Another issue worth mentioning briefly is the handling of open connections. Although connection pooling is active by default, in some occasions the number of open connections kept increasing. This is not a major problem for SQL Server, but Oracle and PostreSQL are stricter. The root cause of this "leak" were datareaders that where not used inside a Begin Using/End Using block (the recommend way). The solution was to explicitly close the datareader as soon as it was no longer needed. While simple in theory, this can be challenging in a class where a "global" datareader is used across several methods. A better practice is to use datareaders only locally and use a datatable as a "global" record store when there is no better alternative, because the .Net runtime takes care of the connections.
WinForms, of course
WinForms is the short term for the classes in the namespaces System.Windows.Forms and System.Drawing. All the graphic output is based on GDI+ which is the Windows graphic library developed by Microsoft and an enhancement of the basic GDI-API of the Win32 API.
Although the Windows Presentation Foundation (WPF) was already a part of the .Net Framework when the migration project started, it was never considered as an option for several reasons:
- WPF's improvements, like an XML-based UI definition, advanced databinding capabilities, and animations, would not have benefited this application.
- WinForms is solid, fast, and feature-rich, with excellent support from third-party component vendors.
- The WinForms application model more closely resembles the familiar VO model than WPF does.
- Learning WPF would have required significant effort with little benefit to the project.
Excurse - the power of modern third party GUI components
EurekaFach already used third-party components like the CodeJock UI components for the ribbon and the Tx Text Control for rich text editing, with all the formatting options that users are already used to. For the EurekaFach .Net project, the decision was made to use the DevExpress WinForms UI components. While other companies, like ActiPro, ComponentOne, Infragistics, SyncFusion, Telerik and others, offer similar high-quality, DevExpress stood out for the rich functionality of its UI components, especially the GridControl, and its outstanding support.
Replacing bBrowser with the DevExpress GridControl
The advanved capabilities of the GridControl eliminated the need to replace bBrowser (or ReportPro). Customizing a grid with filters and conditional formatting, combined with print previews and advanced printing capabilities, can be activated and customized with very little coding.
Fig 2: The print preview of the Grid Control offers the same printing capabilities as a report designer
Fig 3: The print preview also offers a document export functionality
Fig 4: Customizing the Grid Control with formatting rules based on cell values
After all these years, is still amazing how easy it has become for developers to implement functions that were once exclusive to big office applications like Microsoft Excel and Word. In EurekaFach .Net, users can customize query filters inside the grid control, highlight filtered rows with custom formatting, and automatically save these settings for future use. Implementing such features through coding would have taken months in the past, if it had been possible at all.
VO runtime or .Net?
The old VO runtime functions can be used directly within an X# project when the project dialect is "Visual Objects". There is no need to replace an AllTrim() function with a trim()-method, an Upper()-function with a toUpper()-method or a CTod function with Datetime.Parse(). In fact, deliberately replacing an Instr() function with a substring()-method for the sake of modernization can be risky, as they they work differently despite appearing similar. Some lessons can only be learned the hard way...
Fig. 5: For the dialect, "Visual Objects" was the obvious choice
While it is recommended to use .Net runtime functions for new functionality, all the VO runtime functions can still be used without any changes This is also true for the immediate window during debugging, with two notable exceptions: the colon (:) has to be replaced with a dot, and the index for arrays and lists always starts at 0. Additionally, X# syntax is case-sensitive😉 so it's best to avoid using different cases for variable names.
Fig 6: The immediate window prefers C# syntax
Although, for performance reasons, it is recommended to use generic Lists (like List<T>, where T stands for a type), rather than arrays, the VO arrays can still be directly used in X#. Modern language features of C#, like LINQ queries or anonymous types, are fully supported by X# but the classic AScan()-function remains a powerful query function.
Seperation of concerns
As noted earlier, EurekaFach .Net, like its predecessor, is based on a classic 2-tier application model. However, this does not mean the application is a monolythic block. First, the Visual Studio solution consists of several projects that can be used as building blocks. Second, and more importantly, EurekaFach uses a strict separation between UI classes and classes that implement the logic behind a window or dialog. All window and dialog classes are implemented in a C# project, and the window/dialog classes in the main X# project inherit from these C# classes. While the C# classes only contain class definitions and the code created by the WinForms designer, the X# classes contain all other code, including event handlers.
The most important reason for this "separation of concerns" was that the WinForms designer in C# was significantly better than the one in X# when the migration project started.
A few metrics about the project
Since Visual Studio's code metrics analyzer does not work with X# projects, we had to either use a tool like NDepend or write a custom script to gather "total lines for codes" metrics. A PowerShell script was used to analyze the source code by counting PRG files, class definitions, and lines that were not comments.
Metric |
Value |
Projects |
18 |
PRG files |
455 |
C# files |
65 |
Class definitions |
588 |
Lines of code (excluding comments) |
160278 |
Windows and dialog boxes |
165 |
Database tables |
268 |
Table 1: A few (estimated) metrics for EurekaFach .Net
Summary
The migration of EurekaFach to EurekaFach .Net was a success on many levels, being completed on time and within budget. Several key factors contributed to this success:
- The rock-solid and fast X# compiler, along with the reliable VO runtime functions.
- The highly effective X# extension for Visual Studio (though there is room for improvement in the code editor).
- The outstanding support from the X# team (thanks to Robert, Chris and other X# developers).
- The decision to keep the development project as a pure migration project, rather than changing for example the architecture as well.
- Using WinForms instead of WPF, as this kept the migration of the VO code simple.
- The fact that EurekaFach has been a stable application with a rich functionality that had been in use by thousands of users for many years.
- The outstanding functionality of the DevExpress UI components, especially the Grid Control, which offers users great convenience and makes the developer's life really joyful :)
We hope these insights are helpful to other developers who are considering migrating large VO applications that may be nearing their "end of life".
We would be happy to answer questions about the EurekaFach migration project. Please contact us through info@eureka-fach.de.