Falls kein Datenprovider vorhanden ist, der die Auswahl bietet, kann man einen eigenen Datenprovider implementieren. Dafür sind zwei Klassen notwendig, zum einen der Datenprovider selbst und zum anderen seine Eigenschaften.

Eigenschaften des Datenproviders

Die Eigenschaften

  • Die Klasse muss die Basis Klasse ControlDataProviderPropertiesBase erweitern.
  • Die Eigenschaften sollten prinzipiell simple Datentypen sein. Komplexe Datentypen sollten dringend vermieden werden.
    Beispielsweise sollte die Guid einer Konfiguration, anstatt die Konfiguration selbst, definiert werden.
  • Die Properties-Klassen der DataProvider dürfen keine Referenzen auf veränderliche Datentypen enthalten.
    Andernfalls kommt es bei der Implementierung von DataProvider-Modifier-Methoden zu schwer zu findenden Fehlern mit undefiniertem Verhalten, weil Änderungen an den Listen sich dauerhaft auf Masken aller Benutzer auswirken.
  • Sinnvoll ist es mit Guid, GuidSet, etc. zu arbeiten, auch wenn diese nicht in den Attributen angeben werden können. Die Umwandlung, sollte dann direkt im Konstruktor erfolgen.
  • WICHTIG: Der vollständige Klassenname des eigentlichen Datenproviders muss in die Eigenschaft DataProviderTypeFullName gesetzt werden.

Beispiel

public class CDPEnumValuesProperties : ControlDataProviderPropertiesBase
{
    public Guid MasterGuid { get; set; }
    public bool DisplayIcon { get; set; } = true;
    public EnumEntryActiveState ActiveState { get; set; }
    public GuidSet WantedValues { get; set; }

    public CDPEnumValuesProperties(string masterGuid = null, bool displayIcon = true, string activeState = null, string[] wantedValues = null) : base()
    {
        DataProviderTypeFullName = typeof(CDPEnumValues).FullName;

        if (!string.IsNullOrEmpty(masterGuid))
            MasterGuid = masterGuid.ToGuid();

        DisplayIcon = displayIcon;

        if (activeState != null || activeState.IsGuid())
            ActiveState = Api.Enum.GetEnumValue<EnumEntryActiveState>(activeState.ToGuid());

        WantedValues = GuidSet.Parse(wantedValues);
    }
}

Der Datenprovider

  • Er muss das Interface IControlDataProvider implementieren.
  • In Properties stehen die Eigenschaften zur Verfügung.
  • Die Methode GetRows() liefert das Ergebnis zurück.
    • Es ist sinnvoll Datenprovider so zu implementieren, dass diese selbst erweitert werden können. In diesem Beispiel wurde eine virtuelle Methode AddWhere() implementiert. Diese kann überschrieben werden, und dadurch können weitere Filter implementiert werden.
    • Die Parameter enthalten den Filter, der in der UI vom Anwender eingegeben wurde. Dieser Filter muss auf die Ergebnisliste angewendet werden. In diesem Beispiel in der Methode AddFilter().
  • Die Methode GetByKeys() liefert für eine gegebene Menge von Schlüsseln die Einträge zurück. Diese aus zwei Gründen nicht unbedingt GetRows() aufrufen.
    • Wenn das Zeitverhalten durch eine direkte Implementierung zu erwarten besser ist.
    • Wenn Schüssel vorkommen können, die nicht in der Auswahl vorhanden sind. Beispiel: Können gewählte Auswahlwerte nicht in der Auswahlliste vorhanden sein. Mögliche Gründe sind, das sie inaktiv gesetzt worden sind oder nur bestimmte Personen bestimmte Auswahllistenwerte wählen können.
public class CDPEnumValues : IControlDataProvider
{
    public ControlDataProviderPropertiesBase Properties { get; set; }

    public virtual IQueryable<ControlDataRow> GetRows(ControlDataProviderParameter parameter = null)
    {
        if (Properties != null && Properties is CDPEnumValuesProperties prop)
        {
            IEnumerable<ValueEnum> enumValues = AddWhere(Api.Enum.GetEnumValues(prop.MasterGuid));

            IEnumerable<ControlDataRow> values = enumValues.Select(CreateRow);

            values = AddFilter(values, parameter?.Filter);

            return values.AsQueryable();
        }

        if (Properties == null)
            throw new ArgumentNullException("Data provider: The properties are NULL");

        throw new ArgumentException(String.Format("Data provider: The properties are {​0​} and not {​1​}", Properties.GetType().FullName, typeof(CDPEnumValuesProperties).FullName));
    }

    public override IEnumerable<ControlDataRow> GetByKeys(IReadOnlyCollection<string> keys)
    {
        foreach (string key in keys)
            if (key != null && Guid.TryParse(key, out Guid guid) && Api.Enum.GetEnumValue(guid) is ValueEnum value)
                yield return CreateRow(value);
    }

    public virtual IEnumerable<ValueEnum> AddWhere(IEnumerable<ValueEnum> list)
    {
        if (Properties is CDPEnumValuesProperties prop)
        {
            return list.Where(ff => (prop.OnlyTheseValues == null || prop.OnlyTheseValues.Contains(ff.ValueGuid))
                && ((prop.WantedValues != null && prop.WantedValues.Contains(ff.ValueGuid))
                    || ((prop.IncludeNonReadable || ff.CurrentUserCanSelect())
                        && (prop.ActiveState == null || prop.ActiveState == EnumEntryActiveState.All || ff.IsActive == (prop.ActiveState == EnumEntryActiveState.Active)))));
        }

        return list;
    }

    protected virtual ControlDataRow CreateRow(ValueEnum value)
    {
        return new ControlDataRow
        {
            Key = value.GuidString,
            Title = value.Translation_Guid.ToString().Translate(),
            DataItem = value,
        };
    }

    public virtual IEnumerable<ControlDataRow> AddFilter(IEnumerable<ControlDataRow> list, string filter)
    {
        if (string.IsNullOrWhiteSpace(filter))
            return list; // no filter

        string conditionFilterPart = filter.Replace("\"", "").ToLower();
        return list.Where(ff => ff.Title != null && ff.Title.ToLower().Contains(conditionFilterPart));
    }
}

Alternative Basisklassen für den Datenprovider

  • CDPBase
    Die Basisklasse implementiert Properties und stellt Hilfsmethoden zur Verfügung.
  • SimpleCDPBase
    Erweitert CDPBase und implementiert GetByKeys, so daß man nur noch eine GetByKey Methode implementieren muss.
  • StaticCDPBase
    Erweitert CDPBase und implementiert den Filter und die Sortierung. Statt GetRows implementiert man selbst GetRawRows. Auch die GetByKeys ist schon implementiert. Nur zu empfehlen, wenn GetRawRows hochperformant alle möglichen Ergebnisse gesichert enthält.