# ModelBuilders - Conventions

Because ModelBuilders have been designed to work with the powerful XAF TypesInfo system (opens new window) they can apply metadata in an imperative way.

# Imperative simple example

All of the language features of C# (if statements, foreach loops, string interpolation etc) can be used by ModelBuilders to apply attributes.















 
 
 
 
 
 
 
 
 
 
 
 
 
 




using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class DemoTaskModelBuilder : ModelBuilder<DemoTask>
    {
        public DemoTaskModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            //For demo simplicity
            //this could come from configuration, or a database
            if(new Random().Next() % 2 == 0)
            {
                var caption = "contacts";
                For(m => m.Contacts)
                    .HasTooltip($"View, assign or remove {caption} for the current task");
            }
            else
            {
                var caption = "assigned contacts";
                For(m => m.Contacts)
                    .HasTooltip($"View, assign or remove {caption} for the current task");
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

The ability to build metadata on the fly opens up a range of possibilities for advanced scenarios such as reading that metadata directly from a configuration file, database, or other runtime modifications.

CAUTION

XAF reads metadata at application startup, consequently, in order to to able to see any of the changes that have been made the application must be restarted in order to apply them.

# Imperative advanced example

Consider the Sensors business object that has a number of similar typed properties named Value1 to Value5.















 


 


 


 


 


using System;
using DevExpress.ExpressApp.Model;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
using DevExpress.Persistent.Validation;
using DevExpress.Xpo;

namespace MainDemo.Module.BusinessObjects
{
    public class Sensor : BaseObject
    {
        public Sensor(Session session) : base(session) { }

        private int value1;
        public int Value1 { get => value1; set => SetPropertyValue(nameof(Value1), ref value1, value); }

        private int value2;
        public int Value2 { get => value2; set => SetPropertyValue(nameof(Value2), ref value2, value); }

        private int value3;
        public int Value3 { get => value3; set => SetPropertyValue(nameof(Value3), ref value3, value); }

        private int value4;
        public int Value4 { get => value4; set => SetPropertyValue(nameof(Value4), ref value4, value); }

        private int value5;
        public int Value5 { get => value5; set => SetPropertyValue(nameof(Value5), ref value5, value); }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

To apply a caption to those properties in an efficient 'imperative' way a for loop can be used.















 
 
 
 
 



using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class SensorModelBuilder : ModelBuilder<Sensor>
    {
        public SensorModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            for (int i = 1; i <= 5; i++)
            {
                For($"Value{i}")
                    .HasCaption($"Sensor Value {i}");
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# ConventionBuilder - ForProperties

Whilst he code above illustrates the extraordinary power of ModelBuilders it highlights the ease with which it's possible to create code that could, at some point in the future, become very difficult to refactor. To mitigate this Xenial.Framework has the ForProperties construct that can accept a range of properties.















 
 
 
 
 
 
 



using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class SensorModelBuilder : ModelBuilder<Sensor>
    {
        public SensorModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            ForProperties(
                m => m.Value1,
                m => m.Value2,
                m => m.Value3,
                m => m.Value4,
                m => m.Value5
            ).HasDisplayFormat("{0:n}");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

TIP

This is particularly useful if an attribute should only be applied to selected properties.

# ConventionBuilder - ForAllProperties

For those occasions when it would be useful to apply an attribute to all the properties of a business object there is the ForAllProperties construct.

The code belows illustrates how to set the AllowEdit property to false on all of the business object's properties, essentially adding ModelDefaultAttribute("AllowEdit", "False")to each one.















 
 



using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class SensorModelBuilder : ModelBuilder<Sensor>
    {
        public SensorModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            ForAllProperties()
                .WithModelDefault(ModelDefaults.AllowEdit, false);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

TIP

The same result can be achieved with the shorthand NotAllowingEdit().

# ConventionBuilder - Except

If the desired result is to apply an attribute to most but not all of the properties of the business object then the framework has the Except method that can be applied to ForAllProperties which acts acts as a filter.

The code below illustrates how only properties Value2 and Value4 should be editable, with the rest being un-editable.
















 
 
 
 




using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class SensorModelBuilder : ModelBuilder<Sensor>
    {
        public SensorModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            ForAllProperties()
                .Except(
                    m => m.Value2,
                    m => m.Value4
                )
                .WithModelDefault(ModelDefaults.AllowEdit, false);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

TIP

There are several overloads that make it possible to create more complex filters.

# ConventionBuilder - ForPropertiesOfType

There will be occasions when it would be very useful to apply attributes to a specific data type.

The code below demonstrates how it would be possible to apply DisplayFormat and EditMask to all integer properties.















 
 
 



using System;
using DevExpress.ExpressApp.DC;
using Xenial.Framework.ModelBuilder;

namespace MainDemo.Module.BusinessObjects
{
    public class SensorModelBuilder : ModelBuilder<Sensor>
    {
        public SensorModelBuilder(ITypeInfo typeInfo) : base(typeInfo) { }

        public override void Build()
        {
            base.Build();

            ForPropertiesOfType<int>()
                .HasDisplayFormat("{0:n3}")
                .HasEditMask("n3");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

TIP

This can be used with the Except filter as well!