【问题标题】:.NET Core, circular references on particular entity using XMLParser.NET Core,使用 XMLParser 对特定实体的循环引用
【发布时间】:2021-01-19 07:29:41
【问题描述】:

我正在使用 C# .Net Core 和 XMLSerializer,我检查了其他各种问题,但提供的答案似乎都不适合我。我有一个 MaintenanceMoment,它有多个时间块,所以是一对多的关系,我创建它如下:

我的型号代码:

PRMaintenanceMoment.cs

    public class PRMaintenanceMoment
    {
      [XmlIgnore]
      public int ID { get; set; }

      [Required]
      [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
      public string EarliestExecutionDate { get; set; }

      [Required]
      [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
      public string LatestExecutionDate { get; set; }

      public List<Timeblock> Timeblock { get; set; } = new List<Timeblock>();
    }

Timeblock.cs

public class Timeblock
{
    [XmlIgnore]
    public int ID { get; set; }

    [ForeignKey("MaintenanceMomentID")]
    public PRMaintenanceMoment MaintenanceMoment { get; set; }

    [Required]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm:ss}")]
    public DateTime EarliestExecutionTime { get; set; }

    [Required]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm:ss}")]
    public DateTime LatestExecutionTime { get; set; }
}

我的数据库上下文中的引用是这样的: ApplicationDbContext.cs

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<Timeblock> Timeblocks { get; set; }

 protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
        modelBuilder.Entity<PRMaintenanceMoment>()
            .HasMany(tb => tb.Timeblock)
            .WithOne(pr => pr.MaintenanceMoment);
 }
}

为 MaintenanceMoment 提供服务的我的控制器是这样创建的(我已经排除了通用 repo 和接口,因为它们对本示例无关紧要):

PRMaintenanceMoment.cs

    public class PRMaintenanceMomentRepository : GenericRepository<PRMaintenanceMoment>, IPRMaintenanceMomentRepository
{
    public PRMaintenanceMomentRepository(ApplicationDbContext context)
        : base(context) { }

    public override List<PRMaintenanceMoment> Index()
    {
        var context = _context.PRMaintenanceMoments
               .Include(PRMaintenanceMoment => PRMaintenanceMoment.Timeblock)
            .ToList();

        return context;
    }

    public void Update(int id, PRMaintenanceMoment maintenanceMoment)
    {
        PRMaintenanceMoment DBPRMaintenanceMoment = _context.PRMaintenanceMoments.FirstOrDefault(ms => ms.ID.Equals(id));

        _context.Entry<PRMaintenanceMoment>(DBPRMaintenanceMoment).CurrentValues.SetValues(maintenanceMoment);

        _context.SaveChanges();
    }
}

最终创建的迁移显示如下:

migrationBuilder.CreateTable(
            name: "PRMaintenanceMoments",
            columns: table => new
            {
                ID = table.Column<int>(nullable: false)
                    .Annotation("MySql:ValueGenerationStrategy", 
                     MySqlValueGenerationStrategy.IdentityColumn),
                EarliestExecutionDate = table.Column<string>(nullable: false),
                LatestExecutionDate = table.Column<string>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_PRMaintenanceMoments", x => x.ID);
            });

        migrationBuilder.CreateTable(
            name: "Timeblocks",
            columns: table => new
            {
                ID = table.Column<int>(nullable: false)
                    .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                MaintenanceMomentID = table.Column<int>(nullable: true),
                EarliestExecutionTime = table.Column<DateTime>(nullable: false),
                LatestExecutionTime = table.Column<DateTime>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Timeblocks", x => x.ID);
                table.ForeignKey(
                    name: "FK_Timeblocks_PRMaintenanceMoments_MaintenanceMomentID",
                    column: x => x.MaintenanceMomentID,
                    principalTable: "PRMaintenanceMoments",
                    principalColumn: "ID",
                    onDelete: ReferentialAction.Restrict);
            });

        migrationBuilder.CreateIndex(
            name: "IX_PlanningRequests_PRMaintenanceMomentID",
            table: "PlanningRequests",
            column: "PRMaintenanceMomentID");

        migrationBuilder.CreateIndex(
            name: "IX_Timeblocks_MaintenanceMomentID",
            table: "Timeblocks",
            column: "MaintenanceMomentID");

这对我来说似乎很好,那么,正如许多问题所暗示的那样,从 JSON 序列化程序中添加 ReferenceLoopHandling。我试过这个,但这似乎没有帮助。为了完整起见,我添加了我的 startup.cs 文件中最重要的部分;这当然不是完整的文件,而是最重要的设置。

startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers()
             .AddXmlSerializerFormatters(); // Adding the XML serializer here

        services.AddMvc(options =>
        {
            options.MaxValidationDepth = 64;
            options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
            options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
        });

services.AddMvc().AddNewtonsoftJson(jsonOptions => { jsonOptions.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;  });

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

最后抛出的确切错误是:

System.InvalidOperationException: There was an error generating the XML document.
---> System.InvalidOperationException: A circular reference was detected while serializing an object of type SALES005.Models.PRMaintenanceMoment.
at System.Xml.Serialization.XmlSerializationWriter.WriteStartElement(String name, String ns, Object o, Boolean writePrefixed, XmlSerializerNamespaces xmlns)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterList1.Write3_PRMaintenanceMoment(String n, String ns, PRMaintenanceMoment o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterList1.Write2_Timeblock(String n, String ns, Timeblock o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterList1.Write3_PRMaintenanceMoment(String n, String ns, PRMaintenanceMoment o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterList1.Write4_ArrayOfPRMaintenanceMoment(Object o)

【问题讨论】:

  • 您应该只构建一个表。多次运行代码会在数据库中创建重复的表并导致错误。在第二次运行 CreateTable 方法之前,您需要从数据库中删除表。
  • 我不确定我是否遵循;两个表都只创建一次。该索引确实是在迁移的后期添加的,但我认为这不会引起任何问题?
  • 您运行了多少次代码来创建表格?
  • 我运行了一次代码;使用迁移工具
  • XML 序列化很难调试。通常我会注释掉 c# 类的部分以隔离问题发生的位置。序列化将忽略不在 c# 类中的 XML 标记。因此,您可以注释掉类中的属性,直到错误消失。

标签: c# asp.net-core foreign-keys xmlserializer


【解决方案1】:

Newtonsoft可以解决json循环引用,但是不能解决xml。所以你可以改变查询方法。

public List<PRMaintenanceMoment> get6()
    {
        var pRMaintenanceMoments = (from prm in _db.PRMaintenanceMoment
                     
                   select new PRMaintenanceMoment
                   {
                       ID=prm.ID,
                        EarliestExecutionDate=prm.EarliestExecutionDate,
                         LatestExecutionDate=prm.LatestExecutionDate,
                         Timeblock=prm.Timeblock.Select(c=>new Timeblock
                         {
                              ID=c.ID,
                         }).ToList()
                          
                   }).ToList();
        return pRMaintenanceMoments;
    }

【讨论】:

  • 这在实施时似乎确实有效,你能解释一下为什么这个特定的解决方案有效,而我当前的上下文没有?或者提供一些文档链接,以便我将来自己解决这些问题?
  • Include :EF Core 将 LINQ 查询的表示传递给数据库提供程序。数据库提供者反过来将其翻译为特定于数据库的查询语言。可能会生成多层查询。 document。在某些情况下,它不能解决一些问题。在这种情况下,您需要使用复杂的查询来使转换后的 SQL 语句更加详细。 Complex Query Operators
  • 感谢您的补充说明,我将通过您提供的链接查看复杂的查询运算符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 2012-04-04
  • 2019-02-13
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
相关资源
最近更新 更多