Browse Source

Merge pull request #24405 from abpframework/auto-merge/rel-10-0/4199

Merge branch dev with rel-10.0
pull/24413/head
Ma Liming 6 months ago
committed by GitHub
parent
commit
13889649ed
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 45
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
  2. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  3. 4
      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
  6. 1
      npm/ng-packs/packages/schematics/src/models/api-definition.ts
  7. 9
      npm/ng-packs/packages/schematics/src/utils/model.ts

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();
}
}

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

@ -86,7 +86,7 @@ public static class TypeHelper
{
return default;
}
if (IsPrimitiveExtended(typeof(TProperty), includeEnums: true))
{
var conversionType = typeof(TProperty);

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

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Volo.Abp.Http.ProxyScripting.Configuration;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling;
@ -29,6 +30,8 @@ public class PropertyApiDescriptionModel
public string? Regex { get; set; }
public bool IsNullable { get; set; }
public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo)
{
var customAttributes = propertyInfo.GetCustomAttributes(true);
@ -39,6 +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 = 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
{

1
npm/ng-packs/packages/schematics/src/models/api-definition.ts

@ -20,6 +20,7 @@ export interface PropertyDef {
type: string;
typeSimple: string;
isRequired: boolean;
isNullable: boolean;
}
export interface Module {

9
npm/ng-packs/packages/schematics/src/utils/model.ts

@ -153,6 +153,10 @@ export function createImportRefToInterfaceReducerCreator(params: ModelGeneratorP
type = simplifyType(prop.type);
}
if (prop.isNullable) {
type = `${type} | null`;
}
const refs = parseType(prop.type).reduce(
(acc: string[], r) => acc.concat(parseGenerics(r).toGenerics()),
[],
@ -186,10 +190,7 @@ export function createRefToImportReducerCreator(params: ModelGeneratorParams) {
}
function isOptionalProperty(prop: PropertyDef) {
return (
prop.typeSimple.endsWith('?') ||
((prop.typeSimple === 'string' || prop.typeSimple.includes('enum')) && !prop.isRequired)
);
return !prop.isRequired;
}
export function parseBaseTypeWithGenericTypes(type: string): string[] {

Loading…
Cancel
Save