esiste un modo "elegante" per assegnare a una proprietà specifica un valore predefinito?
Forse da DataAnnotations, qualcosa del tipo:
[DefaultValue("true")]
public bool Active { get; set; }
Grazie.
public bool Inactive { get; set; }
😉
esiste un modo "elegante" per assegnare a una proprietà specifica un valore predefinito?
Forse da DataAnnotations, qualcosa del tipo:
[DefaultValue("true")]
public bool Active { get; set; }
Grazie.
public bool Inactive { get; set; }
😉
Risposte:
Puoi farlo modificando manualmente la prima migrazione del codice:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
anche true
durante la creazione di un Event
oggetto. Il valore predefinito è sempre false
su una proprietà bool non nullable, quindi a meno che non venga modificato, questo è ciò che l'entità framework salverà nel db. Oppure mi sfugge qualcosa?
È passato un po 'di tempo, ma lasciando una nota per gli altri. Ho ottenuto ciò che è necessario con un attributo e ho decorato i campi della mia classe di modello con quell'attributo come desidero.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Ho ottenuto l'aiuto di questi 2 articoli:
Cosa ho fatto:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
private void SetAnnotatedColumn(ColumnModel col)
{
AnnotationValues values;
if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
{
col.DefaultValueSql = (string)values.NewValue;
}
}
Quindi, nel costruttore Configurazione della migrazione, registrare il generatore SQL personalizzato.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
[SqlDefaultValue(DefaultValue = "getutcdate()")]
su tutte le entità. 1) Rimuovi semplicemente modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
2) AggiungimodelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Le risposte di cui sopra sono state di grande aiuto, ma hanno fornito solo una parte della soluzione. Il problema principale è che non appena si rimuove l'attributo Valore predefinito, il vincolo sulla colonna nel database non verrà rimosso. Quindi il valore predefinito precedente rimarrà comunque nel database.
Ecco una soluzione completa al problema, inclusa la rimozione dei vincoli SQL sulla rimozione degli attributi. Sto anche riutilizzando l' DefaultValue
attributo nativo di .NET Framework .
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
Perché ciò funzioni è necessario aggiornare i file IdentityModels.cs e Configuration.cs
Aggiungi / aggiorna questo metodo nella tua ApplicationDbContext
classe
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
modelBuilder.Conventions.Add(convention);
}
Aggiorna il tuo Configuration
costruttore di classi registrando il generatore Sql personalizzato, in questo modo:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
Successivamente, aggiungi una classe di generatore SQL personalizzata (puoi aggiungerla al file Configuration.cs o a un file separato)
internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private int dropConstraintCount = 0;
protected override void Generate(AddColumnOperation addColumnOperation)
{
SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
base.Generate(addColumnOperation);
}
protected override void Generate(AlterColumnOperation alterColumnOperation)
{
SetAnnotatedColumn(alterColumnOperation.Column, alterColumnOperation.Table);
base.Generate(alterColumnOperation);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
SetAnnotatedColumns(createTableOperation.Columns, createTableOperation.Name);
base.Generate(createTableOperation);
}
protected override void Generate(AlterTableOperation alterTableOperation)
{
SetAnnotatedColumns(alterTableOperation.Columns, alterTableOperation.Name);
base.Generate(alterTableOperation);
}
private void SetAnnotatedColumn(ColumnModel column, string tableName)
{
AnnotationValues values;
if (column.Annotations.TryGetValue("SqlDefaultValue", out values))
{
if (values.NewValue == null)
{
column.DefaultValueSql = null;
using (var writer = Writer())
{
// Drop Constraint
writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
Statement(writer);
}
}
else
{
column.DefaultValueSql = (string)values.NewValue;
}
}
}
private void SetAnnotatedColumns(IEnumerable<ColumnModel> columns, string tableName)
{
foreach (var column in columns)
{
SetAnnotatedColumn(column, tableName);
}
}
private string GetSqlDropConstraintQuery(string tableName, string columnName)
{
var tableNameSplittedByDot = tableName.Split('.');
var tableSchema = tableNameSplittedByDot[0];
var tablePureName = tableNameSplittedByDot[1];
var str = $@"DECLARE @var{dropConstraintCount} nvarchar(128)
SELECT @var{dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF @var{dropConstraintCount} IS NOT NULL
EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + @var{dropConstraintCount} + ']')";
dropConstraintCount = dropConstraintCount + 1;
return str;
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
attributo? Se è così, perché? Nei miei test sembrava non avere alcun effetto tralasciandolo.
Le proprietà del modello non devono essere "proprietà automatiche" Anche se è più semplice. E l'attributo DefaultValue è in realtà solo metadati informativi. La risposta accettata qui è un'alternativa all'approccio del costruttore.
public class Track
{
private const int DEFAULT_LENGTH = 400;
private int _length = DEFAULT_LENGTH;
[DefaultValue(DEFAULT_LENGTH)]
public int LengthInMeters {
get { return _length; }
set { _length = value; }
}
}
vs.
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
Funzionerà solo per le applicazioni che creano e consumano dati usando questa classe specifica. Di solito questo non è un problema se il codice di accesso ai dati è centralizzato. Per aggiornare il valore in tutte le applicazioni è necessario configurare l'origine dati per impostare un valore predefinito. La risposta di Devi mostra come si può fare usando le migrazioni, sql o qualunque sia la lingua parlata dall'origine dati.
Quello che ho fatto, ho inizializzato i valori nel costruttore dell'entità
Nota: gli attributi DefaultValue non imposteranno automaticamente i valori delle tue proprietà, devi farlo da solo
Dopo il commento di @SedatKapanoglu, sto aggiungendo tutto il mio approccio che funziona, perché aveva ragione, il solo utilizzo dell'API fluente non funziona.
1- Creare un generatore di codice personalizzato e sovrascrivere Genera per un ColumnModel.
public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
{
if (column.Annotations.Keys.Contains("Default"))
{
var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
column.DefaultValue = value;
}
base.Generate(column, writer, emitName);
}
}
2- Assegna il nuovo generatore di codice:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Usa API fluenti per creare l'Annotazione:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
È semplice! Basta annotare con richiesto.
[Required]
public bool MyField { get; set; }
la migrazione risultante sarà:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Se si desidera true, modificare defaultValue su true nella migrazione prima di aggiornare il database
true
?
Ammetto che il mio approccio sfugge all'intero concetto "Code First". Ma se hai la possibilità di cambiare il valore predefinito nella tabella stessa ... è molto più semplice delle lunghezze che devi passare sopra ... Sono troppo pigro per fare tutto quel lavoro!
Sembra quasi che l'idea originale dei poster avrebbe funzionato:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
Pensavo che avessero appena commesso l'errore di aggiungere delle virgolette ... ma purtroppo tale intuitività. Gli altri suggerimenti erano semplicemente troppo per me (a condizione che avessi i privilegi necessari per entrare nella tabella e apportare le modifiche ... dove non tutti gli sviluppatori lo faranno in ogni situazione). Alla fine l'ho fatto alla vecchia maniera. Ho impostato il valore predefinito nella tabella di SQL Server ... Intendo davvero, già abbastanza! NOTA: ho ulteriormente testato la creazione di un database add-migrazione e aggiornamento e le modifiche sono state bloccate.
Basta sovraccaricare il costruttore predefinito della classe Model e passare qualsiasi parametro rilevante che è possibile o meno utilizzare. In questo modo puoi facilmente fornire valori predefiniti per gli attributi. Di seguito è riportato un esempio.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Aim.Data.Domain
{
[MetadataType(typeof(LoginModel))]
public partial class Login
{
public Login(bool status)
{
this.CreatedDate = DateTime.Now;
this.ModifiedDate = DateTime.Now;
this.Culture = "EN-US";
this.IsDefaultPassword = status;
this.IsActive = status;
this.LoginLogs = new HashSet<LoginLog>();
this.LoginLogHistories = new HashSet<LoginLogHistory>();
}
}
public class LoginModel
{
[Key]
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string LoginCode { get; set; }
[Required]
public string Password { get; set; }
public string LastPassword { get; set; }
public int UserGroupId { get; set; }
public int FalseAttempt { get; set; }
public bool IsLocked { get; set; }
public int CreatedBy { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public string Culture { get; set; }
public virtual ICollection<LoginLog> LoginLogs { get; set; }
public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
}
}
Supponiamo di avere un nome di classe chiamato Products e di avere un campo IsActive. hai solo bisogno di creare un costruttore:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
Quindi il valore predefinito di IsActive è True!
Edite :
se vuoi farlo con SQL usa questo comando:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
In EF core rilasciato il 27 giugno 2016 è possibile utilizzare l'API fluente per impostare il valore predefinito. Vai alla classe ApplicationDbContext, trova / crea il nome del metodo OnModelCreating e aggiungi la seguente API fluente.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
In .NET Core 3.1 è possibile effettuare le seguenti operazioni nella classe del modello:
public bool? Active { get; set; }
In DbContext OnModelCreating si aggiunge il valore predefinito.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
Il risultato è il seguente nel database
Nota: se non hai nullable (bool?) Per la tua proprietà riceverai il seguente avviso
The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.
Ho scoperto che usare l'inizializzatore di proprietà automatico sulla proprietà di entità è sufficiente per portare a termine il lavoro.
Per esempio:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
Hmm ... Prima faccio DB, e in quel caso, in realtà è molto più semplice. EF6 giusto? Basta aprire il modello, fare clic con il pulsante destro del mouse sulla colonna per cui si desidera impostare un valore predefinito, scegliere proprietà e verrà visualizzato un campo "Valore predefinito". Basta compilare e salvare. Imposta il codice per te.
Il tuo chilometraggio può variare in base al codice, tuttavia, non ho lavorato con quello.
Il problema con molte altre soluzioni, è che mentre potrebbero funzionare inizialmente, non appena ricostruirai il modello, esso eliminerà qualsiasi codice personalizzato inserito nel file generato dalla macchina.
Questo metodo funziona aggiungendo una proprietà aggiuntiva al file edmx:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
E aggiungendo il codice necessario al costruttore:
public Thingy()
{
this.Iteration = 1;
Impostare il valore predefinito per la colonna nella tabella in MSSQL Server e nel codice di classe aggiungere l'attributo, in questo modo:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
per la stessa proprietà.
this.Active = true;
? Penso che il valore del DB avrà la precedenza durante il recupero, ma attenzione se il nuovo viene quindi associato un'entità per un aggiornamento senza un primo recupero, poiché il rilevamento delle modifiche potrebbe vedere questo come si desidera aggiornare il valore. Commenta perché non uso EF da molto tempo e mi sembra che sia uno scatto al buio.