C# 9 introduced records and init-only properties to make it easier to write immutable reference objects.
I've been trying to convert an old Entity Framework project to use these features, but I've hit a bit of friction between immutable C# records with init-only properties and trying to make changes to the underlying SQL records.
Maybe I'm just pushing against the flow, but is there a pattern for defining your C# classes as immutable init-only records but still allowing for updates to the underlying SQL data?
My current (working) code which uses mutable classes:
MyReport.cs
namespace MyNamespace
{
public sealed class MyReport
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ReportId { get; set; }
public DateTime ReportDate { get; set; }
public bool ReadyToUse { get; set; }
}
}
MyApp.cs
using (var dbContext = DbContext.Create(connectionString))
{
// create a new report
var myReport = new MyReport
{
ReportDate = reportDate,
ReadyToUse = false
};
dbContext.SaveChanges();
... do some other stuff ...
// update the report status
usageReport.ReadyToUse = true;
dbContext.SaveChanges();
}
However, if I change the implementation of MyReport to use C# 9 records and init-only properties:
MyReport.cs
namespace MyNamespace
{
public sealed record MyReport
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ReportId { get; init; }
public DateTime ReportDate { get; init; }
public bool ReadyToUse { get; init; }
}
}
then I start getting an error:
CS8852 - Init-only property or indexer can only be assigned in an object initializer
on the line
usageReport.ReadyToUse = true;
I've got no complaints about the error because you obviously can't update an init-only property outside of the constructor, but I was wondering if there's a good way to work with init-only properties and mutable SQL data in Entity Framework.
I thought about doing this:
using (var dbContext = DbContext.Create(connectionString))
{
// create a new report
var myReport = new MyReport
{
ReportDate = reportDate,
ReadyToUse = false
};
dbContext.SaveChanges();
... do some other stuff ...
// update the report status
usageReport = usageReport with { ReadyToUse = true };
dbContext.SaveChanges();
}
but then I'm not sure how to tell the dbContext to treat the new usageReport as a change to the underlying SQL data without triggering a massive delete and re-insert.