Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fluent API ValueGeneratedOnAddOrUpdate() does not seem to be working. #3955

Closed
Eneuman opened this issue Dec 2, 2015 · 10 comments
Closed

Fluent API ValueGeneratedOnAddOrUpdate() does not seem to be working. #3955

Eneuman opened this issue Dec 2, 2015 · 10 comments

Comments

@Eneuman
Copy link

Eneuman commented Dec 2, 2015

I'm trying to add a "Created" and "Updated" column to one of my entities.
I want "Created" to contain the DateTime when the entity was first saved in the databse and "Updated" the last time is was changed.

Using the Fluent API code example, from the "Generated properties" doc page, produces a "Cannot insert NULL into column Created" error.

This is my setup:

Entity:
public DateTime Created{ get; set; }
public DateTime Updated { get; set; }

Fluent API
b.Property(u => u.Uppdaterades).ValueGeneratedOnAddOrUpdate();
b.Property(u => u.Skapades).ValueGeneratedOnAdd();

Am I missing something?

@rowanmiller
Copy link
Contributor

@Eneuman In addition to telling EF when they are generated, you need to setup some mechanism to actually generate the values.

You can use builder.Entity<Blog>().Property(b => b.Created).HasDefaultValueSql("GETDATE()") to take care of generating values when new rows are inserted.

For the updated one, you probably want to add a trigger. There is no first class API to do this, but you can use SQL(...) in your migrations to add one.

Alternatively, if you want to generate them on the app server (rather than in the database), you can override SaveChanges and just set the values there.

public override int SaveChanges()
{
    ChangeTracker.DetectChanges();
    var now = DateTime.UtcNow;

    foreach (var item in ChangeTracker.Entries<Blog>().Where(e => e.State == EntityState.Added))
    {
        item.Property("Created").CurrentValue = now;
        item.Property("Updated").CurrentValue = now;
    }

    foreach (var item in ChangeTracker.Entries<Blog>().Where(e => e.State == EntityState.Modified))
    {
        item.Property("Updated").CurrentValue = now;
    }

    return base.SaveChanges();
}

@Eneuman
Copy link
Author

Eneuman commented Dec 3, 2015

@rowanmiller Thanks for the quick reply!
Now I know how it works, but I think it's a bit confusing.
The naming "ValueGeneratedOnAddOrUpdate" suggests that a value actually is generated on insert and update.

And the docs says:
"Value generated on add"
"Value generated on add means that if you don’t specify a value, one will be generated for you."

"Value generated on add or update"
"Value generated on add or update means that a new value is generated every time the record is saved (insert or update)."

Maybe add a section in the docs that describes how you supply your own value generator?

@Eneuman
Copy link
Author

Eneuman commented Dec 3, 2015

It would be great if this was possible:

b.Property(u => u.Updated).ValueGeneratedOnAddOrUpdate(MyValueGeneratingFunction);

private DateTime MyValueGeneratingFunction()
{
  return DateTime.UtcNow;
}

@matthewDDennis
Copy link

I agree that something is wrong here. The documentation at http://ef.readthedocs.org/en/latest/modeling/generated-properties.html#id7 strongly suggests that a value will be generated, and uses the specific case of Modified and Created DateTime values.

@rowanmiller
Copy link
Contributor

Cool, I will update the docs to make it clear that you are telling EF that a value will be generated. There are some cases (integers keys, GUID keys, byte[] concurrency tokens) where EF will setup a way to generate values... and for anything else you need to setup some form of generation.

@rowanmiller
Copy link
Contributor

Update to docs out for review dotnet/EntityFramework.Docs#88

@rowanmiller
Copy link
Contributor

Closing as this is not being tracked by the PR in the docs repo

@ericwj
Copy link

ericwj commented Mar 15, 2016

They're calling each other @rowanmiller - overriding int SaveChanges() might result in the very confusing fact that the behavior might depend on how SaveChanges is called (sync/async/with/without arguments).

Why are all overloads of SaveChanges[Async] virtual? Shouldn't only the most relevant version be virtual to avoid this kind of duplication/confusion? Would it be possible to even provide one code path for both sync and async code paths?

@NickStrupat
Copy link

@Eneuman @rowanmiller I have been working on an EF Core version of my EntityFramework.Triggers library at https://github.com/NickStrupat/EntityFramework.Triggers

@incompletude
Copy link

incompletude commented Mar 18, 2018

I'm trying to use ValueGeneratedOnAddOrUpdate(). It seems to remove the property value from the generated SQL, thus causing an error on the trigger because NEW.senha doesn't exist.

Without ValueGeneratedOnAddOrUpdate the code works properly, but the property values are not updated.

CREATE FUNCTION fn_criar_usuario()
RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'INSERT') THEN
        SELECT ces.fn_criar_senha(NEW.senha)
        INTO NEW.senha;
        SELECT ces.fn_criar_chave()
        INTO NEW.chave;
    END IF;
    RETURN NEW;
END; $$
LANGUAGE 'plpgsql';
                entity.Property(e => e.Senha)
                    .HasColumnName("senha")
                    .ValueGeneratedOnAddOrUpdate()
                    .IsRequired();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants