Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ This release gives some ❤️ to AvaloniaUI and other frameworks which use ILis
* Add `IReadOnlyList<T>` interface to `EntityCollection` and `EntitySet` and `LoadResult`
* Add `IList<T>` interface to `EntityCollection`
* Nullability annotations for `EntitySet` and `EntityCollection`
* Nullability annotations for many parts of the most used public API
Comment thread
Daniel-Svensson marked this conversation as resolved.

**Other**
* Fixed build pipeline problems after updating to VS 2016
### Server

* Added Nullability annotations for a few core types of public API

Comment thread
coderabbitai[bot] marked this conversation as resolved.
### Other

* Fixed build pipeline problems after updating to VS 2026
* Improved polyfills to allow modernization of the codebase
* Analyzer fixes
* Fix CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()'
* Fix MSTEST****: varius test related
Comment thread
Daniel-Svensson marked this conversation as resolved.
Expand Down
3 changes: 2 additions & 1 deletion src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ dotnet_diagnostic.VSTHRD010.severity = suggestion
dotnet_diagnostic.CA1510.severity = none
# CA1863: Cache a 'CompositeFormat' for repeated use in this formatting operation
dotnet_diagnostic.CA1863.severity = none

dotnet_diagnostic.CA1826.severity = warning
dotnet_code_quality.CA1826.exclude_ordefault_methods = true

###############################
# C# Coding Conventions #
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,25 @@
using System.Linq;
using System.Runtime.Serialization;

#nullable enable

namespace OpenRiaServices.Client
{
public abstract partial class ComplexObject : INotifyDataErrorInfo
{
private EventHandler<DataErrorsChangedEventArgs> _errorsChangedHandler;
private EventHandler<DataErrorsChangedEventArgs>? _errorsChangedHandler;
/// <summary>
/// Raises the event whenever validation errors have changed for a property.
/// </summary>
/// <param name="propertyName">The property whose errors have changed.</param>
private void RaiseValidationErrorsChanged(string propertyName)
private void RaiseValidationErrorsChanged(string? propertyName)
{
this._errorsChangedHandler?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
/// <summary>
/// Explicitly implement the <see cref="INotifyDataErrorInfo.ErrorsChanged"/> event.
/// </summary>
event EventHandler<DataErrorsChangedEventArgs> INotifyDataErrorInfo.ErrorsChanged
event EventHandler<DataErrorsChangedEventArgs>? INotifyDataErrorInfo.ErrorsChanged
{
add { this._errorsChangedHandler += value; }
remove { this._errorsChangedHandler -= value; }
Expand All @@ -42,7 +44,7 @@ event EventHandler<DataErrorsChangedEventArgs> INotifyDataErrorInfo.ErrorsChange
/// or type-level errors when <paramref name="propertyName"/> is
/// <c>null</c> or empty.
/// </returns>
IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
IEnumerable INotifyDataErrorInfo.GetErrors(string? propertyName)
{
IEnumerable<ValidationResult> results;
if (string.IsNullOrEmpty(propertyName))
Expand Down
70 changes: 39 additions & 31 deletions src/OpenRiaServices.Client/Framework/ComplexObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Runtime.Serialization;
using OpenRiaServices.Client.Internal;

#nullable enable

namespace OpenRiaServices.Client
{
/// <summary>
Expand All @@ -17,17 +19,17 @@ namespace OpenRiaServices.Client
[DataContract]
public abstract partial class ComplexObject : INotifyPropertyChanged, IEditableObject
{
private PropertyChangedEventHandler _propChangedHandler;
private ComplexObjectValidationResultCollection _validationErrors;
private Dictionary<string, ComplexObject> _trackedInstances;
private Action _onDataMemberChanging;
private Action _onDataMemberChanged;
private Action<string, IEnumerable<ValidationResult>> _onMemberValidationChanged;
private EditSession _editSession;
private object _parent;
private string _parentPropertyName;
private PropertyChangedEventHandler? _propChangedHandler;
private ComplexObjectValidationResultCollection? _validationErrors;
private Dictionary<string, ComplexObject>? _trackedInstances;
private Action? _onDataMemberChanging;
private Action? _onDataMemberChanged;
private Action<string, IEnumerable<ValidationResult>>? _onMemberValidationChanged;
private EditSession? _editSession;
private object? _parent;
private string? _parentPropertyName;
private bool _isDeserializing;
private MetaType _metaType;
private MetaType? _metaType;

/// <summary>
/// Gets the map of child ComplexObject instances currently being
Expand All @@ -49,14 +51,14 @@ private Dictionary<string, ComplexObject> TrackedInstances
/// Gets the parent entity if this complex type instance is hosted
/// by an entity. May return null.
/// </summary>
private Entity Entity
private Entity? Entity
{
get
{
object currParent = this._parent;
object? currParent = this._parent;
while (currParent != null)
{
Entity entity = currParent as Entity;
Entity? entity = currParent as Entity;
if (entity != null)
{
return entity;
Expand Down Expand Up @@ -90,6 +92,11 @@ internal MetaType MetaType
/// Gets a value indicating whether this complex type instance is currently
/// attached to a parent.
/// </summary>
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_parent))]
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_parentPropertyName))]
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_onDataMemberChanging))]
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_onDataMemberChanged))]
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_onMemberValidationChanged))]
internal bool IsAttached
{
get
Expand Down Expand Up @@ -206,7 +213,7 @@ internal void Detach()
/// <summary>
/// Event raised whenever a <see cref="ComplexObject"/> property has changed.
/// </summary>
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged
{
add { this._propChangedHandler += value; }
remove { this._propChangedHandler -= value; }
Expand Down Expand Up @@ -278,7 +285,7 @@ private void AttachComplexObjectInstance(MetaMember metaMember)
// First check if the parent has an existing instance attached for this
// property and detach if necessary.
string memberName = metaMember.Name;
ComplexObject prevInstance = null;
ComplexObject? prevInstance = null;
if (this.TrackedInstances.TryGetValue(memberName, out prevInstance))
{
prevInstance.Detach();
Expand Down Expand Up @@ -366,7 +373,7 @@ private void OnMemberValidationChanged(string propertyName, IEnumerable<Validati
this.NotifyParentMemberValidationChanged(propertyName, validationResults);
}

private void NotifyParentMemberValidationChanged(string propertyName, IEnumerable<ValidationResult> validationResults)
private void NotifyParentMemberValidationChanged(string? propertyName, IEnumerable<ValidationResult> validationResults)
{
if (this.IsAttached)
{
Expand Down Expand Up @@ -406,7 +413,7 @@ protected void ValidateProperty(string propertyName, object value)
// if this instance is currently being deserialized, or if it is hosted by an
// entity that is being deserialized or is having state applied to it, skip
// validation
Entity rootEntity = this.Entity;
Entity? rootEntity = this.Entity;
if (this.IsDeserializing || (rootEntity != null && (rootEntity.IsDeserializing || rootEntity.IsApplyingState)))
{
return;
Expand Down Expand Up @@ -440,7 +447,7 @@ protected void ValidateProperty(string propertyName, object value)
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Property_Is_ReadOnly, propertyName));
}

ComplexObject complexObject = value as ComplexObject;
ComplexObject? complexObject = value as ComplexObject;
if (complexObject != null && complexObject.IsAttached)
{
throw new InvalidOperationException(Resource.ComplexType_InstancesCannotBeShared);
Expand Down Expand Up @@ -482,7 +489,7 @@ protected virtual void ValidateProperty(ValidationContext validationContext, obj
Validator.TryValidateProperty(value, validationContext, validationResults);

// Process the validation the errors for this property
this.OnMemberValidationChanged(validationContext.MemberName, validationResults);
this.OnMemberValidationChanged(validationContext.MemberName!, validationResults);
}

/// <summary>
Expand All @@ -498,8 +505,8 @@ private ValidationContext CreateValidationContext()
{
// Get the validation context from the entity container if available,
// otherwise create a new context.
ValidationContext parentContext = null;
Entity rootEntity = this.Entity;
ValidationContext? parentContext = null;
Entity? rootEntity = this.Entity;
if (rootEntity != null && rootEntity.EntitySet != null && rootEntity.EntitySet.EntityContainer != null)
{
parentContext = rootEntity.EntitySet.EntityContainer.ValidationContext;
Expand Down Expand Up @@ -540,16 +547,15 @@ private void CheckForCycles(object candidateParent)
// Walk up the containment chain searching for this instance.
// If found, setting the parent of this instance to the candidate
// would result in a cycle.
object currParent = candidateParent;
object? currParent = candidateParent;
while (currParent != null)
{
if (currParent == this)
{
throw new InvalidOperationException(Resource.CyclicReferenceError);
}

ComplexObject complexParent = currParent as ComplexObject;
if (complexParent != null)
if (currParent is ComplexObject complexParent)
{
currParent = complexParent._parent;
}
Expand Down Expand Up @@ -586,6 +592,7 @@ internal void VerifyNotEditing()
/// edit session in progress for this instance. This is the case when
/// BeginEdit has been called, but EndEdit/CancelEdit have not.
/// </summary>
[System.Diagnostics.CodeAnalysis.MemberNotNullWhen(true, nameof(_editSession))]
internal bool IsEditing
{
get
Expand All @@ -594,6 +601,7 @@ internal bool IsEditing
}
}

/// <inheritdoc cref="Entity.IsMergingState"/>
protected internal bool IsMergingState { get; set; }

/// <summary>
Expand Down Expand Up @@ -681,7 +689,7 @@ protected void EndEdit()
private class EditSession
{
private readonly ComplexObject _instance;
private IDictionary<string, object> _snapshot;
private IDictionary<string, object?>? _snapshot;
private readonly ValidationResult[] _validationErrors;

private EditSession(ComplexObject instance)
Expand Down Expand Up @@ -777,19 +785,19 @@ protected override void OnRemove(ValidationResult item)

// search (by value) for the item in our parents error collection
ICollection<ValidationResult> resultCollection = GetValidationResults(this._complexObject._parent);
ValidationResultEqualityComparer comparer = new ValidationResultEqualityComparer();
item = resultCollection.FirstOrDefault(p => comparer.Equals(p, item));
ValidationResultEqualityComparer comparer = ValidationResultEqualityComparer.Instance;
var existing = resultCollection.FirstOrDefault(p => comparer.Equals(p, item));

if (item != null)
if (existing != null)
{
resultCollection.Remove(item);
resultCollection.Remove(existing);
}
}
}

private static ICollection<ValidationResult> GetValidationResults(object parent)
{
ComplexObject complexParent = parent as ComplexObject;
ComplexObject? complexParent = parent as ComplexObject;
if (complexParent != null)
{
return complexParent.ValidationErrors;
Expand Down Expand Up @@ -820,7 +828,7 @@ protected override void OnHasErrorsChanged()
{
this._complexObject.RaisePropertyChanged(nameof(HasValidationErrors));
}
protected override void OnPropertyErrorsChanged(string propertyName)
protected override void OnPropertyErrorsChanged(string? propertyName)
{
this._complexObject.RaiseValidationErrorsChanged(propertyName);
}
Expand Down
Loading
Loading