# 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");
}
}
}
}
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); }
}
}
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}");
}
}
}
}
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}");
}
}
}
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);
}
}
}
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);
}
}
}
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");
}
}
}
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!