diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs index 3dfdd5271..a40ad43c4 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -13,16 +14,17 @@ namespace Artemis.Core /// public class DataModelPath { + private Expression> _accessorLambda; private readonly LinkedList _segments; /// /// Creates a new instance of the class /// - /// The data model at which this path starts + /// The target at which this path starts /// A string representation of the - public DataModelPath(object dataModel, string path) + public DataModelPath(object target, string path) { - Target = dataModel ?? throw new ArgumentNullException(nameof(dataModel)); + Target = target ?? throw new ArgumentNullException(nameof(target)); Path = path ?? throw new ArgumentNullException(nameof(path)); if (string.IsNullOrWhiteSpace(Path)) @@ -53,34 +55,25 @@ namespace Artemis.Core public IReadOnlyCollection Segments => _segments.ToList().AsReadOnly(); /// - /// Gets a boolean indicating whether this data model path can have an inner path because it points to a list + /// Gets a boolean indicating whether this data model path points to a list /// - public bool CanHaveInnerPath => Segments.LastOrDefault()?.GetPropertyType()?.IsAssignableFrom(typeof(IList)) ?? false; - - /// - /// Gets the inner path of this path, only available if this path points to a list - /// - public DataModelPath InnerPath { get; internal set; } + public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null && + typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType()); internal Func Accessor { get; private set; } - public void SetInnerPath(string path) - { - if (!CanHaveInnerPath) - { - var type = Segments.LastOrDefault()?.GetPropertyType(); - throw new ArtemisCoreException($"Cannot set an inner path on a data model path if it does not point to a list (value is of type: {type?.Name})"); - } - - InnerPath = new DataModelPath(GetValue(), path); - } - /// /// Gets the current value of the path /// public object GetValue() { - return Accessor?.Invoke(Target); + if (_accessorLambda == null) + return null; + + // If the accessor has not yet been compiled do it now that it's first required + if (Accessor == null) + Accessor = _accessorLambda.Compile(); + return Accessor(Target); } /// @@ -89,8 +82,6 @@ namespace Artemis.Core /// If static, the property info. If dynamic, null public PropertyInfo GetPropertyInfo() { - if (InnerPath != null) - return InnerPath.GetPropertyInfo(); return Segments.LastOrDefault()?.GetPropertyInfo(); } @@ -100,8 +91,6 @@ namespace Artemis.Core /// If possible, the property type public Type GetPropertyType() { - if (InnerPath != null) - return InnerPath.GetPropertyType(); return Segments.LastOrDefault()?.GetPropertyType(); } @@ -111,21 +100,20 @@ namespace Artemis.Core /// If found, the data model property description public DataModelPropertyAttribute GetPropertyDescription() { - if (InnerPath != null) - return InnerPath.GetPropertyDescription(); return Segments.LastOrDefault()?.GetPropertyDescription(); } /// public override string ToString() { - if (InnerPath != null) - return $"{Path} > {InnerPath}"; return Path; } private void Initialize(string path) { + var startSegment = new DataModelPathSegment(this, "target", "target"); + startSegment.Node = _segments.AddFirst(startSegment); + var segments = path.Split("."); for (var index = 0; index < segments.Length; index++) { @@ -147,7 +135,7 @@ namespace Artemis.Core return; } - Accessor = Expression.Lambda>( + _accessorLambda = Expression.Lambda>( // Wrap with a null check Expression.Condition( nullCondition, @@ -155,7 +143,8 @@ namespace Artemis.Core Expression.Convert(Expression.Default(expression.Type), typeof(object)) ), parameter - ).Compile(); + ); + Accessor = null; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs index a82b3d1b3..d7093ac38 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using Artemis.Core.DataModelExpansions; @@ -12,11 +13,14 @@ namespace Artemis.Core /// public class DataModelPathSegment { + private Expression> _accessorLambda; + internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path) { DataModelPath = dataModelPath; Identifier = identifier; Path = path; + IsStartSegment = !DataModelPath.Segments.Any(); } /// @@ -34,6 +38,11 @@ namespace Artemis.Core /// public string Path { get; } + /// + /// Gets a boolean indicating whether this is the first segment in the path + /// + public bool IsStartSegment { get; } + /// /// Gets the type of data model this segment of the path points to /// @@ -64,7 +73,13 @@ namespace Artemis.Core /// public object GetValue() { - return Type == DataModelPathSegmentType.Invalid ? null : Accessor(DataModelPath.Target); + if (Type == DataModelPathSegmentType.Invalid || _accessorLambda == null) + return null; + + // If the accessor has not yet been compiled do it now that it's first required + if (Accessor == null) + Accessor = _accessorLambda.Compile(); + return Accessor(DataModelPath.Target); } /// @@ -82,12 +97,12 @@ namespace Artemis.Core // Dynamic types have no property and therefore no property info if (Type == DataModelPathSegmentType.Dynamic) return null; + // The start segment has none either because it is the datamodel + if (IsStartSegment) + return null; - // If this is the first segment in a path, the property is located on the data model - if (Previous == null) - return DataModelPath.Target.GetType().GetProperty(Identifier); // If this is not the first segment in a path, the property is located on the previous segment - return Previous.GetValue()?.GetType().GetProperty(Identifier); + return Previous.GetPropertyType()?.GetProperty(Identifier); } /// @@ -99,14 +114,24 @@ namespace Artemis.Core // Dynamic types have a data model description if (Type == DataModelPathSegmentType.Dynamic) return ((DataModel) GetValue())?.DataModelDescription; + if (IsStartSegment && DataModelPath.Target is DataModel targetDataModel) + return targetDataModel.DataModelDescription; + if (IsStartSegment) + return null; var propertyInfo = GetPropertyInfo(); if (propertyInfo == null) return null; // Static types may have one as an attribute - return (DataModelPropertyAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(DataModelPropertyAttribute)) ?? - new DataModelPropertyAttribute {Name = propertyInfo.Name.Humanize(), ResetsDepth = false}; + var attribute = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(DataModelPropertyAttribute)); + if (attribute != null) + { + if (string.IsNullOrWhiteSpace(attribute.Name)) + attribute.Name = propertyInfo.Name.Humanize(); + return attribute; + } + return new DataModelPropertyAttribute {Name = propertyInfo.Name.Humanize(), ResetsDepth = false}; } /// @@ -115,6 +140,10 @@ namespace Artemis.Core /// If possible, the property type public Type GetPropertyType() { + // The start segment type is always the target type + if (IsStartSegment) + return DataModelPath.Target.GetType(); + // Prefer basing the type on the property info var propertyInfo = GetPropertyInfo(); var type = propertyInfo?.PropertyType; @@ -131,27 +160,35 @@ namespace Artemis.Core internal Expression Initialize(ParameterExpression parameter, Expression expression, Expression nullCondition) { - var previousValue = Previous != null ? Previous.GetValue() : DataModelPath.Target; - if (previousValue == null) + if (IsStartSegment) { - Type = DataModelPathSegmentType.Invalid; - return null; + Type = DataModelPathSegmentType.Static; + return CreateExpression(parameter, expression, nullCondition); } - // Determine this segment's type by looking for a dynamic data model with the identifier - if (previousValue is DataModel dataModel) + var previousType = Previous.GetPropertyType(); + if (previousType == null) { - var hasDynamicDataModel = dataModel.DynamicDataModels.TryGetValue(Identifier, out var dynamicDataModel); + Type = DataModelPathSegmentType.Invalid; + return CreateExpression(parameter, expression, nullCondition); + } + + // Prefer static since that's faster + DetermineStaticType(previousType); + + // If no static type could be found, check if this is a data model and if so, look for a dynamic type + if (Type == DataModelPathSegmentType.Invalid && typeof(DataModel).IsAssignableFrom(previousType)) + { + var dataModel = (DataModel) Previous.GetValue(); + // Cannot determine a dynamic type on a null data model, leave the segment invalid + if (dataModel == null) + return CreateExpression(parameter, expression, nullCondition); + // If a dynamic data model is found the use that + var hasDynamicDataModel = dataModel.DynamicDataModels.TryGetValue(Identifier, out var dynamicDataModel); if (hasDynamicDataModel) DetermineDynamicType(dynamicDataModel); - // Otherwise look for a static type - else - DetermineStaticType(previousValue); } - // Only data models can have dynamic types so if it is something else, its always going to be static - else - DetermineStaticType(previousValue); return CreateExpression(parameter, expression, nullCondition); } @@ -160,13 +197,17 @@ namespace Artemis.Core { if (Type == DataModelPathSegmentType.Invalid) { + _accessorLambda = null; Accessor = null; return null; } Expression accessorExpression; + // A start segment just accesses the target + if (IsStartSegment) + accessorExpression = expression; // A static segment just needs to access the property or filed - if (Type == DataModelPathSegmentType.Static) + else if (Type == DataModelPathSegmentType.Static) accessorExpression = Expression.PropertyOrField(expression, Identifier); // A dynamic segment calls the generic method DataModel.DynamicChild and provides the identifier as an argument else @@ -179,7 +220,7 @@ namespace Artemis.Core ); } - Accessor = Expression.Lambda>( + _accessorLambda = Expression.Lambda>( // Wrap with a null check Expression.Condition( nullCondition, @@ -187,8 +228,8 @@ namespace Artemis.Core Expression.Convert(Expression.Default(accessorExpression.Type), typeof(object)) ), parameter - ).Compile(); - + ); + Accessor = null; return accessorExpression; } @@ -198,9 +239,8 @@ namespace Artemis.Core DynamicDataModelType = dynamicDataModel.GetType(); } - private void DetermineStaticType(object previous) + private void DetermineStaticType(Type previousType) { - var previousType = previous.GetType(); var property = previousType.GetProperty(Identifier, BindingFlags.Public | BindingFlags.Instance); Type = property == null ? DataModelPathSegmentType.Invalid : DataModelPathSegmentType.Static; } diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs index 5a10805ef..5d61efb43 100644 --- a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs +++ b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs @@ -64,7 +64,8 @@ namespace Artemis.Core.DataModelExpansions throw new ArgumentNullException(nameof(dynamicDataModel)); if (key == null) throw new ArgumentNullException(nameof(key)); - + if (key.Contains('.')) + throw new ArtemisCoreException("The provided key contains an illegal character (.)"); if (_dynamicDataModels.ContainsKey(key)) { throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " + @@ -87,7 +88,7 @@ namespace Artemis.Core.DataModelExpansions dynamicDataModel.PluginInfo = PluginInfo; dynamicDataModel.DataModelDescription = new DataModelPropertyAttribute { - Name = name ?? key.Humanize(), + Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name, Description = description }; _dynamicDataModels.Add(key, dynamicDataModel); diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index 0d9388737..c5d8abbcf 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -105,6 +105,12 @@ namespace Artemis.Core InternalDisablePlugin(); OnPluginDisabled(); } + // A failed load is still enabled in plugin info (to avoid disabling it permanently after a fail) + // update even that when manually disabling + else if (!enable && !Enabled) + { + PluginInfo.Enabled = false; + } } internal virtual void InternalEnablePlugin() diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs index e01c168c7..b252793c3 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertiesViewModel.cs @@ -1,5 +1,4 @@ using System; -using Artemis.Core; using Artemis.Core.DataModelExpansions; using Artemis.UI.Shared.Services; @@ -11,8 +10,11 @@ namespace Artemis.UI.Shared private int _index; private Type _listType; - public DataModelListPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath) + public DataModelListPropertiesViewModel(DataModel dataModel, object listItem) : base(null, null, null) { + DataModel = dataModel; + ListType = listItem.GetType(); + DisplayValue = listItem; } public int Index @@ -42,7 +44,7 @@ namespace Artemis.UI.Shared return; ListType = DisplayValue.GetType(); - PopulateProperties(dataModelUIService); + PopulateProperties(dataModelUIService, DisplayValue); foreach (var dataModelVisualizationViewModel in Children) dataModelVisualizationViewModel.Update(dataModelUIService); } @@ -51,5 +53,11 @@ namespace Artemis.UI.Shared { return DisplayValue; } + + /// + public override string ToString() + { + return $"[List item {Index}] {DisplayPath ?? Path}"; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs index 5a49bf5cb..2001cc4d8 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs @@ -1,5 +1,4 @@ using System; -using Artemis.Core; using Artemis.Core.DataModelExpansions; using Artemis.UI.Shared.Services; @@ -10,8 +9,19 @@ namespace Artemis.UI.Shared private int _index; private Type _listType; - public DataModelListPropertyViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath) + public DataModelListPropertyViewModel(DataModel dataModel, object listItem, DataModelDisplayViewModel displayViewModel) : base(null, null, null) { + DataModel = dataModel; + ListType = listItem.GetType(); + DisplayValue = listItem; + DisplayViewModel = displayViewModel; + } + + public DataModelListPropertyViewModel(DataModel dataModel, object listItem) : base(null, null, null) + { + DataModel = dataModel; + ListType = listItem.GetType(); + DisplayValue = listItem; } public int Index @@ -43,5 +53,11 @@ namespace Artemis.UI.Shared ListType = DisplayValue.GetType(); UpdateDisplayParameters(); } + + /// + public override string ToString() + { + return $"[List item {Index}] {DisplayPath ?? Path} - {DisplayValue}"; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs index 73274ca9c..8a38f5f92 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs @@ -105,20 +105,25 @@ namespace Artemis.UI.Shared protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, object listItem) { var listType = listItem.GetType(); - var path = new DataModelPath(listItem, ""); // If a display VM was found, prefer to use that in any case var typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType); if (typeViewModel != null) - return new DataModelListPropertyViewModel(DataModel, this, path) {DisplayViewModel = typeViewModel}; + return new DataModelListPropertyViewModel(DataModel, listItem, typeViewModel); // For primitives, create a property view model, it may be null that is fine if (listType.IsPrimitive || listType.IsEnum || listType == typeof(string)) - return new DataModelListPropertyViewModel(DataModel, this, path) {ListType = listType}; + return new DataModelListPropertyViewModel(DataModel, listItem); // For other value types create a child view model if (listType.IsClass || listType.IsStruct()) - return new DataModelListPropertiesViewModel(DataModel, this, path) {ListType = listType}; + return new DataModelListPropertiesViewModel(DataModel, listItem); return null; } + + /// + public override string ToString() + { + return $"[List] {DisplayPath ?? Path} - {List.Count} item(s)"; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs index 4221cde2d..90da4e1d5 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs @@ -13,7 +13,7 @@ namespace Artemis.UI.Shared public override void Update(IDataModelUIService dataModelUIService) { // Always populate properties - PopulateProperties(dataModelUIService); + PopulateProperties(dataModelUIService, null); // Only update children if the parent is expanded if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel) @@ -28,9 +28,15 @@ namespace Artemis.UI.Shared return Parent.IsRootViewModel ? DataModel : base.GetCurrentValue(); } - protected override int GetChildDepth() + internal override int GetChildDepth() { return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1; } + + /// + public override string ToString() + { + return DisplayPath ?? Path; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs index 869c3bfc8..6938f7a6f 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs @@ -50,5 +50,11 @@ namespace Artemis.UI.Shared { DisplayViewModel?.UpdateValue(DisplayValue); } + + /// + public override string ToString() + { + return $"[{DisplayValueType.Name}] {DisplayPath ?? Path} - {DisplayValue}"; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 058f6adec..ec2b2fc06 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using Artemis.Core; @@ -136,21 +137,6 @@ namespace Artemis.UI.Shared IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum); } - public DataModelVisualizationViewModel GetChildForCondition(DataModelConditionPredicate predicate, DataModelConditionSide side) - { - if (side == DataModelConditionSide.Left) - { - if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null) - return null; - - return GetChildByPath(predicate.LeftDataModel.PluginInfo.Guid, predicate.LeftPropertyPath); - } - - if (predicate.RightDataModel == null || predicate.RightPropertyPath == null) - return null; - return GetChildByPath(predicate.RightDataModel.PluginInfo.Guid, predicate.RightPropertyPath); - } - public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath) { if (DataModel.PluginInfo.Guid != dataModelGuid) @@ -183,31 +169,44 @@ namespace Artemis.UI.Shared return null; } - protected virtual int GetChildDepth() + internal virtual int GetChildDepth() { return 0; } - protected void PopulateProperties(IDataModelUIService dataModelUIService) + internal void PopulateProperties(IDataModelUIService dataModelUIService, object overrideValue) { - if (IsRootViewModel) + if (IsRootViewModel && overrideValue == null) return; + Type modelType; + if (overrideValue != null) + modelType = overrideValue.GetType(); + else if (Parent.IsRootViewModel) + modelType = DataModel.GetType(); + else + modelType = DataModelPath.GetPropertyType(); + // Add missing static children - var modelType = Parent.IsRootViewModel ? DataModel.GetType() : DataModelPath.GetPropertyType(); foreach (var propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var childPath = Path != null ? $"{Path}.{propertyInfo.Name}" : propertyInfo.Name; if (Children.Any(c => c.Path != null && c.Path.Equals(childPath))) continue; + if (propertyInfo.GetCustomAttribute() != null) + continue; - var child = CreateChild(dataModelUIService, childPath, GetChildDepth()); + var child = CreateChild(dataModelUIService, childPath, GetChildDepth(), overrideValue); if (child != null) Children.Add(child); } // Remove static children that should be hidden - var hiddenProperties = DataModel.GetHiddenProperties(); + ReadOnlyCollection hiddenProperties; + if (overrideValue != null && overrideValue is DataModel overrideValueDataModel) + hiddenProperties = overrideValueDataModel.GetHiddenProperties(); + else + hiddenProperties = DataModel.GetHiddenProperties(); foreach (var hiddenProperty in hiddenProperties) { var childPath = Path != null ? $"{Path}.{hiddenProperty.Name}" : hiddenProperty.Name; @@ -217,7 +216,11 @@ namespace Artemis.UI.Shared } // Add missing dynamic children - var value = Parent.IsRootViewModel ? DataModel : DataModelPath.GetValue(); + object value; + if (overrideValue != null) + value = overrideValue; + else + value = Parent.IsRootViewModel ? DataModel : DataModelPath.GetValue(); if (value is DataModel dataModel) { foreach (var kvp in dataModel.DynamicDataModels) @@ -226,7 +229,7 @@ namespace Artemis.UI.Shared if (Children.Any(c => c.Path != null && c.Path.Equals(childPath))) continue; - var child = CreateChild(dataModelUIService, childPath, GetChildDepth()); + var child = CreateChild(dataModelUIService, childPath, GetChildDepth(), overrideValue); if (child != null) Children.Add(child); } @@ -238,12 +241,12 @@ namespace Artemis.UI.Shared Children.RemoveRange(toRemoveDynamic); } - protected DataModelVisualizationViewModel CreateChild(IDataModelUIService dataModelUIService, string path, int depth) + private DataModelVisualizationViewModel CreateChild(IDataModelUIService dataModelUIService, string path, int depth, object overrideValue) { if (depth > MaxDepth) return null; - var dataModelPath = new DataModelPath(DataModel, path); + var dataModelPath = new DataModelPath(overrideValue ?? DataModel, path); if (!dataModelPath.IsValid) return null;