Usando un atributo personalizado en EF7 (core) OnModelCreating

c# ef-code-first entity-framework entity-framework-core npgsql

Pregunta

Tengo un DefaultAttribute definido así:

[AttributeUsage(AttributeTargets.Property)]
public class DefaultAttribute : Attribute
{
    /// <summary>
    /// Specifies this property has a default value upon creation.
    /// </summary>
    /// <param name="defaultValue">The default value of the property.</param>
    /// <param name="useAsLiteral">Set to true if the value is <em>not</em> quoted in the DDL.</param>
    public DefaultAttribute(object defaultValue, bool useAsLiteral = false)
    {
        DefaultValue = defaultValue;
        UseAsLiteral = useAsLiteral;
    }

    public object DefaultValue { get; private set; }

    /// <summary>
    /// True if the default value is not quoted in the DDL
    /// </summary>
    public bool UseAsLiteral { get; private set; }
}

He decorado varias de mis entidades con este atributo, así:

public class MyEntity
{
    . . . (other properties) . . .
    [StringLength(200)]
    [Required]
    [Default("My Default Description!")]
    public string Description { get; set; }
}

Luego, en mi método OnModelCreating en el contexto de mi base de datos, escribí el siguiente código:

//examine custom annotations for shaping the schema in the database.
foreach (var entityType in builder.Model.GetEntityTypes())
    foreach (var property in entityType.GetProperties())
    {
        var annotations = property.GetAnnotations();

        // evaluate default values
        var defaultAnnotation = annotations.FirstOrDefault(x => x.Name == typeof(DefaultAttribute).FullName);
        if (defaultAnnotation != null)
        {
            var defaultValue = defaultAnnotation.Value as DefaultAttribute;
            if (defaultValue == null) continue;

            if (defaultValue.UseAsLiteral)
                property.Npgsql().DefaultValueSql = defaultValue.DefaultValue.ToString();
            else
                property.Npgsql().DefaultValue = defaultValue.DefaultValue;
        }
    }

Mi expectativa, al agregar una migración, (y la posterior actualización de la base de datos) es que habría un valor predeterminado de "¡Mi descripción predeterminada!" para la columna Description de MyEntity ... sin embargo, ese no es el caso.

No estoy recibiendo ningún error, pero no lo estoy haciendo como sospecharía, y entrar en OnModelCreating con un punto de interrupción también es inexplicablemente difícil de hacer.

¿Estoy haciendo esto correctamente? ¿Simplemente no funciona? ¿Es que no es compatible con EF7? ¿O no es compatible con mi implementación de PostgreSQL? Cualquier idea sería apreciada.

ACTUALIZACIÓN Con la respuesta de @ IvanStoev, pude hacer que esto funcionara con algunas modificaciones menores (Reflexión en .NET Core un poco diferente de la tradicional):

//examine custom annotations for shaping the schema in the database.
foreach (var entityType in builder.Model.GetEntityTypes())
    foreach (var property in entityType.GetProperties())
    {
        var memberInfo = property.PropertyInfo ?? (MemberInfo)property.FieldInfo;
        var defaultValue = memberInfo?.GetCustomAttribute<DefaultAttribute>();
        if (defaultValue == null) continue;
        if (defaultValue.UseAsLiteral)
            property.Npgsql().DefaultValueSql = defaultValue.DefaultValue.ToString();
        else
            property.Npgsql().DefaultValue = defaultValue.DefaultValue;
    }

Esto funcionó como un campeón.

Respuesta aceptada

EF Core no sabe nada acerca de su atributo personalizado, por lo que no hay forma de que se descubra y se exponga como una anotación (que en general es una cosa diferente y no está necesariamente asociada con un atributo).

Debe extraer el atributo manualmente de PropertyInfo o FieldInfo cuando FieldInfo presente:

foreach (var entityType in builder.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(DefaultAttribute)) as DefaultAttribute;
        if (defaultValue == null) continue;
        if (defaultValue.UseAsLiteral)
            property.Npgsql().DefaultValueSql = defaultValue.DefaultValue.ToString();
        else
            property.Npgsql().DefaultValue = defaultValue.DefaultValue;
    }


Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué