Entities (Row)

    Unlike full blown ORMs like NHibernate / Entity Framework, Serenity provides minimum features required to map and query databases with intellisense, compile time checking and easy refactoring.

    Serenity entities are usually named like XYZRow. They are subclasses of Serenity.Data.Row.

    Let’s define a simple row class:

    Let’s study parts of a row declaration.

    Here we define an entity named SimpleRow, which probably maps to a table named Simple in database.

    Row suffix here is not required, but common practice, and it prevents clashes with other class names.

    Now we declare our first property. This property maps to a database column named Name in the Simple table.

    It is not possible to use an auto property here (like get; set;). Field values must be read and set through a special object called Field.

    Field objects are very similar to WPF dependency properties. Here is a dependency property declaration sample:

    1. public static readonly DependencyProperty MyCustomProperty =
    2. DependencyProperty.Register("MyCustom", typeof(string), typeof(Window1));
    3. {
    4. get { return this.GetValue(MyCustomProperty) as string; }
    5. set { this.SetValue(MyCustomProperty, value); }
    6. }

    Here we define a static dependency property object (MyCustomProperty), that contains property metadata and allows us to set and get property value through its GetValue and SetValue methods. Dependency properties allows WPF to offer features like validation, data binding, animation, and more.

    Similar to dependency properties, Field objects contains column metadata and clears way for some features like assignment tracking, building queries without expression trees, change notification etc.

    While dependency properties are declared as static members in class they are used, Field objects are declared in a nested class named RowFields. This allows to group and reference them easier, without having to add Field or Property suffix, and keeps our entity clear from field declarations.

    Here is our second property, named Age, with type Int32?.

    Serenity entity properties are always nullable, even if database column is not nullable.

    Is it not possible for a not null field to have a null value, if you query it through a left/right join? How can you say, if its retrieved value is null or zero in that case?

      We noted that field objects are declared in a nested subclass named RowFields (usually). Here we are creating its sole static instance. Thus, there is only one RowFields instance per row type, and one field instance per row property.

      Init is an extension method that initializes members of RowFields. It creates field objects that are not explictly initialized.

      Now we define SimpleRow’s parameterless constructor. Base Row class requires a RowFields instance to work, and we pass our static Fields object. So all instances of a row type (SimpleRow) share a single RowFields (SimpleRow.RowFields) instance. This means they share all the metadata.

      1. public class RowFields : RowFieldsBase
      2. {
      3. public StringField Name;
      4. public Int32Field Age;

      Here we define our nested class that contains field objects. It should be derived from . RowFieldsBase is a special class closely related to Row that contains table metadata.

      We declared a StringField and a Int32Field. Their type is based on their property types, and they must match exactly.

      Their names must also match the property names, or you’ll get an initialization error.

      We didn’t initialize these field objects, so their values are initially null.