Galaktikasoft

Application settings in the example application Xafari Northwind

Description of the example of adding user settings and system settings to training application Xafari Nortwind using component Xafari.BC.Settings.

Background information on Xafari.BC.Settings

From the conceptual angle, with the typical use of the component functionality, the 'Setting' is a stored value of a global variable within the limits of the system. The value of the Setting either can be stored as a global value (applicable to all users of the application system), or it can be specified for certain user or role. In order to implement such behavior, the concept of 'layer' is used. The layer allows to redefine the value of the setting in the following layer, for example, layer 'by users' allows to determine the values: 1) Global value 2) Value for the layer 3) Value for the user. This functionality can be used for the purposes of the application as well, for example, the Setting can depend on the document type, in which case the document type is the layer.

The concept of Settings has the following specifics:

  1. The Setting is a stored global variable.
  2. It is only possible to add a new Setting in the Visual Studio.
  3. It is possible to set value for the Setting in the application (special forms, intended for the user and for the administrator, are used for this purpose).
  4. Values of Settings are stored in the server database.

Training task definition

The following discussion refers to training application Xafari Nortwind. It is necessary to add Settings to the application. The Settings must be accessible for the user so that the user can set values of the Settings. The list of the required Settings is shown in the table:

Setting Setting type
Category Category (persistent)
ShortName String
HomePage String
Region String
TerritoryName String
Code String
Employee Employee (persistent)
ShippedDate DateTime

We also raise an additional requirement that the Settings must be combined in the following groups:

It is necessary to allow the user to set values of the settings at the following levels:

  1. Global value — for all users
  2. At the user's subdivision level
  3. At the user level

To achieve this, it is necessary to define three layers, respectively:

  1. Default layer
  2. Layer based on object type 'Department'
  3. Layer based on object type 'InheritedUser'

Each element of the second layer will be based on an instance of 'Department', and elements based on the instances of 'InheritedUser', that belong to the specified 'Department', will be its descendants.

Connecting modules

To start the work with application settings, it is necessary to connect the following modules:
XafariBCSettingsModule has to be connected to a platform-independent part of the application solution.
A link to the respective library will be added to directory 'References' of the project.
Recommendation: Link the library again manually.

Connecting modules

Description of setting layers

Description of the last layer:
public class NorthwindSettingsInheritedUser : SettingValueSlice
{
    public override List ChildrenSlices
    {
        get { return new List(); }
    }
}

This code tells that instances of class 'NorthwindSettingsInheritedUser' will represent the last layer in the layer hierarchy, and also:

Description of an interim layer:
public class NorthwindSettingsDepartment : SettingValueSlice
{
    public override string Name
    {
        get { return this.SliceObject.Name; }
    }
 
    public override IEnumerable GetChildrenFromNextSlice()
    {
        var users = this.ObjectSpace.GetObjects(CriteriaOperator.Parse("Department = ?", this.SliceObject));
        return users.Select(user => (NorthwindSettingsInheritedUser)this.GetChildSlice(user)).ToList();
    }
}

This code tells that instances of class NorthwindSettingsDepartment will represent an interim layer in the layer hierarchy, and also:

Default layer description:
public class NorthwindSettingsDefaultValueSlice : SettingDefaultValueSlice
{
    public override IEnumerable GetChildrenFromNextSlice()
    {
        var departments = this.ObjectSpace.GetObjects();
        return departments.Select(slice1 => (NorthwindSettingsDepartment)this.GetChildSlice(slice1)).ToList();
    }
}

This code tells that instances of class NorthwindSettingsDefaultValueSlice will represent the default layer in the layer hierarchy, and also:

Description of settings and setting groups

Let us describe the interface containing settings for application Northwind:

public interface IModelNorthwindSettings
{
}

We extend model node Setting in the platform-independent module (file Module.cs) by adding a setting node for application Northwind:

public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders)
{
    extenders.Add();
}

We add 3 setting groups, Documents, Classifiers, Olap. Settings interface IModelNorthwindSettings will be as shown below:
public interface IModelNorthwindSettings

{
    Documents Documents { get; }
    Classifiers Classifiers { get; }
    IModelOlapSettings Olap { get; }
}

The following code was used to describe settings group Documents:

public interface Documents : IModelBCSettingsGroup
{
    Order Order { get; }
}

Group Documents includes group Order:

public interface Order : IModelBCSettingsGroup
{
    IModelKeyEmployeeObject Employee { get; }
    IModelKeyDateTime ShippedDate { get; }
}

Group Order includes 2 settings. Setting ShippedDate is of type IModelKeyDateTime, which is provided by platform XAFARI. Setting Employee is of type IModelKeyEmployeeObject and is persistent, the code describing the setting of type IModelKeyEmployeeObject:

public interface IModelKeyEmployeeObject : IModelBCSettingsObjectItem
{
}
[DomainLogic(typeof(IModelKeyEmployeeObject))]
public class IModelKeyEmployeeObjectLogic : IModelBCSettingsXPObjectItemLogic
{
    public IModelKeyEmployeeObjectLogic(IModelKeyEmployeeObject instance) : 
    base(instance) { }
}

Please note that when describing a persistent type setting, it is necessary that the setting interface is inherited from interface IModelBCSettingsObjectItem, and the domain logic of the setting is inherited from class IModelBCSettingsXPObjectItemLogic. The remaining parts of the process of creation of the persistent type interface do not differ from the process of creation of the non-persistent type setting.
Let's return to the description of settings for application Northwind.
Setting group Classifiers is described as shown below:

public interface Classifiers : IModelBCSettingsGroup
{
    Product Product { get; }
    Territory Territory { get; }
    Supplier Supplier { get; }
}

In the description we can see, that group Classifiers includes groups Product, Territory, and Supplier. These groups and the settings contained in them are described, as shown below:
Group Product:

public interface Product : IModelBCSettingsGroup
{
    IModelKeyCategoryObject Category { get; }
    IModelKeyString ShortName { get; }
}

Setting Category, which is a member of group Product, is described, as shown below:

public interface IModelKeyCategoryObject : IModelBCSettingsObjectItem
{
}
[DomainLogic(typeof(IModelKeyCategoryObject))]
public class IModelKeyCategoryObjectLogic : IModelBCSettingsXPObjectItemLogic
{
    public IModelKeyCategoryObjectLogic(IModelKeyCategoryObject instance) : base(instance) { }
}

Group Territory:

public interface Territory : IModelBCSettingsGroup
{
    IModelKeyString TerritoryName { get; }
    IModelKeyString Code { get; }
}

Group Supplier:

public interface Supplier : IModelBCSettingsGroup
{
    IModelKeyString HomePage {  get; }
    IModelKeyString Region { get; }
}
Using SettingsAccessor

At this phase, we have described the setting layers and the settings properly.
All work with the settings and layers is done by means of an Instance of class SettingsAccessor. Therefore, to work with the settings for application Northwind, it is necessary to implement one's own class SettingsAccessor based on the base class:

public class NorthwindSettingsAccessor : SettingsAccessor
{
    public NorthwindSettingsAccessor(XafApplication application) : base(application)
    {
        this.RootSlice = new NorthwindSettingsDefaultValueSlice();
        this.SliceTypes = new[]
        {
            typeof (NorthwindSettingsDepartment),
            typeof (NorthwindSettingsInheritedUser),
        };
    }
}

It is necessary to inherit class NorthwindSettingsAccessor from class SettingsAccessor and specify some properties of class SettingsAccessor:

After the above manipulations are completed, it is necessary to assign an instance of the class described above to property Instance of class SettingsAccessor (this assignment is made based on event LoggedOn of the application):

void Application_LoggedOn(object sender, LogonEventArgs e)
{
    var application = sender as XafApplication;
    if (application == null) return;
    if (SettingsAccessor.Instance != null) return;
    SettingsAccessor.Instance = new NorthwindSettingsAccessor(application);
    SettingsAccessor.Instance.CurrentSlice = SettingsAccessor.Instance.GetSlice(((InheritedUser)SecuritySystem.CurrentUser).Department, SecuritySystem.CurrentUser);
}

We can see in the code, that a layer corresponding to the current user is assigned to property CurrentSlice; if nothing is assigned, then property CurrentSlice will correspond to the default layer.
You can see the results of the work described above in the Xafari demo center: