Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating entities using data annotations or fluent api in EF 7.0 (In Memory)

I can't verify and test my database by in memory providers. for example I set these properties to required :

public abstract class Log
{
    #region Properties
    public Guid Id { get; set; }
    [Required]
    public string ClientIp { get; set; }
    [Required]
    public string Application { get; set; }
    [Required]
    public string Host { get; set; }
    [Required]
    public string Path { get; set; }
    [Required]
    public string Method { get; set; }
    [Required]
    public string User { get; set; }
    [Required]
    public string Date { get; set; }
    #endregion
}

and this is my DBContext :

public class ApplicationDbContext : IdentityDbContext<ApplicationUsers, Role, Guid>, IUnitOfWork
{
    private readonly IConfigurationRoot _configuration;

    public ApplicationDbContext(IConfigurationRoot configuration)
    {
        _configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var useInMemoryDatabase = _configuration[key: "UseInMemoryDatabase"].Equals(value: "true",
            comparisonType: StringComparison.OrdinalIgnoreCase);
        if (useInMemoryDatabase)
            optionsBuilder.UseInMemoryDatabase();
        else
            optionsBuilder.UseSqlServer(
                connectionString: _configuration[key: "ConnectionStrings:ApplicationDbContextConnection"]
                , sqlServerOptionsAction: serverDbContextOptionsBuilder =>
                {
                    var minutes = (int) TimeSpan.FromMinutes(3).TotalSeconds;
                    serverDbContextOptionsBuilder.CommandTimeout(commandTimeout: minutes);
                });
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Log>()
            .HasKey(c => c.Id);
        modelBuilder.Entity<Log>()
            .HasDiscriminator<int>(name: "Type")
            .HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information))
            .HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error));


    }

And this is my unit test :

[TestClass]
public class LogRepositoryTest
{


  private readonly IServiceProvider _serviceProvider;
    public LogRepositoryTest()
    {
        var services = new ServiceCollection();
        services.AddScoped<IUnitOfWork, ApplicationDbContext>();
        services.AddScoped<ILogRepository, LogRepository>();
        services.AddSingleton(provider => new ConfigurationBuilder()
            .AddInMemoryCollection(initialData: new[]
            {
                new KeyValuePair<string, string>(key: "UseInMemoryDatabase", value: "true"),

            })
            .Build());
         services.AddEntityFrameworkInMemoryDatabase().AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
        _serviceProvider = services.BuildServiceProvider();
    }
    [TestMethod]
    public async Task Verify_SaveRequestLog()
    {
        using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
            {
                context.Set<Log>().Add(new LogRequest());
                var result =await context.SaveAllChangesAsync();
                Assert.AreEqual(1, result);
            }

        }
    }

But the unit test method always return 1 and passes, meanwhile the empty object of LogRequest must not save anything to database! How can I determine not null properties for unit test ? In fact how can I enforce unit test to reflect to validation policies ?

Update:

Based on this linke : Entity Framework Core Issues

that I asked, I got this respond:

EF Core doesn't do any validation of entities beyond what is needed for internal consistency. Validation is something that could be done in EF, but experience shows that it is not something that is useful to many developers because it usually cannot replace either client-side validation or database validation and there are also other places where validation can be done more effectively.

Going beyond EF to the database, the in-memory database does not currently validate nullability (i.e. requiredness) when saving property values. I will leave this issue open so that we can discuss as a team whether this is something we should add.

Also, if the intent is test with an in-memory database as an approximation for a relational database, then you might want to consider using SQLite in in-memory mode. See https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/index for more information.

like image 379
paradise_human Avatar asked Dec 10 '16 20:12

paradise_human


People also ask

Should I use Fluent API or data annotations?

The fluent API is considered a more advanced feature and we would recommend using Data Annotations unless your requirements require you to use the fluent API.

Should I use fluent validation?

Summary. FluentValidation provides a great alternative to Data Annotations in order to validate models. It gives better control of validation rules and makes validation rules easy to read, easy to test, and enable great separation of concerns.

Does data annotation attributes override Fluent API configuration?

To write Fluent API configurations, override the OnModelCreating() method of DbContext in a context class, as shown below. You can use Data Annotation attributes and Fluent API at the same time. Entity Framework gives precedence to Fluent API over Data Annotations attributes.

Does EF core support data annotation attributes?

Configuration enables you to override EF Core's default behaviour. Configuration can be applied in two ways, using the Fluent API, and through DataAnnotation attributes. Attributes are a kind of tag that you can place on a class or property to specify metadata about that class or property.


1 Answers

Based on this linke : Entity Framework Core Issues

that I asked, I got my answer :

class MyContext : DbContext
{
public override int SaveChanges()
{
    var entities = from e in ChangeTracker.Entries()
                   where e.State == EntityState.Added
                       || e.State == EntityState.Modified
                   select e.Entity;
    foreach (var entity in entities)
    {
        var validationContext = new ValidationContext(entity);
        Validator.ValidateObject(entity, validationContext);
    }

    return base.SaveChanges();
}
}
like image 113
paradise_human Avatar answered Nov 15 '22 08:11

paradise_human