Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Entity Framework Core attribute equivalent to modelBuilder's HasDefaultValueSql?

I want to use annotations for setting the default value for my properties in Entity Framework Core. The issue is that the database is not setting the default values so the value is not being passed down to the database layer.

I want to do something similar to modelBuilder's HasDefaultValueSql:

[DefaultValue("400")]
public int LengthInMeters {get; set;}

How do you convert the below code to attributes?

modelBuilder.Entity<Patient>().Property(c => c.LengthInMeters).HasDefaultValueSql("400");

Using default values by themselves doesn't work. I want to use attributes alone without having to mess with the migrations.

Problems: I've tried other methods with EF but Entity Framework Core doesn't have some items. Such as modelBuilder.Conventions nor AttributeToColumnAnnotationConvention nor CSharpMigrationCodeGenerator nor modelBuilder.Properties()

like image 935
Demodave Avatar asked Mar 06 '19 23:03

Demodave


People also ask

What is HasDefaultValueSql?

HasDefaultValueSql(IConventionPropertyBuilder, String, Boolean) Configures the default value expression for the column that the property maps to when targeting a relational database. C# Copy.

What is ModelBuilder in EF core?

The ModelBuilder is the class which is responsible for building the Model. The ModelBuilder builds the initial model from the entity classes that have DbSet Property in the context class, that we derive from the DbContext class. It then uses the conventions to create primary keys, Foreign keys, relationships etc.

What is the difference between ef6 and EF core?

EF 6 is a stable and mature ORM while EF Core is relatively new. Microsoft rebuilt EF Core from the ground up and removed many of the internal dependencies and providers that EF 6 had (like SQLClient). In the long run, that will make EF Core much more extensible and lighter weight.

How do I change the default value for EF core?

In EF core released 27th June 2016 you can use fluent API for setting default value. Go to ApplicationDbContext class, find/create the method name OnModelCreating and add the following fluent API. – Daniel Z.


2 Answers

This is what I ended up doing, if someone has a cleaner not as intensive way of implementation let me know.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        foreach (var property in entityType.GetProperties())
        {
            var memberInfo = property.PropertyInfo ?? (MemberInfo)property.FieldInfo;
            if (memberInfo == null) continue;
            var defaultValue = Attribute.GetCustomAttribute(memberInfo, typeof(DefaultValueAttribute)) as DefaultValueAttribute;
            if (defaultValue == null) continue;                   
            property.SqlServer().DefaultValue = defaultValue.Value;
        }
    }
}       

I can set the default value in the database using the default value attribute

[DefaultValue("400")]
public int LengthInMeters {get; set;}
like image 199
Demodave Avatar answered Sep 17 '22 18:09

Demodave


Struggled a while getting this job done in another way using EF-Core conventions. I discovered a way to add so called "Plugins" which implement the IConventionSetPlugin interface with which you can add custom conventions. It needs some additional code to get EntityFramework to use the plugin.

But first things first, let's create our PropertyAttributeConvention.

public class DefaultValueAttributeConvention : PropertyAttributeConventionBase<DefaultValueAttribute>
{
    public DefaultValueAttributeConvention(ProviderConventionSetBuilderDependencies dependencies) : base(dependencies) { }

    protected override void ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, DefaultValueAttribute attribute,
        MemberInfo clrMember, IConventionContext context)
    {
        propertyBuilder.HasDefaultValue(attribute.Value, fromDataAnnotation: true);
    }
}

Here we just say the ef propertybuilder to use the default value defined in our [DefaultValue] attribute.

To add the convention we need to create a custom plugin class:

public class CustomConventionSetPlugin : IConventionSetPlugin
{
    public ConventionSet ModifyConventions(ConventionSet conventionSet)
    {
        conventionSet.PropertyAddedConventions.Add(new DefaultValueAttributeConvention(null));
        return conventionSet;
    }
}

For our plugin to get used, we have to create an ef extension class (which itself contains another ExtensionInfo class)

public class CustomDbContextOptionsExtension : IDbContextOptionsExtension
{
    public void ApplyServices(IServiceCollection services)
    {
        services.AddSingleton<IConventionSetPlugin, CustomConventionSetPlugin>();
    }

    public void Validate(IDbContextOptions options) { }

    public DbContextOptionsExtensionInfo Info => new CustomDbContextOptionsExtensionInfo(this);

    private class CustomDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo
    {
        public CustomDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { }

        public override long GetServiceProviderHashCode() => 0;

        public override void PopulateDebugInfo(IDictionary<string, string> debugInfo) { }

        public override bool IsDatabaseProvider => false;
        public override string LogFragment => "";
    }
}

In the extension class we're adding our plugin class to the EF-ServiceCollection.

The last step is to go to our DbContext and add our extension. This can be done in the OnConfigure method:

public class MyDatacontext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(new CustomDbContextOptionsExtension());
    }
}

Now the [DefaultValue] attribute can be used on our entity properties. If we want to add different custom conventions we dont have to create all that extension/plugin classes again. Just create a new convention class and add it through our existing plugin class to the convetionSet.

like image 32
Daniel Z. Avatar answered Sep 17 '22 18:09

Daniel Z.