user9248102
user9248102

Reputation: 299

Update an existing entry with Entity Framework

I want to update an existing entry with entity framework and here is my current code:

[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);
    }

What have I tried:

result = product; doesn't seem to update the row in db.

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

doesn't seem to update the row in db.

result.Title = product.Title - works, but then I have to do this for each field, is there a way to update a row simply by replacing the values with another object?

Upvotes: 1

Views: 854

Answers (1)

Isma
Isma

Reputation: 15180

Edit

Actually, I realized that the code below will not work because you are already tracking the same entity, this is caused by this line:

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

So you need to either remove that line and just use the Update method below with the product object as the parameter or use the result object and update it based on the data from your product class and then save:

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

In this case you don't need to call _repository.update, just _repository.save

Using product to update

Assuming your Product class is an object of the same class as your Product entity class you need to make sure that it is being tracked by entity framework and to mark it as modified before it can be saved:

To do that, modify your update method as follows:

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
}

Then just save it and it should work:

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

A better approach?

Instead of sending entity framework entities back and forth to and from your views, you could create a model class specifically for your view and then retrieve and update your database entity models as needed:

For example if your Product database entity looks like this:

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

In your view you don't need / want to use the InternalId field, so you would have a model in your Website assembly that could look like the following:

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

And then in your controller, this is what you will use:

[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;

If you have a lot of fields, you don't want to do this manually, there are some libraries that will do this for you, for example, AutoMapper or my personal favorite (faster, easier to use) ValueInjecter

Using ValueInjecter you would do something like this to assign all the common properties automatically

    dbProduct.InjectFrom(product);

Finally, just call save, this time you don't need to change the state because EF is already tracking your entity:

    _productRepository.Save();

Upvotes: 1

Related Questions