Click here to Skip to main content
Click here to Skip to main content

Signum Framework Tutorials Part 1 – Southwind Entities

By , 14 Nov 2012
Rate this:
Please Sign up or sign in to vote.

Signum Framework Tutorials 

Content

Introduction

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).

Signum Framework focus is to simplify the creation of composable vertical modules that can be used in many applications, and promotes clean and simple code via encouraging functional programming and the scope pattern.

If you want to know more about what makes Signum Framework different, take a look to the previous tutorial

About Signum Framework 2.0 

We are pleased to announce that finally we have released Signum Framework 2.0. 

The release took more than expected but, as a result, is much more ambitious. We have been using our framework internally in a daily basis and we think that this release is finished and will glad everyone brave enough to use it.

The new release is focused in different trends:

  • Signum.Web: Based in ASP.Net MVC 3.0 and Razor, tries to keep the same feeling and productivity of Signum.Windows without constraining the possibilities of the web (jQuery, Ajax, control of the markup, friendly urls…)
  • Keeping up to date with technology: The framework now runs only on .Net 4.0/ ASP.Net MVC 3.0 and the snippets and templates target Visual Studio 2010.
  • Improving pretty much everything and fixing bugs: For a complete list look at the change log http://www.signumframework.com/ChangeLog2.0.ashx

About this series

In order to show the capabilities of the framework, and have a good understanding of the architecture, we’re preparing a series of tutorials in which we will work on a stable application: Southwind.

Southwind is the Signum version of Northwind, the well-known example database provided with Microsoft SQL Server.

In this series of tutorials we will create the whole application, including the entities, business logic, windows (WPF) and web (MVC) user interface, data loading and any other aspect worth to explain.

If you want to know more about the principles of Signum framework look at the previous tutorial

Time to get our hands dirty with the entities:

Instalation 

Signum framework is Open Source (LGPL) and can be downloaded for free in codeplex , the source code is also available in github

The installer will copy the following:

  • Assemblies: {Program files}\Signum Software\Signum Framework 2.0
  • Code Snippets: {Documents}\Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets
  • Project template: {Documents}\Visual Studio 2010\Templates\ProjectTemplates\Visual C#
  • WPF Control Template: {Documents}\Visual Studio 2010\Templates\ItemTemplates\Visual C#
  • ASP.Net MVC 3 Razor Control Template: {Program Files}\Microsoft Visual Studio 10.0\Common7\IDE\ ItemTemplates\CSharp\Web\MVC 3\CodeTemplates\AddView\CSHTML

Once installed, open Visual Studio 2010, and create a new project. Under Visual C#/.Net Framework 4 there’s a new Signum Framework 2.0 Client – Server template. Create a new project called Southwind and a solution with 5 projects should be created:

  • Southwind.Entities
  • Southwind.Load
  • Southwind.Logic
  • Southwind.Web
  • Southwind.Windows

Unload everything but Southwind.Entities for now, and open MyEntity.cs

Creating the entities

Signum Framework promotes a code-first approach and the entities you write have a straightforward mapping to database tables. Also, since 100% of the SQL queries are produced by the framework, including schema modification, you almost forget about SQL Management Studio.

For teaching, however, will be easier to start by showing what we are trying to accomplish in a familiar diagram. Here is Northwind database:

Out first entity: Region

Let’s start simple. In order to create the entity for Region we need to inherit from IdentifiableEntity class and create the description field.

We will call it Description, not RegionDescription, since this redundancy makes sense only to simplify writing SQL manually (not the case).

Also, we don’t have to worry about RegionID, since every IdentifiableEntity already has Id and ToStr field/property. 

We already have the snippets installed so just remove MyEntityDN class and press:

entityWithName [Tab] [Tab] Region [Tab] description [Tab] Description [Enter]
    

After this, we should have something like this:

[Serializable]
public class RegionDN : Entity
{
    [NotNullable, SqlDbType(Size = 100), UniqueIndex]
    string description;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 100)]
    public string Description
    {
        get { return description; }
        set { SetToStr(ref description, value, () => Description); }
    }

    public override string ToString()
    {
        return description;
    }
}
    

This is our entity class, let's analyze: 

The class

The class is Serializable, so you can send it though a web service or save it in a file.

Also, inherits from Entity to enables concurrency support. Since this entity will be changed once in a while by admins, we can change to IdentifiableEntity and save the inherited Tick column. 

Finally, notice that we write the name of our entities in singular, as well as the name of the table. 

The field 

There’s a description field. In Signum Framework entities, every field will generate a database column.

The column will try to match the CLR type whenever possible so in the case of strings it will be nullable and, by default, a length of 200 characters.

The attributes over the field override some of this defaults, in this case makes the column not nullable and with a length of 100.

Just by placing UniqueIndex attribute, an index will be created over the column.

The property

Every property gives access to the underlying field for the user interface and business logic.

In order to user the property in queries, there have to be a field with the same name but lowercase. Unfortunately VS snippets are not smart enough so you have to repeat the name twice.

By decorating the properties with some ValidationAttributes we enforce simple validation rules over the entities. More flexible validation options are available and the attributes can be overridden. 

Also we can decorate our properties with other annotations to change the display name (DescriptionAttribute), the format of numbers or dates (FormatAttribute), the unit of the value (UnitAttribute), etc…

Finally, we can see that the getter of the property is trivial, but the setter calls a protected Set method. This method enables change tracking does the following:

  • Set the value of the field to the new value
  • Marks the entity as modified (dirty) so is not skipped when saving
  • Fires PropertyChanged event so the user interface (if any) gets updated
  • Returns true if there was a change, false if the value was the same

Entity with foreign key: Territory

Let’s continue now with Territory:

entityWithName [Tab] [Tab] Territory [Tab] description [Tab] Description [Enter]

We also change the base class to IdentifiableEntity, so TerritoryID comes for free but we have to create the RegionID column and the foreign key. Quite simple, just create a property of type Region:

field [Tab] [Tab] RegionDN [Tab] region [Tab] Region [Enter] 

Territory property is mandatory, so let's add a NotNullValidator over the property. The result should be something like this:

[Serializable]
public class TerritoryDN : Entity
{
    RegionDN region;
    [NotNullValidator]
    public RegionDN Region
    {
        get { return region; }
        set { Set(ref region, value, () => Region); }
    }

    [NotNullable, SqlDbType(Size = 100), UniqueIndex]
    string description;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 100)]
    public string Description
    {
        get { return description; }
        set { SetToStr(ref description, value, () => Description); }
    }

    public override string ToString()
    {
        return description;
    }
}

Since RegionDN is an IdentifiableEntity, the framework already knows that has to create an idRegion column and a foreign key for you. There's no easy way to access this underlying column (apart from doing territory.Region.Id) but dealing with id's is unnecessary and error prone.  For Signum Framework, foreign keys are an implementation detail.  

The next step should be the EmployeeTerritories relational table, but this table is not an ‘Entity’ but a Many-to-Many relationship between Employees and Territories. In Signum Framework this is represented by a MList<TerritoryDN> field on Employee entity.

Before getting into Employee table, we can see that some fields (Address, City, Region, PostalCode and Country) are also repeated in Customers, Orders and Supplier tables. 

EmbeddedEntity: Address 

In order to create an address entity that ‘belongs’ to the parent table, instead of having their own, we just have to make it inherit from EmbeddedEntity:

entity [Tab] [Tab] Address [Enter]
fieldString [Tab] [Tab] address [Tab] Address [Enter] 
fieldString [Tab] [Tab] city [Tab] City [Enter] 
fieldString [Tab] [Tab] region [Tab] Region [Enter] 
fieldString [Tab] [Tab] postalCode [Tab] PostalCode [Enter] 
fieldString [Tab] [Tab] county [Tab] Country [Enter]

Now let’s change the base class to EmbeddedEntity and make some changes to the size of the fields (and the corresponding validators) to mimic the Northwind database.

[Serializable]
public class AddressDN : EmbeddedEntity
{
    [NotNullable, SqlDbType(Size = 60)]
    string address;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 60)]
    public string Address
    {
        get { return address; }
        set { Set(ref address, value, () => Address); }
    }

    [NotNullable, SqlDbType(Size = 15)]
    string city;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 15)]
    public string City
    {
        get { return city; }
        set { Set(ref city, value, () => City); }
    }

    [NotNullable, SqlDbType(Size = 15)]
    string region;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 15)]
    public string Region
    {
        get { return region; }
        set { Set(ref region, value, () => Region); }
    }

    [NotNullable, SqlDbType(Size = 10)]
    string postalCode;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 10)]
    public string PostalCode
    {
        get { return postalCode; }
        set { Set(ref postalCode, value, () => PostalCode); }
    }

    [NotNullable, SqlDbType(Size = 15)]
    string country;
    [StringLengthValidator(AllowNulls = false, Min = 3, Max = 15)]
    public string Country
    {
        get { return country; }
        set { Set(ref country, value, () => Country); }
    }
}

Getting big: Employee

Now things get interesting, Employee it’s one of the biggest tables on Northwind and has some new aspects:

  • An Address field, of type AddressDN, that contains the fields of our new EmbeddedEntity.
  • An MList that will create the relational table.
  • A relationship to itself to represent the Employee hierarchy.
  • A bunch of new value types like DateTime, DateTime? (nullable), byte[] for the photo, and a infinite length string (notes).

We should have the basis of the entity written in a few seconds with the following key strokes:

entity [Tab] [Tab] Employee [Enter]
fieldString [Tab] [Tab] lastName [Tab] LastName[Enter] 
fieldString [Tab] [Tab] firstName [Tab] FirstNaame [Enter] 
fieldString [Tab] [Tab] title [Tab] Title[Enter] 
fieldString [Tab] [Tab] titleOfCourtesy [Tab] TitleOfCourtesy [Enter] 
field [Tab] [Tab] DateTime? [Tab] birthDate [Tab] BirthDate [Enter]
field [Tab] [Tab] DateTime? [Tab] hireDate [Tab] HireDate [Enter]
field [Tab] [Tab] AddressDN [Tab] address [Tab] Address [Enter]
fieldString [Tab] [Tab] homePhone [Tab] HomePhone [Enter] 
fieldString [Tab] [Tab] extension [Tab] Extension [Enter] 
field [Tab] [Tab] byte[] [Tab] photo [Tab] Photo [Enter]
fieldString [Tab] [Tab] notes [Tab] Notes [Enter] 
field [Tab] [Tab] Lite<EmployeeDN> [Tab] reportsTo [Tab] ReportsTo [Enter]
fieldString [Tab] [Tab] photoPath [Tab] PhotoPath [Enter] 
field [Tab] [Tab] MList<TerritoryDN> [Tab] territories [Tab] Territories [Enter]

Now, this time inheriting from Entity is all right.

String fields  

Let’s manually set the size of all the string fields, and relax nullability when necessary by removing NotNullable over the field, and set AllowNulls = true on the StringLengthValidator.

NVarChar(MAX) fields  

For long string fields, like ‘note’, we could override the type to NText type with the attribute

SqlDbType(SqlDbType=SqlDbType.NText)

But since NText is already deprecated in favor of NVarChar(MAX), we will place this attribute instead:

SqlDbType(Size = int.MaxValue)

The framework understand int.MaxValue and replaces it for NVarChar(MAX)

Other string fields, like homePhone and extension, give us the opportunity to use a TelephoneValidator  

DateTime fields 

Notice that for DateTime fields we just make the field type nullable when is not mandatory.

Also, we could place a convenient validator to constraining our dates: 

[DateTimePrecissionValidator(DateTimePrecision.Minutes)]

This way we avoiding rounding errors due to seconds, milliseconds, etc.. 

Notice that all the ValidatorAttributes, by convention, accept null values so in order to prevent null values you will need a NotNullValidator as well. 

VarBinary(MAX) fields 

Just as we did with notes field.  The same is applicable to photo field. By default byte[] fields are translated to VarBinary. Instead of using SqlDbType.Image, (also deprecated)  we will use Size = int.MaxValue to make it VarBinary(MAX).

EmbeddedEntity fields 

The address field, of type AddressDN, will include all the corresponding columns in the form Address_Address, Address_City, Address_Region,....

Also, in order to express that the address entity itself is null (not some of the internal fields), a new Address_HasValue field will be created, and the types of the internal fields will be overridden to support null values.

We can disable this feature just by placing [NotNullable] over the address field, but in this case this is all right.

Lite fields 

Lite<T> is a generic class that creates a lightweight reference to an entity. At runtime, it only contains Type, Id and ToStr fields, and can be used in your entities to control lazy retrieving.

On the schema, however, a Lite<T> field is exactly the same than a field of type T: An id with a foreign key to the other entity. 

Also Lite<T> can be used in your business logic to pass it as a parameter, effectively making an strongly typed Id, and is much easier to debug since it contains the ToStr of the original entity.   

Its also completely integrated with out LINQ provider so you can retrieve Lite<T>, to populate a combo box for example. We will see Lite<T> many times in the course of this tutorial.

In this case, by making ‘reportsTo’ a field of type Lite<EmployeeDN> we stop the engine from retrieving every employee all the way up in the chain of command.

MList<TerritoryDN> fields 

An MList is an self-change-tracking version of a List. It contains all the convenient methods of a List (RemoveAll, Invert, and Indexer...) and can be easily data-bound since it implements INotifyCollectionChanged.   

On the database, an MList field won’t create any column, but a table with all the items that belong to an entity associated to the entity using an idParent foreign key.  

In our case, the field territories of type MList<TerritoryDN> will create a Table with name EmployeeDNTerritories that will look quite similar to the original one. 

However, MList<T> is not limited to relational tables (collection of other entities) but can also be used to create collection of values (MList<int>) or collections of embedded entities (MList<AddressDN>). 

Getting fast: Rest of the entities

After writing Employee entity, writing the rest of the entities should be straightforward.

Maybe it gets a little boring, but this is only for teaching purposes. In a real application you will create the entities instead of the tables, not after.

We resist making a tool that generates the entities from a legacy database automatically. Signum Framework is quite strict with some conventions and would be hard to consider on any random legacy database. 

But more important, the entities will be the core of your application, writing them by hand is a good opportunity to reconsider de design and fix legacy mistakes.

Some small notes:

  • Shipper (straightforward)
  • Customer
    • Use our AddressDN EmbeddedEntity.
    • Skip Customers demographics (the table is empty and adds no value) 
  • Supplier
    • Use our AddressDN EmbeddedEntity.
    • Use URLValidators on HomePage.
    • Use TelephoneValidator on Phone and Fax.
  • Product
    • o Make the relationship to CategoryDN and SupplierDN, both a Lite<T> relationship.
  • Category
    • User a byte[] field (with SqlDbType(Size=int.MaxValue)) for picture.
  • Order
    • Use our AddressDN EmbeddedEntity
    • Make the relationship to Shipper and Customer a Lite relationship

Finally, the only tricky point in Order entity is how to implement OrderDetails. It’s a relational table but has some information attached to the relationship (UnitPrice, Quantity, Discount). 

As we have seen, we can implement it using an MList<T> being T and EmbeddedEntity, we will have to create an OrderDetailDN EmbeddedEntity first:

entity [Tab] [Tab] OrderDetail [Enter]
field [Tab] [Tab] Lite<ProductDN> [Tab] product [Tab] Product [Enter]
field [Tab] [Tab] decimal [Tab] unitPrice [Tab] UnitPrice [Enter]
field [Tab] [Tab] int [Tab] quantity [Tab] Quantity [Enter]
field [Tab] [Tab] float [Tab] discount [Tab] Discount[Enter]

The result should be like this after adding a ValidationAttribute and changing the base type.

[Serializable]
public class OrderDetailsDN : EmbeddedEntity
{
    Lite<ProductDN><productdn> product;
    [NotNullValidator]
    public Lite<ProductDN> Product
    {
        get { return product; }
        set { Set(ref product, value, () => Product); }
    }

    decimal unitPrice;
    public decimal UnitPrice
    {
        get { return unitPrice; }
        set { Set(ref unitPrice, value, () => UnitPrice); }
    }

    int quantity;
    public int Quantity
    {
        get { return quantity; }
        set { Set(ref quantity, value, () => Quantity); }
    }

    float discount;
    public float Discount
    {
        get { return discount; }
        set { Set(ref discount, value, () => Discount); }
    }
}
</productdn>

PropertyValidation

So far we have used ValidationAttributes for all our validation. Simple but not that flexible.  

Let’s push the validation system a little bit. Suppose that we want to be sure that discount is something like 5%, 10%... 25%, always a multiple of 5%.

We don’t have a Validator attribute that fits these requirements, but we could create one just by creating a class that inherits from ValidationAttribute.

In this case, however, we will just override PropertyValidation method in the entity itself:

protected override string PropertyValidation(PropertyInfo pi)
{
    if (pi.Is(() => Discount))
    {
        if ((discount * 100) % 5 != 0)
            return "Discount should be multiple of 5%"; 
    }

    return base.PropertyValidation(pi);
}
        

This method will be called for every property of the entity, and if it returns and string, the property value will be considered wrong. If everything is ok it should return null.

This technique has the advantage that we can take into account more than one property value to make our validation logic. In this case we will need to use Notify(()=>OtherProperty) to force the re-evaluation of the validation logic for the affected properties after changing the value.

Finally, just by creating a field MList in OrderDN entity we will have the expected result.

MList<OrderDetailsDN> details;
public MList<OrderDetailsDN> Details
{
    get { return details; }
    set { Set(ref details, value, () => Details); }
}
    

At this moment, Southwind entities should be able to produce a database quite similar to Northwind, let’s try:

Southwind.Logic

Reload the project Southwind.Logic. This project contains the business logic that will run on the server (so we have database access) in different scenarios: 

  • The Web interface. 
  • The Windows interface through a WCF service. 
  • The Load application. 
  • The Unit Tests.

We will get into this topic more deep in the next tutorial, for now let’s start simple.

In order to create the database we first have to tell the engine witch entities will get into the schema.

Let’s just rename the example class MyEntityLogic to OrdersLogic, and change the code that includes MyEntityDN to include OrderDN instead.

The rest of the entities get automatically included by walking the dependencies of OrderDN.

Southwind.Load

Next step is reloading Southwind.Load. In this project we have already created a simple Console Application that we can use for manipulating the schema, loading data, or any other administration tasks.

The template already has a menu that allows us to create and synchronize the database. Let’s just go to SQL Management Studio, connect to localhost, and create a new database.

By convention the name of the database should be the same than the project, in this case ‘Southwind’, but you can change it in the connection string.

Once created, just mark Southwind.Load as startup project and run. Choose the first option, New Database by pressing “N” and… voilà! The schema get’s created according to the entities and should look like this:

Summary 

In this tutorial we have seen how to create entities just by inheriting from some base classes 

  • IdentifiableEntity: for entities with an Id and a Table
  • EmbeddedEntity: for entities that live inside of some parent entity
  • Entity: for entities with an Id and a Table, and also control of concurrent modification. 

In our entities we can create different field and properties:

  • Values: Any standard value type (string, DateTime, int, float, Guid, byte[]..) will create the corresponding column.  
  • References: We can create references to other entity just by creating a field of this entity type, or we can use Lite<T> to enable Lazy loading. We didn't explain it, but this references could be polymorphic using the inheritance support.  
  • Collections: We can also use MList<T> to  make collection tables that could be used to represent collections of entities, values or embedded entities.  
  • Enums: We didn't explain it here, but you can also use enums in your entities.   

We can validate our entities just by placing some ValidationAttribute over the properties, or we can override PropertyValidation method to have full control. In other tutorials we will see more convoluted validation strategies.  

As you see Signum Framework is designed top-down to promote a code-first approach, and trying to make it work on a legacy database is a pain due to some strict conventions (like having Id and ToStr in every entity).

Nevertheless, we have seen how the generated schema is simple and predictable and can be exploited by third-party tools easily.

In the next tutorial we will get deep into how to write the business logic, load legacy data and create the user interfaces, and how these conventions will simplify our code on the long run.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)

About the Author

Olmo del Corral
Software Developer (Senior) Signum Software
Spain Spain
I'm Computer Scientist, one of the founders of Signum Software, and the lead developer behind Signum Framework.
 
www.signumframework.com
 
I love programming in C#, Linq, Compilers, Algorithms, Functional Programming, Computer Graphics, Maths...

Comments and Discussions

 
QuestionDoes not seem to work with VS2010 Express PinmemberHezek1ah3-Aug-11 7:09 
QuestionGreat to see new version is out. PinmemberSteveMets21-Jul-11 8:24 
AnswerRe: Great to see new version is out. PinmemberOlmo del Corral21-Jul-11 22:09 
QuestionMy first thoughts. PinprotectorPete O'Hanlon12-Jul-11 22:00 
AnswerRe: My first thoughts. PinmemberOlmo del Corral12-Jul-11 23:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 14 Nov 2012
Article Copyright 2011 by Olmo del Corral
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid