Ho definito DefaultAttribute
modo:
[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; }
}
Ho decorato diverse delle mie entità con questo attributo, in questo modo:
public class MyEntity
{
. . . (other properties) . . .
[StringLength(200)]
[Required]
[Default("My Default Description!")]
public string Description { get; set; }
}
Quindi, nel mio metodo OnModelCreating nel mio contesto di database, ho scritto il seguente codice:
//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;
}
}
La mia aspettativa, quando aggiungo una migrazione, (e il successivo aggiornamento del database) è che ci sarebbe un valore predefinito di "La mia descrizione predefinita!" per la colonna Description
di MyEntity
... tuttavia, questo non è il caso.
Non ricevo errori, ma non lo faccio come sospetto, e anche entrare in OnModelCreating
con un punto di interruzione è inspiegabilmente difficile da fare.
Sto facendo questo correttamente? Non funziona? Non è supportato solo in EF7? Oppure non è supportato nella mia implementazione di PostgreSQL? Qualsiasi intuizione sarebbe apprezzata.
AGGIORNAMENTO Usando la risposta di @ IvanStoev, sono riuscito a farlo funzionare con alcune modifchezioni minori (Reflection in .NET Core un po 'diverso dal tradizionale):
//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;
}
Questo ha funzionato come un campione.
EF Core non sa nulla del tuo attributo personalizzato, quindi non c'è modo di essere scoperto ed esposto come annotazione (che in generale è una cosa diversa e non necessariamente associata all'attributo).
Devi estrarre manualmente l'attributo da PropertyInfo
o FieldInfo
quando presenti:
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;
}