c# - 为什么我不能通过实体框架覆盖某个字段?
问题描述
我正在开发一个使用实体框架的 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)
解决方案
这很可能是 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 不会更新默认列的评论表示歉意。这是不正确的,仅适用于初始插入。
更新 - 所需设置:
推荐阅读
- r - 如何在 levelplot(R lattice 包)中绘制单个特定的等高线?
- python - SQLAlchemy Core GROUP BY 计算字段
- ios - 创建一个模态弹出视图,点击注释的标注,Swift 中的 MKMapView
- terraform - terraform 有条件地创建日志配置和后续容器定义
- python - 如何在 python 中将 ZipFile 对象转换为二进制文件?
- javascript - 测试 React 搜索组件时出错
- javascript - 对反应表中的数据进行排序 - 不是按字母顺序排列,而是按特定顺序
- z3 - 如何在 SMTLIB / Z3 / CVC4 中声明 forall 量词?
- javascript - 在同一运行时安全隔离两个 Javascript 代码库?
- reactjs - 如何将 React Context 与 Hooks 一起使用?