Browse Source

Add IsNullable property detection for PropertyInfo

pull/24335/head
maliming 2 months ago
parent
commit
ddfaf82496
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 45
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
  2. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  3. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  4. 1
      framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj
  5. 57
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs

45
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();
}
/// <summary>
/// Checks whether the property is nullable, including nullable reference types (NRT).
/// </summary>
/// <param name="propertyInfo">Property info to check</param>
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<Type, Func<object, byte[]?>> NullableGetterCache = new ();
private static Func<object, byte[]?> CreateNullableAccessor(Type attrType)
{
var param = Expression.Parameter(typeof(object), "attr");
var casted = Expression.Convert(param, attrType);
var flagsField = attrType.GetField("NullableFlags");
if (flagsField == null)
{
return _ => null;
}
var access = Expression.Field(casted, flagsField);
return Expression.Lambda<Func<object, byte[]?>>(access, param).Compile();
}
}

6
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs

@ -116,12 +116,6 @@ public static class TypeHelper
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
public static bool IsNullableOrNotValueType(Type type)
{
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) ||
!type.IsValueType;
}
public static bool IsNullableEnum(Type type)
{
return type.IsGenericType &&

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs

@ -42,7 +42,7 @@ public class PropertyApiDescriptionModel
Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType),
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType),
IsRequired = customAttributes.OfType<RequiredAttribute>().Any() || propertyInfo.GetCustomAttributesData().Any(attr => attr.AttributeType.Name == "RequiredMemberAttribute"),
IsNullable = TypeHelper.IsNullableOrNotValueType(propertyInfo.PropertyType),
IsNullable = ReflectionHelper.IsNullable(propertyInfo),
Minimum = customAttributes.OfType<RangeAttribute>().Select(x => x.Minimum).FirstOrDefault()?.ToString(),
Maximum = customAttributes.OfType<RangeAttribute>().Select(x => x.Maximum).FirstOrDefault()?.ToString(),
MinLength = customAttributes.OfType<MinLengthAttribute>().FirstOrDefault()?.Length ?? customAttributes.OfType<StringLengthAttribute>().FirstOrDefault()?.MinimumLength,

1
framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj

@ -5,6 +5,7 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace />
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

57
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs

@ -84,8 +84,65 @@ public class ReflectionHelper_Tests
constants.ShouldNotBeEmpty();
constants.Except(IdentityPermissions.GetAll()).Count().ShouldBe(0);
}
[Fact]
public void IsNullable_Test()
{
var prop1 = typeof(TestClass).GetProperty(nameof(TestClass.Prop1))!;
ReflectionHelper.IsNullable(prop1).ShouldBeFalse();
var prop2 = typeof(TestClass).GetProperty(nameof(TestClass.Prop2))!;
ReflectionHelper.IsNullable(prop2).ShouldBeTrue();
var prop3 = typeof(TestClass).GetProperty(nameof(TestClass.Prop3))!;
ReflectionHelper.IsNullable(prop3).ShouldBeFalse();
var prop4 = typeof(TestClass).GetProperty(nameof(TestClass.Prop4))!;
ReflectionHelper.IsNullable(prop4).ShouldBeTrue();
var prop5 = typeof(TestClass).GetProperty(nameof(TestClass.Prop5))!;
ReflectionHelper.IsNullable(prop5).ShouldBeFalse();
var prop6 = typeof(TestClass).GetProperty(nameof(TestClass.Prop6))!;
ReflectionHelper.IsNullable(prop6).ShouldBeTrue();
var prop7 = typeof(TestClass).GetProperty(nameof(TestClass.Prop7))!;
ReflectionHelper.IsNullable(prop7).ShouldBeFalse();
var prop8 = typeof(TestClass).GetProperty(nameof(TestClass.Prop8))!;
ReflectionHelper.IsNullable(prop8).ShouldBeTrue();
var prop9 = typeof(TestClass).GetProperty(nameof(TestClass.Prop9))!;
ReflectionHelper.IsNullable(prop9).ShouldBeFalse();
var prop10 = typeof(TestClass).GetProperty(nameof(TestClass.Prop10))!;
ReflectionHelper.IsNullable(prop10).ShouldBeTrue();
var prop11 = typeof(TestClass).GetProperty(nameof(TestClass.Prop11))!;
ReflectionHelper.IsNullable(prop11).ShouldBeFalse();
var prop12 = typeof(TestClass).GetProperty(nameof(TestClass.Prop12))!;
ReflectionHelper.IsNullable(prop12).ShouldBeTrue();
}
}
public class TestClass
{
public string Prop1 { get; set; } = null!;
public string? Prop2 { get; set; } = null!;
public required string Prop3 { get; set; }
public required string? Prop4 { get; set; }
public int Prop5 { get; set; }
public int? Prop6 { get; set; }
public required int Prop7 { get; set; }
public required int? Prop8 { get; set; }
public int[] Prop9 { get; set; } = null!;
public int[]? Prop10 { get; set; }
public required int[] Prop11 { get; set; }
public required int[]? Prop12 { get; set; }
}
public class BaseRole
{

Loading…
Cancel
Save