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

3

6

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()

Footstep answered 6/3, 2019 at 23:12 Comment(2)
You can not set a default value using Data Annotations and By convention, a default value is not configuredGarbage
@NaDeRStar That is exactly what I am trying to replace with the attributes.Footstep
F
8

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;}
Footstep answered 8/3, 2019 at 15:9 Comment(0)
R
4

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.

Rebak answered 12/11, 2020 at 11:29 Comment(0)
R
0

Install Microsoft.EntityFrameworkCore.Relational package, it should solve most of your migration issues when moving to EF core.

Robbi answered 19/2, 2021 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.