3

I have a base DbContext class like

public abstract class DbContextBase : DbContext
{
    public DbContextBase()
    {
    }

    public DbContextBase(DbContextOptions options)
        : base(options)
    {
    }

    public override int SaveChanges()
    {
        this.ValidateEntities();

        return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        this.ValidateEntities();

        return base.SaveChangesAsync(cancellationToken);
    }

     public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
     {
        this.ValidateEntities();

        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    protected virtual void ValidateEntities()
    {
        var entities = this.ChangeTracker.Entries().
                            Where(s => s.State == EntityState.Added || s.State == EntityState.Modified);

        foreach (var entity in entities)
        {
            var validationContext = new ValidationContext(entity);
            Validator.ValidateObject(entity, validationContext);
        }
    }
}

All my Db Context classes inherit from this base class. The problem is that the line Validator.ValidateObject() is not throwing ValidationException even when there is a validation violation. In the debugger I can see that this line is executed. For example, for below model class I try calling SaveChangesAsync() with Name set to null but the validation is passing:

public class MyModel : IEntity<long>
{
    [Key]
    public long Id { get; set; }

    [Required]
    public string Name { get; set; }
}

IEntity<T> just imposes property Id on all models.

kovac
  • 4,945
  • 9
  • 47
  • 90
  • 1
    If that's the real code, `var entity` is not an entity, but change tracker entry instance. May be you should call it `entry` and then use `entry.Entity`, or add `Select(s => e.Entry)` to the previous line. – Ivan Stoev Jan 06 '19 at 09:03
  • Sorry for the typo, I meant `Select(s => s.Entity)` – Ivan Stoev Jan 06 '19 at 09:44
  • @IvanStoev, your suggestion worked. Do you want to post it as an answer so I can accept it? Thanks btw. – kovac Jan 06 '19 at 09:45

2 Answers2

5

The problem is that the variable entity does not hold entity instance, but change tracker (EntityEntry) instance, so the code is trying to validate the wrong thing.

So either rename the variables and use entry.Entity property:

var entries = this.ChangeTracker.Entries()
    .Where(s => s.State == EntityState.Added || s.State == EntityState.Modified);

foreach (var entry in entries)
{
    var validationContext = new ValidationContext(entry.Entity);
    Validator.ValidateObject(entry.Entity, validationContext);
}

or keep the code as is, but make sure entities variable holds enumerable of entity instances:

var entities = this.ChangeTracker.Entries()
    .Where(s => s.State == EntityState.Added || s.State == EntityState.Modified)
    .Select(s => s.Entity); // <--
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
0

Code

public class MyModel : IEntity<long>
{
    [Key]
    public long Id { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string Name { get; set; }
}

More

Mohamed Elrashid
  • 8,125
  • 6
  • 31
  • 46