News, examples, tips, ideas and plans.
Thoughts around ORM, .NET and SQL databases.

Tuesday, December 13, 2011

Setting default values for persistent properties

While DataObjects.Net 4.4.1 & 4.3.8 Final installers are being prepared and tested, here is one more post about one not so well known feature of effective domain modelling and how that feature is extended in the upcoming release.

DataObjects.Net includes a set of rules for defining the default value for a persistent property.
The rules are the following:

Let T is the type of a persistent property, then:

1. T is a primitive type

If T is a primitive type, Guid or String then the default value for the property is default(T) and the type of the underlying column is T.
public class Animal : Entity {
...
[Field]
public int Age { get; set; }

CREATE TABLE Animal (
...
[Age] [int] NOT NULL

2. T is Nullable<>

If T is Nullable<> then the default value is NULL and the type of the underlying column is Nullable T.
public class Animal : Entity {
...
[Field]
public int? Legs { get; set; }

CREATE TABLE Animal (
...
[Legs] [int] NULL

3. T is a reference type

If T is a reference type then the default value is NULL and the type of the underlying column is the type of primary key of the referenced Entity.
public class Animal : Entity {
...
[Field]
public Person Owner { get; set; }

CREATE TABLE Animal (
...
[Owner.Id] [int] NULL

These rules are more or less obvious and straightforward but what if the overwhelming majority of animals in your application universe have 4 legs and you don't want to set this property manually again and again? Or there should not be homeless animals? Then I guess, you should have the opportunity to override these rules.

This can be done with the help of [Field] attribute.

1. Setting default value for a primitive persistent property

public class Animal : Entity {
...
[Field(DefaultValue = 4)]
public int? Legs { get; set; }

CREATE TABLE Animal (
...
[Legs] [int] NULL
...
ALTER TABLE [dbo].[Animal] ADD CONSTRAINT [DF_Animal_Legs]  DEFAULT ((4)) FOR [Legs]

2. Changing nullability of a reference field.

public class Animal : Entity {
...
[Field(Nullable = false)]
public Person Owner { get; set; }

CREATE TABLE Animal (
...
[Owner.Id] [int] NOT NULL
This setting leads to NOT NULL column which means that the value of the property must be set prior to any Session.Persist() call while constructing the Animal entity. Therefore, the right way to set such properties is inside entity's constructor, before executing any queries.

3. Setting default value for a reference persistent property.

Note, this feature is implemented in the upcoming version of DataObjects.Net (by upcoming I mean 4.3.8 or 4.4.1 branches).
public class Animal : Entity {
...
[Field(DefaultValue = 1)]  // 1 here is the key of the default animal owner
public Person Owner { get; set; }

CREATE TABLE Animal (
...
[Owner.Id] [int] NULL
...
ALTER TABLE [dbo].[Animal] ADD CONSTRAINT [DF_Animal_OwnerId]  DEFAULT ((1)) FOR [Owner.Id]
This makes sense in a scenario when the key of the default referenced entity is already known on a compilation step or it is a well-known constant identifier so there is an Entity with that key in the domain.

I hope these little tricks will help you in modelling your domains with even less efforts.

2 comments:

  1. Nice!

    I also saw an example of Field(DefaultValue..) that uses enum http://support.x-tensive.com/question/4559/default-field-set-to-enum-works-for-memory-database-but-not-postgres

    Good work!

    ReplyDelete
  2. Right.

    Malisa, thanks for pointing to that. I'll update the post.

    ReplyDelete