In this class library (targeting .NET Standard 2.0 and .NET Framework 4.6.1), I am doing data validation at the data context level, as these classes may be used by non-web applications, so I'm following the example from https://github.com/dotnet/efcore/issues/9662 in a static helper class:
internal static ICollection<ValidationResult> ValidateChanges(DbContext context)
{
var modifiedEntries = context.ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added ||
x.State == EntityState.Modified);
List<ValidationResult> validationResults = new List<ValidationResult>();
foreach (var entry in modifiedEntries)
{
var validationContext = new ValidationContext(entry);
Validator.TryValidateObject(entry, validationContext, validationResults, true);
}
return validationResults;
}
My POCO (simplified):
public class Person
{
[Required]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
// other properties
}
My unit test (simplified):
[Fact]
public void TestDataValidation()
{
IPersonService svc = _container.Resolve<IPersonService>();
Person p = svc.Get(1);
string origValue = p.FirstName;
p.FirstName = string.Empty;
try
{
Assert.Throws<ValidationException>(() => svc.Update(p, p.Id));
}
catch (Xunit.Sdk.XunitException) // ValidationException not thrown
{
// restore original value
throw;
}
}
The service class method:
public virtual TEntity Update(TEntity entity, TKey key)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
((IDatabaseContext)Context).BeginTransaction();
TEntity dbEntity = Get(key);
Context.Entry(dbEntity).CurrentValues.SetValues(entity);
((IDatabaseContext)Context).Commit();
return entity;
}
The Commit() method:
public abstract class DatabaseContext<TContext> : DbContext, IDatabaseContext
where TContext : DbContext
{
public void Commit()
{
ICollection<ValidationResult> validationResults = DbContextHelper.ValidateChanges(this);
if (validationResults.Count > 0)
{
foreach (ValidationResult validationResult in validationResults)
{
string message = string.Format("{0}: {1}", string.Join(", ", validationResult.MemberNames.ToArray()), validationResult.ErrorMessage);
throw new ValidationException(message);
}
}
base.Commit();
}
}
When I execute the test, the validation complete fails. When I put a break point in the middle of the foreach loop of the validation method, this is what I see:
modifiedEntrieshas one item (as expected)entry.CurrentValues["FirstName"]is an empty string (as expected)- When you view it in the debugger watch list,
entry.CurrentValues.Properties[5]shows{Property: Person.FirstName (string) Required}(seems right) validationContext.ItemsshowsCount = 0(wait, what? shouldn't this be 1?)
Then when I reach the end of the validation method, validationResults is empty, even though there definitely should be something in there.
According to Data annotations attributes not working in asp net core you need AddDataAnnotations() on the ServiceCollection, but that's in ASP.NET MVC Core. Is there something similar I needed to do for my unit test application? Am I missing anything else?
Thanks in advance