.NET Core,使用 XMLParser 对特定实体的循环引用
.NET Core, circular references on particular entity using XMLParser
我正在使用 C# .Net Core 和 XMLSerializer,我已经检查了各种其他问题,但 none 提供的答案似乎对我有用。我有一个具有多个时间块的 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 服务的控制器是这样创建的(我排除了通用的回购和接口,因为它们对这个例子无关紧要):
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)
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;
}
我正在使用 C# .Net Core 和 XMLSerializer,我已经检查了各种其他问题,但 none 提供的答案似乎对我有用。我有一个具有多个时间块的 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 服务的控制器是这样创建的(我排除了通用的回购和接口,因为它们对这个例子无关紧要):
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)
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;
}