简体   繁体   English

C# 9 - 使用实体框架更新记录的仅初始化属性

[英]C# 9 - updating init-only properties on records with Entity Framework

C# 9 introduced records and init-only properties to make it easier to write immutable reference objects. C# 9 引入了记录仅初始化属性,以便更轻松地编写不可变引用对象。

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.我一直在尝试将旧的实体框架项目转换为使用这些功能,但我在具有仅初始化属性的不可变 C# 记录和尝试对底层 SQL 记录进行更改之间遇到了一些摩擦。

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?也许我只是在逆流而上,但是有没有一种模式可以将您的 C# 类定义为不可变的仅初始化记录,但仍允许更新基础 SQL 数据?

My current (working) code which uses mutable classes :我当前使用可变的(工作)代码:

MyReport.cs我的报告.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我的应用程序.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 的实现更改为使用 C# 9 记录和仅初始化属性:

MyReport.cs我的报告.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 CS8852 - 仅初始化属性或索引器只能在 object 初始化程序中分配

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.我对这个错误没有任何抱怨,因为您显然无法在构造函数之外更新仅初始化属性,但我想知道是否有一种好方法可以在实体框架中使用仅初始化属性和可变 SQL 数据。

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.但是我不确定如何告诉dbContext将新的 usageReport 视为对底层 SQL 数据的更改,而不会触发大量删除和重新插入。

You certainly can use the C# 9 records with Entity Framework.您当然可以将 C# 9 记录与实体框架一起使用。 What you can not do is to use them with the default behavior of "retreive an entity, update its properties and call .SaveChanges() .不能做的是将它们与“检索实体,更新其属性并调用.SaveChanges()的默认行为一起使用。

Instead you must use the copy-and-update syntax of records in conjunction with the DbContext.Update() function:相反,您必须将记录的复制和更新语法与 DbContext.Update() function 结合使用:

var report = dbContext.Set<MyReport>().AsNoTracking().First(...some linq query...);
var updatedReport = report with {ReadyToUse = true};
dbContext.Update(updatedRecord);
dbContext.SaveChanges();

IMPORTANT: You need to call .AsNoTracking () on every query or disable the change tracker to prevent EF from tracking the retrieved entity.重要提示:您需要在每个查询上调用.AsNoTracking ()或禁用更改跟踪器以防止 EF 跟踪检索到的实体。 If you don't, it will throw an exception in ``.SaveChanges () '' saying that another entity with the same key is already being tracked.如果你不这样做,它会在 ``.SaveChanges () '' 中抛出一个异常,说明另一个具有相同键的实体已经被跟踪。 If you decide to declare all your entities as records, disabling the tracking system is the best option and will have a positive impact on the overall performance of the application.如果您决定将所有实体声明为记录,则禁用跟踪系统是最佳选择,将对应用程序的整体性能产生积极影响。

EXTRA: The solution is exactly the same when working with F# records.额外:使用 F# 记录时,解决方案完全相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM