【问题标题】:DateTime column in EntityFramework code-first does not work as expectedEntityFramework 代码优先中的 DateTime 列无法按预期工作
【发布时间】:2013-07-07 23:53:28
【问题描述】:

我有基本型号 User 和两个附加型号 UserCreateModelUserEditModelUserCreateModel 工作正常并创建了用户,但是当我尝试编辑用户时它给了我错误。

我不明白他为什么对 datetime 采取行动,因为它在数据库中已经存在有效的 DateTime 属性。我想做的就是更新Name 属性。我在这里做错了什么?

错误

将 datetime2 数据类型转换为 datetime 数据类型导致值超出范围。声明已终止。

控制器

public HttpResponseMessage PutUser(int id, UserEditModel user)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }

    if (id != user.Id)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }

    db.Entry(user).State = EntityState.Modified;

    try
    {
        db.SaveChanges(); // Exception here, all validation passed with success
    }
    catch (DbUpdateConcurrencyException ex)
    {
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
    }

    return Request.CreateResponse(HttpStatusCode.OK);
}

型号

public class User
{
    public int Id { get; set; }
    public virtual DateTime CreatedDateTime { get; set; }
    public virtual string Username { get; set; }
    public virtual string Password { get; set; }
    public virtual string PasswordSalt { get; set; }
    public virtual string AccessToken { get; set; }
    public virtual string Name { get; set; }
}

public class UserEditModel : User
{
    [Required]
    public override string Name { get; set; }
}

【问题讨论】:

  • 请注意标题和标签。 MVC != MVVM,它们都与这个 EF 问题无关。

标签: c# entity-framework


【解决方案1】:

当您将实体映射到数据库时,您需要告诉 Entity Framework 使用正确的列类型。

如果您使用的是 fluent API,您可以这样做:

Property(p => p.CreatedDateTime).HasColumnType("datetime2");

或者如果您更喜欢直接在 POCO 上使用 column 属性:

public class User
{   
    public int Id { get; set; }

    [Column(TypeName = "DateTime2")]
    public virtual DateTime CreatedDateTime { get; set; }

    public virtual string Username { get; set; }
    public virtual string Password { get; set; }
    public virtual string PasswordSalt { get; set; }
    public virtual string AccessToken { get; set; }
    public virtual string Name { get; set; }
}

小心;根据您配置实体框架的方式,您现有的数据库可能会被删除并重新创建以应对更改。

有关详细信息,请参阅以下博客文章:

【讨论】:

  • 奇怪,现在它确实有效,但它删除了所有以前的值,如用户名、密码等,它们变为空。 >.
  • @Steve Slaumas 的回答涵盖了第二个问题。
【解决方案2】:

通过设置db.Entry(user).State = EntityState.Modified;,你告诉EF你想更新user所有属性。并且因为您的 UserEditModel 派生自 User 实体,EF 将尝试更新所有实体属性。

我不知道您为什么从实体派生 Edit 模型,但它会导致您的场景出现问题。我宁愿只从数据库加载用户,更新你想要的属性并保存它:

var dbUser = db.Users.Find(user.Id);
dbUser.Username = user.Name;
db.SaveChanges();

更一般地,您可以像这样更新多个属性:

var dbUser = db.Users.Find(user.Id);
db.Entry(dbUser).CurrentValues.SetValues(user);
db.SaveChanges();

但它要求编辑模型中的属性与实体属性具有相同的名称(并且编辑模型不是从实体派生的)。

【讨论】:

  • 我推导出来是因为验证。当我创建用户时,我需要它有usernamepassword 等。当我编辑时,我只想传递name。如果我不为每个操作创建多个模型,我会在不应该出现的地方出现验证错误。这种方法适合我的情况吗?
  • @Steve:在我看来,这不是正确的做法。 EF 验证!= MVC 验证。有两个视图模型用于创建和编辑操作是可以的,但我会在实体和视图模型之间复制属性,并根据需要将 MVC 验证逻辑放在视图模型上。
  • @Steve 我同意 Slauma 的观点。请参阅我的answer to this question,这可能会对您有所帮助。
  • @Benjamin 所以基本上最好的方法是手动更新属性model.property = viewmodel.property 像这样,对吗?
  • 或者使用诸如AutoMapper之类的框架来为你做。
猜你喜欢
  • 2015-06-25
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 2013-12-14
  • 2021-10-11
相关资源
最近更新 更多