Diego Venâncio
Diego Venâncio

Reputation: 6007

Datetime default get null after update with entity framework

I have a table with a DATETIME DEFAULT field.

CREATE TABLE People 
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
...
DtOccurrence DATETIME DEFAULT getDATE(),
);

Using scaffolding for generate Class and Entitity for Controllers + Views. Default CRUD working fine, but if I try update a register, [DtOccurrence] get NULL in database.

How fix it? Thanks in advance

Create saving OK

enter image description here

Update only [Name] field send null [DtOccurrence] for database and my auto-generated class dont have this [DtOccurrence] field:

enter image description here

UPDATE: CONTROLLER Create method

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("Id,Name")] People people)
    {
        if (ModelState.IsValid)
        {
                _context.Add(people);
                await _context.SaveChangesAsync();

                return RedirectToAction("Edit", "Pessoas", new { people.Id });
        }
        return View(people);
    }

CONTROLLER Edit method

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
    {
        if (id != people.Id)
        {
            return NotFound();
        }

        if (ModelState.IsValid)
        {
            try
            {
                _context.Update(people);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PeopleExists(people.Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return RedirectToAction(nameof(Index));
        }

        return View(people);
    }

Auto-generated class scaffolding

public partial class Pessoa
{
    public Pessoa()
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

Upvotes: 0

Views: 224

Answers (1)

Steve Py
Steve Py

Reputation: 34698

As mentioned in my comment, while your initial request to provide data to the view was given an entity from the DB Context, the object (Person) you get back in your Update method is not the same entity, and is not associated with your DbContext. It is a deserialized copy. Calling Update with it when it does not contain all fields will result in fields getting set to #null. Calling Update with a detached entity like this from a client is also an attack vector for unauthorized updates to your domain. (Debugging tools /plugins can intercept the call to the server and alter the entity data in any number of ways.)

public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
    if (!ModelState.IsValid)
        return View(people);

    var dataPeople = await _context.People.SingleAsync(x => x.id == people.id);
    dataPeople.name = people.name;
    await _context.SaveChangesAsync(); // dataPeople is a tracked entity and will be saved, not people which is acting as a viewmodel.
    return RedirectToAction(nameof(Index));
}

Using Update will generate an update statement where all fields on the entity are overwritten. You may decide to pass an incomplete entity to the view, or an incomplete entity back from the view, but EF has no notion of what data is missing because it wasn't provided/changed, vs. what was cleared out so it updates everything. Instead, you should load the entity from the DbContext based on the ID provided (which will error if the ID is not found) then set the properties you want to change on that tracked entity before calling SaveChanges. This ensures that the resulting SQL update statement contains only the columns you want changed.

As a general rule I recommend using view model classes for communicating models between server and client so it is clear what the data being passed around actually is. Passing entities between server and views is an anti-pattern which is prone to performance problems, serialization issues, and both intentional and accidental data corruption.

Additional validations should include making sure the changes are complete/legal, and potentially checking a row version # or last modified date between the passed model and the data loaded from the DB to ensure they match. When the user opened the page they may have gotten version #1 of the record. When they finally submit the form, if the DB returned version #2, it would indicate that someone else modified that row in that time. (Otherwise you are overwriting the changes)

Upvotes: 1

Related Questions