diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
index ab26313736..5e2fa5d405 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using System.Linq.Expressions;
using System.Reflection;
namespace Volo.Abp.Reflection;
@@ -229,4 +231,47 @@ public static class ReflectionHelper
return publicConstants.ToArray();
}
+
+ ///
+ /// Checks whether the property is nullable, including nullable reference types (NRT).
+ ///
+ /// Property info to check
+ public static bool IsNullable(PropertyInfo propertyInfo)
+ {
+ if (TypeHelper.IsNullable(propertyInfo.PropertyType))
+ {
+ return true;
+ }
+
+#if NET6_0_OR_GREATER
+ var nullabilityInfoContext = new NullabilityInfoContext();
+ var nullabilityInfo = nullabilityInfoContext.Create(propertyInfo);
+ return nullabilityInfo.ReadState == NullabilityState.Nullable;
+#else
+ var attr = propertyInfo.GetCustomAttributes().FirstOrDefault(a => a.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
+ if (attr != null)
+ {
+ var getter = NullableGetterCache.GetOrAdd(attr.GetType(), CreateNullableAccessor);
+ return getter(attr)?[0] == 2;
+ }
+ return false;
+#endif
+ }
+
+ private static readonly ConcurrentDictionary> NullableGetterCache = new ();
+
+ private static Func