天天看點

【Abp VNext】實戰入門(十四):【1】應用層 —— 對象間循環遞歸引用,導緻Json序列化異常一、前言二、對象遞歸引用Json序列化異常案例三、總結

文章目錄

  • 一、前言
  • 二、對象遞歸引用Json序列化異常案例
    • 1、領域層對象關系:
    • 2、應用層對象關系:
    • 3.1、關聯查詢方式:AbpVnext
    • 3.2、關聯查詢方式:Abp
    • 4、查詢結果Json序列化異常:
    • 5、解決方案:
  • 三、總結

一、前言

應用層涉及到從資料庫提取資料記錄,轉換成對應的DTO,然後架構自動Json序列化,傳回前端;

在Domain領域層建立的領域對象DO通常帶有關系綁定,1:1 或者1:N , 自然Application應用層的資料傳輸對象 DTO也會帶有與之對應關系的DTO;

當查詢A記錄 想要把A記錄關聯的B記錄也一并查詢并傳回的時候,記錄查詢雖然成功,但是在進行Json序列化的時候會提示要轉換的對象出現循環遞歸引用,且超過最大32層循環嵌套深度,直接報JsonException異常。

二、對象遞歸引用Json序列化異常案例

1、領域層對象關系:

//巡航類 Cruise.cs
public class Cruise : Entity<int>, IHasCreationTime, IHasModificationTime
    {      
        /// <summary>
        /// 區域巡檢點清單:1條路徑多個巡檢點
        /// </summary>
        public virtual List<Cruise_Points> Cruise_Points { get; set; }      
    }
    
 //巡檢點:Cruise_Points.cs
 public class Cruise_Points : Entity<int>, IHasModificationTime, IHasCreationTime
    {
        /// <summary>
        /// 預置巡航ID
        /// </summary>
        public virtual int Cruise_Id { get; set; }
        [ForeignKey("Cruise_Id")]
        public virtual Cruise Cruise { get; set; }       
    }

 //巡檢日志:Device_Warns.cs
 public class Device_Warns : Entity<int>, IHasCreationTime, IHasModificationTime
    {
        /// <summary>
        /// 巡檢路徑id:預置巡航/區域巡航
        /// </summary>
        public int? CruiseId { get; set; }
        [ForeignKey("CruiseId")]
        public Cruise Cruise { get; set; }
        /// <summary>
        /// 巡檢點位id:預置巡檢點位/區域巡檢點位
        /// </summary>
        public int? CruisePointId { get; set; }
        [ForeignKey("CruisePointId")]
        public Cruise_Points Cruise_Point { get; set; }
       
    }
           

2、應用層對象關系:

//CruiseDto.cs:巡檢路徑
 public class CruiseDto : EntityDto<int>
    {      
        /// <summary>
        /// 區域巡檢點清單 區域巡檢點清單:1條路徑多個巡檢點
        /// </summary>
        public List<CruisePointDto> Cruise_Points { get; set; }  
    }

//CruisePointDto.cs:巡檢點位
public class CruisePointDto : EntityDto<int>
    {
        /// <summary>
        /// 巡航路徑ID
        /// </summary>
        public virtual int Cruise_Id { get; set; }
        public CruiseDto Cruise { get; set; }
    }
 
 //WarnsDto.cs:告警記錄中包含 巡檢路徑 和巡檢點位
 public class WarnsDto : EntityDto<int>
    {
        /// <summary>
        /// 巡檢路徑id:預置巡航/區域巡航
        /// </summary>
        public int CruiseId { get; set; }
        /// <summary>
        /// 預置巡航記錄
        /// </summary>
        public virtual CruiseDto Cruise { get; set; }
        /// <summary>
        /// 巡檢點位id:預置巡檢點位/區域巡檢點位
        /// </summary>
        public int CruisePointId { get; set; }
        /// <summary>
        /// 區域巡航 巡檢點
        /// </summary>
        public CruisePointDto Cruise_Point { get; set; }       

    }
           

3.1、關聯查詢方式:AbpVnext

public class WarnsAppService : CrudAppService<Device_Warns, WarnsDto, int, WarnsPagedResultRequestDto, CreateWarnsDto, WarnsDto>, IWarnsAppService
    {
        public WarnsAppService(IRepository<Device_Warns, int> repository) : base(repository)
        {
        }
      
        /// <summary>
        /// 重新翻頁查詢
        /// </summary>
        protected override IQueryable<Device_Warns> CreateFilteredQuery(WarnsPagedResultRequestDto input)
        {
            return this.ReadOnlyRepository.WithDetails(p=>p.Device,p => p.Cruise,prop=>prop.Cruise_Point)
                .WhereIf(input.Device_Id != null, p => p.Device_Id == input.Device_Id);
        }  
    }
}
//注意代碼中的 this.ReadOnlyRepository.WithDetails
           

3.2、關聯查詢方式:Abp

//類似這種寫法
 return await _context.Device_Warns.Include(p => p.Cruise,P.Cruise_Point).FirstOrDefaultAsync(p=>p.id=2);
           

4、查詢結果Json序列化異常:

提示遞歸應用嵌套,超過Json序列化最大嵌套深度;

System.Text.Json.JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.

at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth)

at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)

at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)

at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)

at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()

— End of stack trace from previous location where exception was thrown —

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)

at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)

at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

5、解決方案:

在應用層 GasMonitoringApplicationAutoMapperProfile.cs 檔案中添加實體類轉換配置

public class GasMonitoringApplicationAutoMapperProfile : Profile
    {
        public GasMonitoringApplicationAutoMapperProfile()
        {
            CreateMap<Cruise, CruiseDto>()
                .ForMember(x=>x.Cruise_Points,opt=>opt.Ignore());
            CreateMap<CruiseDto, Cruise>(MemberList.Source); //以Dto資料為準進行轉換
            CreateMap<CreateCruiseDto, Cruise>(MemberList.Source); //隻檢測Dto中屬性 Validate
        }
    }
}
           

.ForMember(x=>x.Cruise_Points,opt=>opt.Ignore()); 可有效解決循環遞歸引用問題

但是也會存在問題 在Cruise查詢的時候 就無法關聯查詢出對應的 Cruise_Points 清單了:不過這種查詢需求一般不會有

三、總結

網上還有其他若幹方法,比如在某個關聯屬性上面添加 [JsonIgnore]屬性以忽略Json序列化;

又或者引入Microsoft.AspNetCore.Mvc.NewtonsoftJson 包,然後Startup中添加 Json序列化忽略遞歸引用;

參考文章:https://www.mmbyte.com/article/142486.html

繼續閱讀