Warm tip: This article is reproduced from serverfault.com, please click

EF Core 3 Has Value Generator

发布于 2020-03-29 02:27:16

in the modelBUilder for an entity, I am try to have the created and modified dates be set on add and updates by a custom generator. The reason for going this path is because the DbContext that creates the models is being used a base class. This base class is being inherited by SQL Server & SQLite EFCore extensions. Because of this there should be database explicit functionality in the context. The GetDateUTC() and triggers that were originally implemented SQL Server.

modelBuilder.Entity<CommunicationSendRequest>(entity =>
            {
               ...

                entity.Property(p => p.CreatedAt).ValueGeneratedOnAdd().HasValueGenerator<CreatedAtTimeGenerator>();
                entity.Property(p => p.ModifiedAt).ValueGeneratedOnUpdate().HasValueGenerator<ModifiedAtTimeGenerator>();

            });

but what is happening on add and updates both properties always set to new values. Meaning on brand new inserts the modifiedat is set, and on updates the createdat date is set. Which removes the true created at date.

CreatedAt and Modifiedat dates

The question is are those have value generators setup correctly? Is there a way to accomplish this using the generators? In the generators I tried to also check the state an return the value only if the state was added or modified. But the state always equaled Detached.

 public class CreatedAtTimeGenerator : ValueGenerator<DateTimeOffset>
    {
        public override DateTimeOffset Next(EntityEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException(nameof(entry));
            }

            return DateTimeOffset.UtcNow;
        }

        public override bool GeneratesTemporaryValues { get; }
    }
    public class ModifiedAtTimeGenerator : ValueGenerator<DateTimeOffset>
    {
        public override DateTimeOffset Next(EntityEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException(nameof(entry));
            }

            return DateTimeOffset.UtcNow;
        }

        public override bool GeneratesTemporaryValues { get; }
    }
Questioner
ChampChris
Viewed
0
ChampChris 2020-11-29 23:09:32

What I actually did was to go away from the ValueGenerate concept all together and handle it the creation of CreatedAt, ModifiedAt, and added after this post was created DeletedAt dates by overriding the SaveChanges() methods.

 public override int SaveChanges()
    {
        var selectedEntityList = ChangeTracker.Entries()
            .Where(x => (x.Entity is IEntityCreatedAt ||
                         x.Entity is IEntityModifiedAt ||
                         x.Entity is IEntityIsDeleted) &&
                        (x.State == EntityState.Added || x.State == EntityState.Modified)).ToList();

        selectedEntityList.ForEach(entity =>
        {
            if (entity.State == EntityState.Added)
            {
                if (entity.Entity is IEntityCreatedAt)
                    ((IEntityCreatedAt)entity.Entity).CreatedAt = DateTimeOffset.UtcNow;
            }

            if (entity.State == EntityState.Modified)
            {
                if (entity.Entity is IEntityModifiedAt)
                    ((IEntityModifiedAt)entity.Entity).ModifiedAt = DateTimeOffset.UtcNow;

                if (entity.Entity is IEntityIsDeleted)
                    if (((IEntityIsDeleted)entity.Entity).IsDeleted)
                        ((IEntityIsDeleted)entity.Entity).DeletedAt = DateTimeOffset.UtcNow;
            }
        });

        return base.SaveChanges();
    }