![]() |
Platforms, Frameworks & Libraries »
LINQ »
General
Intermediate
License: The Code Project Open License (CPOL)
Signum Framework PrinciplesBy Olmo del CorralExplains the philosphy behind Signum Framework, an ORM with a full LINQ Provider that encourages an enties-first approach. |
VB 9.0, Windows, .NET 3.5, ASP.NET, WCF, XAML, WPF, LINQ, VS2008, Architect, Dev
|
||||||||||||
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Download SignumFramework_1.0_Setup.zip - 901.21 KB
Download SignumFramework_1.0_Src.zip - 407.68 KB
Signum Framework is a new open source framework for the development of entity-centric N-Layer applications. The core is Signum.Engine, an ORM with a full LINQ provider, which works nicely on both client-server (WPF & WCF) and web applications (ASP.Net MVC, still in progress).
I’ve developed this Framework and it’s hard for me to be objective. However, in this first tutorial, I’ll try to explain the principles that have guide us to design Signum Framework, the pros and cons of the solution and, in concrete terms, how your development experience will get improved if you start using it.
Excuse for the lack of a working source code in this article. I promise the next ones will be more practical, but in the first one I wanted to focus on the philosophy and the design principles.
Why learn a new ORM now, in 2009, with so many ORMs for .Net on the market? Isn’t everything already invented? Mmm…. Not really. We could categorize ORMs in three different groups:
|
DB-First: |
Flexible: |
Entities-First: Signum Framework fits into this category. |
The main idea behind Signum Framework is that you write the entities first (in C#) and then the database is automatically generated. This radical change means that you won’t be able to use your legacy database. But if you have the opportunity to work on a greenfield project, or you are brave enough to change your database, Signum Framework will make your life easier, let’s see why.
The most important principle that has influenced every single part of the design of Signum Framework is simplifying composability. There’s no reason why applications have to be so different and so hard to integrate, and this difference comes fundamentally from the database.
That’s why you can easily buy powerful Grids and Chart controls for any UI technology (WinForms, WPF, Asp.Net…) but you won’t find the tables and entities for your authorization, stock or payments modules, because nobody knows how to integrate them with your database.
What’s so fundamentally wrong about this idea of integrating vertical modules that deal with the UI, and the Database in your application? The answer is the lack of solid foundations.
Signum.Entities provides a small set of base classes (IdentifiablelEntity, EmbeddedEntity…) and primitives (MList<T>, Lazy<T>, Custom attributes…) to model your business entities in such a way that can be easily integrated with other modules. Just like Lego bricks: Small, reusable but not flexible at all.
Since these entities are just C# classes, and we follow an entities-first approach, Signum Framework is able to follow other nice principles:
Signum Framework encourages |
Signum Framework tries to avoid |
|
Clean and Simple code, by exposing static classes, and using the ScopePattern, Attributes… we try to create self explanatory code that does what is expected, removing the clutter. |
Complicated architectures that make your code base 300% larger and harder to understand in order to protect you from changes that will occur 10% of the time. |
|
Remove redundancy at any cost, because saying something once, and only once, is the best way of be prepared for changes. |
Code generation of your entities, making it hard to add behavior to your entities (like validation), keeping you without control of the most important part of the application. |
|
Compile-time checked C#, so common operations (adding, removing or renaming fields or classes) are less painful thanks to refactoring. That’s why we use LINQ queries instead of SQL, and we use strong typed reflection to configure the application. |
XML configurations, that are pure bloat, error-prone, not very expressive and have to be kept in sync (in case of the ORM mapping). You are building a solution over a framework, not configuring a framework to be your solution. |
|
Functional Programming, because lambdas, object initializers and LINQ queries, once understood, make your code shorter, easier to read and more homogeneous. |
Imperative programming, often you have to fallback to imperative programming, but if you have any chance to use declarative/functional programming, your code will be cleaner and with fewer bugs. |
POCO stands for Plain Old C# Object. An ORM with POCO support is one that can deal with classes that have no previous knowledge of the ORM. This is called Persistence Ignorance. It's explained here.
We know that POCO is a feature required by a lot of people 'disappointed' with the current LINQ Providers (no with LINQ per se), like DDD guys. Unfortunately POCO works badly with any other aspect of an ORM. For example, in order to avoid DataContext and make SOAP Serialization possible in a simple way, we embed change tracking inside of the entities. Our entities also have integrated support for validation by implementing IDataErrorInfo and implement INotifyPropertyChanged in order to simplify data binding (in WPF scenarios for example).
So, if the reason you like POCO is because it enables a clean separation of entities and logic, our architecture clearly promotes this: The typical architecture usually has two different assemblies and the entities only have to reference Signum.Entities, not Signum.Engine, where the DB stuff is placed.
Also, if you like POCO because your entities are clean and simple, soon or later you will need to implement IDataErrorInfo or INotifyPropertyChanged and the clutter will appear. In Signum Framework we remove all this clutter by moving it to the base class, keeping your entities simple even with validation.
However, if you like POCO because this way you can write an ORM-independent model, then you will be disappointed by Signum Framework on this point. Since changing the ORM is as common as having a heart transplant, and enabling pure POCO means that a lot of magic has to be added at runtime (i.e. lazy loading, that plays badly with Serialization), and a lot of external mapping has to added, increasing duplication, we choose not to support POCO.
But what's more important, Signum Framework imposes every single IdentifiableEntity (and Entity) to have an Id and a ToStr field. These fields also create columns in the database. So it's not only that Signum Framework imposes your classes to inherit from some certain base classes, it also imposes your tables to do it. That's why you need a new Database. In that sense Signum Framework has a lesser support to POCO than most of the frameworks.
These requirements, however, pay for themselves because they enable huge levels of code re-utilization:
Think of System.Object: Thanks to having ToString, GetHashCode and GetType on every single object then you can add them to a List, a Dictionary, a PropertyGrid or a ComboBox... all these things are much more complex in a language like C++.
Thanks to having a solid foundation for our entities, with Id and ToStr, we can use Lazy<T> to solve lazy loading in a neat and powerful way, or EntityControls that simplify user interface removing almost all code behind for typical scenarios. These requirements also enable the integration of application modules. The most important feature of Signum Framework.
In order to remove duplication, the db schema is automatically generated from your entities using a simple 1-1 mapping from entities to tables:
In the early stages of your application development, when changes are more likely to happen, you can create a new database each time using Administrator class. When the application is on production, and you can’t afford to loose the data, you can generate a synchronization script that will modify the schema according to the changes in your entity model.
Signum.Entities base class ModifiableEntity (from where EmbeddedEntity and IdentifiableEntity inherit) adds validation support by implementing IDataErrorInfo.
There are two ways of implementing validation logic in your entities:
These validations are checked before saving an object, avoiding corruption in the database, and also in the WPF user interface (ASP.Net MVC coming soon) in a very responsive way.
You could also use database-wide validations by subscribing to Signum.Engine events (kind of triggers) in the next version.
Change Tracking is embedded inside of the entities, so there’s no dependency on a global context of the ORM, simplifying serialization and making it easy to send the entities to the client side of the application.
Since the relational databases have no concept of inheritance, one of the most important features of an ORM is the way it handles inheritance.
For a hierarchy like this:
public abstract class PersonDN : Entity
{
string name;
//(..)
}
public class SoldierDN : PersonDN
{
WeaponDN weapon;
//(..)
}
public class TeacherDN : PersonDN
{
BookDN book;
//(..)
}
In NHibernate there are three ways of implementing inheritance in a db:
When we designed inheritances in our framework we went just for the third way because it is the simplest.
In order to enable inheritances scenarios we move the responsibility to the FKs. We have two kinds of Polymorphic-FKs:
However, the main reason for going for the Polymorphic FK solution is that all these complex solutions solve only the problem of saving entity hierarchies, while PFK also allows referencing to ‘potentially every entity’ (using ImplementedByAll), very useful for general entities like Notes and Documents that can be attached to any other entity. PFKs also simplify overriding this information at runtime, making it easy to modify entities out of your control and, ultimately, empowering composability.
Every ORM has to deal with laziness in some way. Many (Linq to SQL or NHibernate for example) do this in a ‘transparent’ way. The problem then is that then you create a hidden dependency from your model to the ORM.
Signum Framework is honest about laziness by using Lazy<T>. Whenever you need to point to a Person, but you don’t want to load it each time, you use Lazy<Person> instead. This object just knows about the Type, the Id and the ToStr, of the entity it’s pointing to.
Lazy<T> is integrated in every tier of Signum Framework: In the UI (EntityControls), the engine itself (Extension methods to retrieve Lazy, and to convert an IdentifiableEntity into a Lazy) and the Linq Provider.
Signum Framework explores a graph of entities looking for cycles before saving. If a cycle is found, it creates the connection in a second phase, so you can save complex graphs of objects at once without worrying about the cycles.
Our model for transactions is based on System.Transaction.TransactionScope, but makes every nested transaction silent by default. This is convenient in order to make the business logic easily composable.
Signum Engine is the powerfull ORM in the code of Signum Framework. But Signum Framework is not just an ORM, but a full framework that helps you in making composable applications in every layer of your application. Signum.Windows is the assembly that radically simplifies building a WPF UI for your entities.
Since all the entities implement INotifyPropertyChanfe and IDataErrorInfo, data binding and validation comes for free on the user interface.
Also, by having Id and ToStr in every single entity we can make EntityControls: Controls that can deal with any kind of entity so, when used for an specific type, they know how to retrieve a list of entities of this type (EntityCombo), enable AutoCompletion (EntityLine)… without writing any line of code-behind when default implementation is enough. If that’s not the case, events are exposed in order to modify the behavior.
Also, Signum.Windows encourages a declarative way of writing the UI, using UserControls instead of Windows. It also exposes a centralized way for Navigation, Security, etc… Which simplifies composability in the user interface.
Signum.Services is a very simple assembly that defines the WCF contracts between a WPF client application and the server. We break the rules again here, because we use NetDataContract instead of DataContract, that enables sharing types (instead of shared contract) between the two sides in order to share the entities.
We want to place the validation in the entities just once, and we are targeting scenarios where you have the control of both the server and the client application, so we have rejected the general SOA-Architecture, with DataContracts on every entity and a big WSDL between the client and the server, because we don’t need both sides (client and server) to evolve independently.
Writing a full LINQ provider for a complex language like SQL is not an easy task. Many of them have tried but very few projects have achieved a proper one.
Many of the LINQ providers out there only implement a small subset of the LINQ operations, sometimes this is ok because the target language of the translation doesn’t support some given operation. But when the target is SQL, often they are just ‘pretending’ that they have a full Linq provider when they don’t. Take note of the following:
Frans Bouma found the same problem when writing the LINQ Provider for LLBLGen PRO, and in order to “sift the wheat from the chaff” he posted a test for LINQ Providers to pass.
Signum Framework exposes the LINQ provider in the Database.Query<T> method. This is how we pass the exam:
We’ve found this test pretty complete, spotting many of the complex things that need to be solved by a proper LINQ provider in order to be useful. We would like to add some more that are also available in Linq to Signum
Finally, there are some handy features of Linq to Signum that need to be mentioned, but that are not applicable to other ORMs, like Retriever Integration (the ability to retrieve full entities when they are part of the result, handy but, of course, slower), or retrieving Lazy objects by referencing a column of type Lazy<T> or by calling ToLazy() on a complete entity.
Signum Framework is a different animal. It uses the latest technologies from Microsoft (LINQ, WCF, WPF) but it’s brave enough to say NO to some of the of the current ‘establishment’ principles, like POCO, SOA, and complex XML configurations.
As many, the whole framework tries to leverage the developer from technical details, so it can be focused on your business entities and logic. But also it provides the solid foundations to make future application composable (from the database to the user interface) in the form that very few primitives used to model business entities.
In the near future, the roadmap of Signum Software is to release Signum.Web, (applying the ideas behind Signum.Windows to ASP.Net MVC) and providing some general purpose modules to simplify common tasks like Authorization, Operations…
As time goes on, we expect third party developers selling business modules like Payements, Stock management, CRM, HR, Payroll, Task, To-Do list… easily to integrate in their application, and this is something that won’t happen with more flexible ORMs.
Who wants to be the first?
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 7 Apr 2009 Editor: Sean Ewington |
Copyright 2009 by Olmo del Corral Everything else Copyright © CodeProject, 1999-2009 Web09 | Advertise on the Code Project |