Repository: AvaloniaUI/Avalonia

Issue:
11.1.0-beta1 stackoverflow on property set (SetProperty), works in 11.0.10
### Describe the bug

This code below is stack overflowing on 11.1.0-beta1. If I roll back to 11.0.10 the code works fine.

``` CSharp
[Required(ErrorMessage = "Name is required!")]
public string Name
{
	get => _name;
	set => SetProperty(ref _name, value, true);
}
```

### To Reproduce

Call ObservableValidator.SetProperty in 11.1.0-beta1 leads to stackoverflow.

``` CSharp
[Required(ErrorMessage = "Name is required!")]
```

I think it's due to the required attribute but I'm not sure.

### Expected behavior

The code should not stackoverflow?

### Avalonia version

11.1.0-beta1

### OS

Windows

### Additional context

_No response_


Hints:
Stacktrace (of stackoverflow) and SetProperty implementation are required to help here. Or minimal repro project.
@BobbyCannon 

I have the same issue, check if your App.axaml.cs's `OnFrameworkInitializationCompleted()` has following code:

```csharp
BindingPlugins.DataValidators.RemoveAt(0);
```

@maxkatz6 Maybe consider adding this note in the Release message
@laolarou726 what exactly should be added to the release message?
> @laolarou726 what exactly should be added to the release message?

I mean this line of code. When I upgrading the Avalonia version for my older project. I also noticed this issue. After some investigation, I noticed this line of code has been added to the latest template project. So maybe add this notice some where in the document or Release message might be a good idea?
minimal repro to paste into `src/Samples/Sandbox`

```cs
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }

....

    public partial class ViewModel : ObservableValidator
    {
        // [Required(ErrorMessage = "Name is required!")] 
        [ObservableProperty] 
        [NotifyDataErrorInfo]
        private string? _Name;
    }
```

and 

```xml
  <TextBox Text="{Binding Name}" />
```

--------

seems to stuck around here: 
![image](https://github.com/AvaloniaUI/Avalonia/assets/47110241/ec21199b-5e7c-4c87-a073-2deabfa332bc)

... and here: 
![image](https://github.com/AvaloniaUI/Avalonia/assets/47110241/1ae23425-95cb-4f38-8148-349ee81029db)

I had commented that line of code then removed it.

![image](https://github.com/AvaloniaUI/Avalonia/assets/840590/d07e04bb-3eb8-45b8-b65b-10d2050edd23)

I was concerned about two things...
1. Why 0 index, what it that changes in an upgrade?
2. What was being removed.

This morning I wrote this...

``` CSharp
// Removes {Avalonia.Data.Core.Plugins.DataAnnotationsValidationPlugin}
var found = BindingPlugins.DataValidators.FirstOrDefault(x => x is DataAnnotationsValidationPlugin);
if (found != null)
{
	BindingPlugins.DataValidators.Remove(found);
}
```

It won't break if the validator moves to a different location.

Also is this still just a bug with the DataAnnotationsValidationPlugin?

I noticed while debugging this value adds { Value: } around the previous value.
@BobbyCannon https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins 

However, this is still a regression imo, so keep this ticket open for now. 

Patch:
diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs
index 0c940a14610..a3a2e0c2b05 100644
--- a/src/Avalonia.Base/Data/BindingNotification.cs
+++ b/src/Avalonia.Base/Data/BindingNotification.cs
@@ -57,7 +57,6 @@ public class BindingNotification
         /// <param name="value">The binding value.</param>
         public BindingNotification(object? value)
         {
-            Debug.Assert(value is not BindingNotification);
             _value = value;
         }
 
diff --git a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs
index e8e6633ab73..8b53190f869 100644
--- a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs
+++ b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs
@@ -187,13 +187,20 @@ protected void SetValue(object? valueOrNotification)
             else if (notification.ErrorType == BindingErrorType.DataValidationError)
             {
                 if (notification.HasValue)
-                    SetValue(notification.Value, notification.Error);
+                {
+                    if (notification.Value is BindingNotification n)
+                        SetValue(n);
+                    else
+                        SetValue(notification.Value, notification.Error);
+                }
                 else
+                {
                     SetDataValidationError(notification.Error!);
+                }
             }
             else
             {
-                SetValue(notification.Value, null);
+                SetValue(notification.Value);
             }
         }
         else


Test patch:
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs
index bedf24bd6d7..8e1865f2092 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs
@@ -1,6 +1,7 @@
 ﻿using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using Avalonia.Data;
 using Avalonia.UnitTests;
 using Xunit;
@@ -271,6 +272,39 @@ public void Updates_Data_Validation_For_Null_Value_In_Property_Chain()
         GC.KeepAlive(data);
     }
 
+    [Fact]
+    public void Updates_Data_Validation_For_Required_DataAnnotation()
+    {
+        var data = new DataAnnotationsViewModel();
+        var target = CreateTargetWithSource(
+            data,
+            o => o.RequiredString,
+            enableDataValidation: true);
+
+        AssertBindingError(
+            target,
+            TargetClass.StringProperty,
+            new DataValidationException("String is required!"),
+            BindingErrorType.DataValidationError);
+    }
+
+    [Fact]
+    public void Handles_Indei_And_DataAnnotations_On_Same_Class()
+    {
+        // Issue #15201
+        var data = new IndeiDataAnnotationsViewModel();
+        var target = CreateTargetWithSource(
+            data,
+            o => o.RequiredString,
+            enableDataValidation: true);
+
+        AssertBindingError(
+            target,
+            TargetClass.StringProperty,
+            new DataValidationException("String is required!"),
+            BindingErrorType.DataValidationError);
+    }
+
     public class ExceptionViewModel : NotifyingBase
     {
         private int _mustBePositive;
@@ -341,6 +375,42 @@ public IndeiViewModel? Inner
         public override IEnumerable? GetErrors(string propertyName) => null;
     }
 
+    private class DataAnnotationsViewModel : NotifyingBase
+    {
+        private string? _requiredString;
+
+        [Required(ErrorMessage = "String is required!")]
+        public string? RequiredString
+        {
+            get { return _requiredString; }
+            set { _requiredString = value; RaisePropertyChanged(); }
+        }
+    }
+
+    private class IndeiDataAnnotationsViewModel : IndeiBase
+    {
+        private string? _requiredString;
+
+        [Required(ErrorMessage = "String is required!")]
+        public string? RequiredString
+        {
+            get { return _requiredString; }
+            set { _requiredString = value; RaisePropertyChanged(); }
+        }
+
+        public override bool HasErrors => RequiredString is null;
+        
+        public override IEnumerable? GetErrors(string propertyName)
+        {
+            if (propertyName == nameof(RequiredString) && RequiredString is null)
+            {
+                return new[] { "String is required!" };
+            }
+
+            return null;
+        }
+    }   
+
     private static void AssertNoError(TargetClass target, AvaloniaProperty property)
     {
         Assert.False(target.BindingNotifications.TryGetValue(property, out var notification));