首页 > 解决方案 > 为什么我不能通过实体框架覆盖某个字段?

问题描述

我正在开发一个使用实体框架的 ASP.NET MVC 应用程序。我想更改表CreatedOn中的字段Users

以下代码

    using (var db = new MainDB())
    {   
        var theUser = db.Users.Single(u => u.LoginName == model.User.LoginName);
        theUser.CreatedOn = new DateTime(2017, 1, 1); // this does not save
        theUser.CF20 = "test20";
        db.SaveChanges();   
    }
    

将字段更改CF20为“test20”,但不会更改CreatedOn字段。

我假设该CreatedOn字段在某处被赋予了一些特殊的状态和功能,在创建记录时用时间戳填充它,并防止它在此后被更改。

我在代码中找不到它在哪里执行此操作,因此我假设它位于实体框架模型、设置等中的某个位置。

我在哪里可以找到它的设置位置以及设置后如何更改它?

附录

例如,我在<EntityType Name="Users">该行下的 .edmx 文件中找到<Property Name="CreatedOn" Type="datetime" Nullable="false" StoreGeneratedPattern="Computed" />并将其更改为,<Property Name="CreatedOn" Type="datetime" Nullable="false" />但它仍将日期计算为 datetime 并且不会更改它。

并输出原始 SQL,我在语句中看到update它遗漏CreatedOn了:我的 SQL 输出显示:

UPDATE [dbo].[Users]
SET [LoginName] = @0,
    [Password] = @1,
    [PasswordSalt] = @2,
    [LastLoginIP] = NULL,
    [Status] = @3,
    [Email] = @4,
    [FailedLoginAttempts] = NULL,
    [CF01] = NULL,
    [CF02] = NULL,
    [CF03] = NULL,
    [CF04] = NULL,
    [CF05] = NULL,
    [CF06] = NULL,
    [CF07] = NULL,
    [CF08] = NULL,
    [CF09] = NULL,
    [CF10] = NULL,
    [LanguageID] = @5,
    [CF11] = NULL,
    [CF12] = NULL,
    [CF13] = NULL,
    [CF14] = NULL,
    [CF15] = NULL,
    [CF16] = NULL,
    [CF17] = NULL,
    [CF18] = NULL,
    [CF19] = @6,
    [CF20] = @7,
    [MustChangePassword] = NULL
WHERE ([ID] = @8)

标签: c#entity-framework

解决方案


这很可能是 SQL Server 中的计算列,因此在您的代码中没有任何地方实际设置它 - 它是在最初添加行时在服务器上完成的。EF 不会在更新查询中包含计算列,并将在更新查询之后读回列,以从数据存储中加载可能更新的值。

讨论后更新:

这是一个最小的解决方案,其中默认值根据需要正确更新。

首先在 SQL Server 上创建一个新表:

CREATE TABLE [dbo].[DateDefaultTest](
    [AnId] [int] IDENTITY(1,1) NOT NULL,
    [CreatedOn] [datetime2](7) NOT NULL,
    [SomeField] [nvarchar](50) NULL,
 CONSTRAINT [PK_DateDefaultTest] PRIMARY KEY CLUSTERED 
(
    [AnId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[DateDefaultTest] ADD  CONSTRAINT [DF_DateDefaultTest_CreatedOn]  DEFAULT (getutcdate()) FOR [CreatedOn]
GO

这有一个作为自动递增主键的 ID 字段、一个字符串字段和CreatedOn我们感兴趣的字段。我们添加了一个 DEFAULT 约束来将其设置为当前的 UTC 日期和时间。

接下来,创建一个控制台应用程序,通过 NuGet 添加 EF 并像往常一样设置 DbContext。我已经调用了我的 context DefaultTestEntities

生成的实体代码如下:

public partial class DateDefaultTest
{
    public int AnId { get; set; }
    public System.DateTime CreatedOn { get; set; }
    public string SomeField { get; set; }
}

在 EF 图的CreatedOn字段属性页中,没有任何更改。

使用以下代码添加几行:

using (var db = new DefaultTestEntities())
{
    db.DateDefaultTests.Add(new DateDefaultTest
    {
         SomeField = "Bananas"
    });
    db.DateDefaultTests.Add(new DateDefaultTest
    {
         SomeField = "Apples",
    });

    db.SaveChanges();
}

这些应显示在表中,并按预期设置当前日期和时间。

正如您所发现的,如果您在插入新行时尝试CreatedOn手动设置,由于 SQL Server 约束,这将不起作用。例如:

db.DateDefaultTests.Add(new DateDefaultTest
{
     SomeField = "Pears",
     CreatedOn = DateTime.UtcNow.AddHours(1)
});

将导致设置一个无意义的“0001-01-01”日期。

如果要CreatedOn显式设置,则必须更新现有行。去做这个:

db.DateDefaultTests
    .Where(x => x.SomeField == "Bananas")
    .First()
    .CreatedOn = DateTime.UtcNow.AddDays(-1);
db.SaveChanges();

如果这不适用于您的情况,我建议您:

  • 检查 SQL Server 中的列以确保未计算该值
  • 重新生成模型
  • 确认您的代码中没有 EF 覆盖,这可能会影响保存实体时的值

对我说 EF 不会更新默认列的评论表示歉意。这是不正确的,仅适用于初始插入。

更新 - 所需设置:

SQL Server CreatedOn 设置

EDMX CreatedOn 设置


推荐阅读