czy istnieje „elegancki” sposób nadania określonej właściwości wartości domyślnej?
Może przez DataAnnotations coś takiego:
[DefaultValue("true")]
public bool Active { get; set; }
Dziękuję Ci.
public bool Inactive { get; set; }
😉
czy istnieje „elegancki” sposób nadania określonej właściwości wartości domyślnej?
Może przez DataAnnotations coś takiego:
[DefaultValue("true")]
public bool Active { get; set; }
Dziękuję Ci.
public bool Inactive { get; set; }
😉
Odpowiedzi:
Możesz to zrobić, ręcznie edytując pierwszą migrację kodu:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
do true
podczas tworzenia Event
obiektu, jak również. Domyślną wartością zawsze jest false
właściwość bool, która nie ma wartości null, więc jeśli nie zostanie zmieniona, to właśnie struktura encji zapisze w db. A może coś mi brakuje?
Minęło trochę czasu, ale pozostawiając notatkę innym. Osiągnąłem to, co jest potrzebne z atrybutem i ozdobiłem pola mojej klasy modelem tym atrybutem, jak chcę.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Otrzymałem pomoc z tych 2 artykułów:
Co ja zrobiłem:
[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;
}
}
Następnie w konstruktorze konfiguracji migracji zarejestruj niestandardowy generator SQL.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
[SqlDefaultValue(DefaultValue = "getutcdate()")]
każdego elementu. 1) Po prostu usuń modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
2) DodajmodelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Powyższe odpowiedzi naprawdę pomogły, ale dostarczyły tylko część rozwiązania. Głównym problemem jest to, że jak tylko usuniesz atrybut Wartość domyślna, ograniczenie kolumny w bazie danych nie zostanie usunięte. Tak więc poprzednia wartość domyślna pozostanie w bazie danych.
Oto pełne rozwiązanie problemu, w tym usunięcie ograniczeń SQL przy usuwaniu atrybutów. Używam również natywnego .NET FrameworkDefaultValue
atrybutu .
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
Aby to zadziałało, musisz zaktualizować IdentityModels.cs i Configuration.cs pliki
Dodaj / zaktualizuj tę metodę w swojej ApplicationDbContext
klasie
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);
}
Zaktualizuj Configuration
konstruktor klasy, rejestrując niestandardowy generator Sql:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
Następnie dodaj niestandardową klasę generatora Sql (możesz dodać ją do pliku Configuration.cs lub osobnego pliku)
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)]
atrybut? Jeśli tak, to dlaczego? W moich testach wydawało się, że nie ma żadnego efektu, pomijając to.
Właściwości modelu nie muszą być „właściwościami automatycznymi”, chociaż jest to łatwiejsze. A atrybut DefaultValue jest tak naprawdę tylko informacyjnymi metadanymi. Przyjęta tutaj odpowiedź jest jedną z alternatyw dla podejścia konstruktora.
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; }
}
Będzie to działać tylko w przypadku aplikacji tworzących i konsumujących dane przy użyciu tej konkretnej klasy. Zwykle nie stanowi to problemu, jeśli kod dostępu do danych jest scentralizowany. Aby zaktualizować wartość we wszystkich aplikacjach, należy skonfigurować źródło danych, aby ustawić wartość domyślną. Odpowiedź Devi pokazuje, jak można to zrobić za pomocą migracji, sql lub dowolnego języka, w którym mówi twoje źródło danych.
To, co zrobiłem, zainicjowałem wartości w konstruktorze encji
Uwaga: atrybuty DefaultValue nie ustawią wartości twoich właściwości automatycznie, musisz to zrobić sam
Po komentarzu @SedatKapanoglu dodałem całe moje podejście, które działa, ponieważ miał rację, samo używanie płynnego API nie działa.
1- Utwórz niestandardowy generator kodu i przesłoń Generuj dla modelu 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- Przypisz nowy generator kodu:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Użyj płynnego interfejsu API, aby utworzyć adnotację:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
To proste! Po prostu adnotuj z wymaganymi.
[Required]
public bool MyField { get; set; }
wynikowa migracja będzie:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Jeśli chcesz mieć wartość true, zmień wartość defaultValue na true w migracji przed aktualizacją bazy danych
true
?
Przyznaję, że moje podejście wymyka się całej koncepcji „Code First”. Ale jeśli masz możliwość zmiany domyślnej wartości w samej tabeli ... jest to o wiele prostsze niż długości, które musisz pokonać powyżej ... Jestem po prostu zbyt leniwy, aby wykonać całą tę pracę!
Wygląda na to, że oryginalny pomysł na plakaty zadziałałby:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
Myślałem, że popełniali błąd, dodając cytaty ... ale niestety nie ma takiej intuicyjności. Inne sugestie były dla mnie po prostu za dużo (oczywiście, mam uprawnienia potrzebne do wejścia do tabeli i wprowadzenia zmian ... gdzie nie każdy programista zrobi to w każdej sytuacji). W końcu zrobiłem to po prostu w staromodny sposób. Ustawiam wartość domyślną w tabeli SQL Server ... Mam na myśli naprawdę, już dość! UWAGA: Następnie przetestowałem, wykonując migrację dodatków i aktualizację bazy danych, a zmiany utknęły.
Po prostu przeciąż domyślnego konstruktora klasy Model i przekaż dowolny odpowiedni parametr, którego możesz użyć lub nie. Dzięki temu można łatwo podać wartości domyślne dla atrybutów. Poniżej znajduje się przykład.
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; }
}
}
Rozważmy, że masz nazwę klasy o nazwie Produkty i masz pole IsActive. potrzebujesz konstruktora tworzenia:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
Zatem domyślną wartością IsActive jest Prawda!
Edycja :
jeśli chcesz to zrobić za pomocą SQL, użyj tego polecenia:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
W rdzeniu EF wydanym 27 czerwca 2016 r. Możesz używać płynnego API do ustawiania wartości domyślnej. Przejdź do klasy ApplicationDbContext, znajdź / utwórz nazwę metody OnModelCreating i dodaj następujący płynny interfejs API.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
W .NET Core 3.1 możesz wykonać następujące czynności w klasie modeli:
public bool? Active { get; set; }
W DbContext OnModelCreating dodajesz wartość domyślną.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
W wyniku tego w bazie danych
Uwaga: jeśli nie masz wartości zerowej (bool?) Dla swojej właściwości, otrzymasz następujące ostrzeżenie
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'.
Przekonałem się, że wystarczy użyć Autoinicjatora właściwości dla właściwości encji, aby wykonać zadanie.
Na przykład:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
Hmm ... najpierw wykonuję DB, aw takim przypadku jest to o wiele łatwiejsze. EF6 prawda? Po prostu otwórz model, kliknij prawym przyciskiem myszy kolumnę, dla której chcesz ustawić wartość domyślną, wybierz właściwości, a zobaczysz pole „DefaultValue”. Po prostu wypełnij i zapisz. Ustawi dla ciebie kod.
Twój przebieg może się różnić w zależności od kodu, ale nie pracowałem z tym.
Problem z wieloma innymi rozwiązaniami polega na tym, że chociaż mogą one działać początkowo, to zaraz po przebudowaniu modelu wyrzucą one dowolny niestandardowy kod wstawiony do pliku generowanego maszynowo.
Ta metoda działa poprzez dodanie dodatkowej właściwości do pliku edmx:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
I dodając niezbędny kod do konstruktora:
public Thingy()
{
this.Iteration = 1;
Ustaw wartość domyślną dla kolumny w tabeli na serwerze MSSQL, aw kodzie klasy dodaj atrybut, jak poniżej:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
dla tej samej nieruchomości.
this.Active = true;
? Myślę, że wartość DB będzie miała pierwszeństwo podczas pobierania, ale uważaj, jeśli new'ing, a następnie dołączenie encji do aktualizacji bez pobierania najpierw, ponieważ śledzenie zmian może postrzegać to jako chęć aktualizacji wartości. Skomentuj, ponieważ nie używałem EF od dawna i wydaje mi się, że to strzał w ciemność.