Search This Blog

Monday, June 30, 2014

Using a built-in lookup editor based on values from another table for editing simple property types

This is actually a variation of a solution I described in my recent A very interesting way to update and display a persistent property via a non-persistent one post, because it is again based on a non-persistent reference property filtered using the DataSourcePropertyAttribute along with a custom data source collection.
This non-persistent reference property is represented using the standard XAF's lookup PropertyEditors, which are quite convenient for providing a user with the capability to select a single value either from a database table or from an arbitrary data source. In one turn, by adding custom logic to the getter and setter of our non-persistent calculated property, we can update the stored persistent value, which does not need to be a reference to another persistent class (table), but to any type (e.g., string or integer). This may come in handy for legacy databases when you cannot really modify the schema to provide normal associations between tables.



Let's take a look at the actual code for this solution:



[Persistent("StoredValue")]
private string stringFieldForStore;
private Position _LookupPropertyForDisplay;
[NonPersistent, DataSourceProperty("LookupDataSource"), XafDisplayName("My drop-down editor based on values from another table")]
public Position LookupPropertyForDisplay {
    get {
        if(_LookupPropertyForDisplay == null && !string.IsNullOrEmpty(stringFieldForStore)) {
            _LookupPropertyForDisplay = LookupDataSource.Single(v => v.Title == stringFieldForStore);
        }
        return _LookupPropertyForDisplay;
    }
    set {
        SetPropertyValue<Position>("LookupPropertyForDisplay", ref _LookupPropertyForDisplay, value);
        if(!IsLoading && !IsSaving) {
            stringFieldForStore = value != null ? value.Title : string.Empty;
            OnChanged("stringFieldForStore");
        }
    }
}
private XPCollection<Position> _lookupDataSource = null;
protected XPCollection<Position> LookupDataSource {
    get {
        if(_lookupDataSource == null) {
            _lookupDataSource = new XPCollection<Position>(Session);
        }
        return _lookupDataSource;
    }
}


Here we have:
1. stringFieldForStore - a hidden string persistent field, which actually stores a selected lookup editor value (the position's Title) in the database;
2. LookupPropertyForDisplay - a visible non-persistent calculated property, which is represented by the standard lookup PropertyEditor in the UI for both Windows and the Web.
3. LookupDataSource - a hidden collection property, which contains persistent objects from another table serving as a data source for the lookup editor.

Here, I would like to once again note that storing a string value instead of the object's identifier is against the best practices for database design (data denormalization). So, use this approach when you need to provide a choice from values based on another table only if there is a strong reason for this. In all other cases, it is possible to create a persistent class mapped to that table and then declare a reference property of this reference type.

6 comments:

  1. The Ultimate Business Application Framework

    Business platform Xafari is intended for development of powerful business-oriented applications. It uses state of the art flexible platform DevExpress XAF http://galaktikasoft.com/xafari/

    ReplyDelete
  2. Dear Dennis,

    I was tested your source code, working fine windows form application. but If was tested in web application, after I switch a lookup value and save, result is null.

    ReplyDelete
    Replies
    1. Syamsuri, I've just tested this code with our MainDemo.Web app and it all worked fine (16.1.6). Would you please submit a ticket and attach your problematic test app along with a video showing the result in the UI? You can use the https://www.devexpress.com/Support/Center/Question/Create service for that purpose.

      Delete
  3. Hi Dennis,

    Is this supposed to work in a XAF Web App, using EF Code First objects ?
    I cannot get it to work (LookupPropertyForDisplay is alway NULL, even in the ObjectSpace.ObjectSpacing event handler)

    ReplyDelete
    Replies
    1. @Vlad: Yes, it should work with EF too - just replace XPCollection<> with List<>, NonPersistentAttribute with NotMappedAttribute, SetPropertyValue with SetPropertyValue from your INotifyPropertyChanged implementation and new XPCollection<> with objectSpace.GetObjects<> (IObjectSpaceLink is required) + remove PersistentAttribute.

      Delete
  4. good example. But, shouldn't _lookupDataSource be declared as static to prevent having a collection for every instance in memory?
    Or perhaps XPO will reuse the same instances.
    Thank you.

    ReplyDelete