【问题标题】:Update an existing entry with Entity Framework使用实体框架更新现有条目
【发布时间】:2018-04-04 13:00:01
【问题描述】:

我想使用实体框架更新现有条目,这是我当前的代码:

[HttpPost]
public IActionResult Edit(Product product)
{
    if (ModelState.IsValid)
    {
        var result = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);

        if (result == null)
            return RedirectToAction("Index", "Products");

        _productRepository.Update(product);
        //result = product;
        _productRepository.Save();

        return View("Edit", result);
    }

我尝试了什么:

result = product; 似乎没有更新 db 中的行。

public void Update(T item)
    {
        _context.Entry(item).CurrentValues.SetValues(item);
    }

似乎没有更新 db 中的行。

result.Title = product.Title - 有效,但是我必须为每个字段都这样做,有没有办法通过用另一个对象替换值来更新一行?

【问题讨论】:

  • _context.Entry(item).CurrentValues.SetValues(item); 没有任何意义。
  • @Isma 除了可能不好的英文措辞之外,上面的代码什么都不做是不是很明显?从item 属性设置item 属性。

标签: c# asp.net sql-server entity-framework


【解决方案1】:

编辑

实际上,我意识到下面的代码将不起作用,因为您已经在跟踪同一个实体,这是由这一行引起的:

 var result = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);

因此,您需要删除该行并仅使用下面的 Update 方法并将产品对象作为参数,或者使用结果对象并根据产品类中的数据对其进行更新,然后保存:

 result.Name = product.Name; 
 [...]

在这种情况下,您无需拨打_repository.update,只需拨打_repository.save

使用产品更新

假设您的 Product 类与您的 Product 实体类属于同一类的对象,您需要确保它正在被实体框架跟踪并在保存之前将其标记为已修改:

为此,请按如下方式修改您的更新方法:

public void Update(T item)
{
    if (!_context.Set<T>().Local.Any(e => e == item))
    {
        _context.Set<T>().Attach(item);
    }
    _context.Entry(item).State = EntityState.Modified
}

然后保存它,它应该可以工作:

_productRepository.Update(product);
_productRepository.Save();

更好的方法?

您可以专门为您的视图创建一个模型类,然后根据需要检索和更新您的数据库实体模型,而不是在视图之间来回发送实体框架实体:

例如,如果您的产品数据库实体如下所示:

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public int InternalId { get; set; }
}

在您看来,您不需要/不想使用 InternalId 字段,因此您的网站程序集中会有一个模型,如下所示:

public class ProductModel
{
    public int Id { get; set; }
    public string ProductName { get; set; }
}

然后在您的控制器中,这就是您将使用的:

[HttpPost]
public IActionResult Edit(ProductModel product)
{
    if (!ModelState.IsValid)
    {
        return View(product);
    }

    var dbProduct = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);

    if (dbProduct == null)
    {
        //Product doesn't exist, create one, show an error page etc...
        //In this case we go back to index
        return RedirectToAction("Index", "Products");
    }

    //Now update the dbProduct using the data from your model
    dbProduct.ProductName = product.ProductName;

如果您有很多字段,您不想手动执行此操作,有一些库会为您执行此操作,例如AutoMapper 或我个人最喜欢的(更快,更易于使用)@ 987654322@

使用 ValueInjecter 你会做这样的事情来自动分配所有的公共属性

    dbProduct.InjectFrom(product);

最后,只需调用save,这次你不需要更改状态,因为EF已经在跟踪你的实体:

    _productRepository.Save();

【讨论】:

  • 感谢您的建议。一旦我开始工作,我肯定会制作一个不同的视图模型。我尝试使用您的功能,但出现以下错误:System.InvalidOperationException: 'The instance of entity type 'Product' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
  • 更新:我在_productRepository.Update(result); 调用之后移动了result = product;,但我没有收到该错误,但数据库没有被更新。
  • 你不需要做结果=产品,那是错误的......可能是实体没有附加的问题。我会更新我的答案...
  • 现在试试,我在设置状态之前添加了attach方法。
  • 另请参阅我的上次编辑,关于您遇到的错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2011-12-10
  • 1970-01-01
相关资源
最近更新 更多