Entity Framework 6 Önce kod Varsayılan değer


204

belirli bir özelliğe varsayılan değer vermenin "zarif" yolu var mı?

Belki de DataAnnotations ile:

[DefaultValue("true")]
public bool Active { get; set; }

Teşekkür ederim.


Belki kurucuda denemek this.Active = true;? Ben getirilirken DB değeri öncelikli olacaktır düşünüyorum, ama dikkatli değişiklik izleme gibi bir getirme ilk olmadan bir güncelleme bir varlık takılarak sonra new'ing eğer olabilir Eğer değerini güncellemek isteyen olarak görüyoruz. Yorum yap, çünkü EF'i uzun zamandır kullanmadım ve bunun karanlıkta bir çekim olduğunu hissediyorum.
AaronLS

3
Yanıtınız için teşekkür ederim, şu ana kadar bu yöntemi kullandım stackoverflow.com/a/5032578/2913441 ama belki daha iyi bir yol olduğunu düşündüm.
marino-krk

2
public bool Inactive { get; set; }😉
Jesse Hufstetler

Microsoft dokümanları "Veri Ek Açıklamaları'nı kullanarak varsayılan bir değer ayarlayamazsınız" dediği için.
hmfarimani

Yanıtlar:


167

İlk kod geçişini elle düzenleyerek yapabilirsiniz:

public override void Up()
{    
   AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
} 

6
OP özel olarak ayarlanmış değilse Eminim bu çalışacaktır değilim Activeiçin truebir oluştururken Eventnesneyi de. Varsayılan değer her zaman falsenullable olmayan bir bool özelliğinde olur, bu nedenle değiştirilmedikçe varlık çerçevesi db'ye kaydedilecek olan budur. Yoksa bir şey mi kaçırıyorum?
GFoley83

6
@ GFoley83, evet, haklısın. Bu yöntem yalnızca veritabanı düzeyinde varsayılan kısıtlama ekler. Eksiksiz bir çözüm için, varlığın yapıcısında varsayılan değeri atamanız veya yukarıdaki
yanıtta

1
Bu temel tipler için geçerlidir. DATETIMEOFFSET gibi bir şey için, defaultValueSql: "SYSDATETIMEOFFSET" kullanın ve varsayılanDeğer DEĞERI: System.DateTimeOffset.Now, geçerli sistem datetimeoffset değerinin bir dizesine çözülmez.
OzBob

3
AFAIK, geçişi yeniden iskele durumunda manuel değişiklikleriniz kaybolacak
Alexander Powolozki

1
@ninbit Ben ilk önce veritabanı düzeyinde kaldırmak için göç yazmanız gerektiğini düşünüyorum, sonra DAL eşleme değiştirmek
gdbdable

74

Bir süre oldu, ama diğerleri için bir not bıraktı. Bir özellik ile ihtiyaç duyulanları elde ettim ve model sınıfı alanlarımı bu özellik ile istediğim gibi dekore ettim.

[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }

Bu 2 makalenin yardımını aldım:

Ben ne yaptım:

Özniteliği Tanımla

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}

Bağlamın "OnModelCreating" bölümünde

modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));

Özel SqlGenerator'da

private void SetAnnotatedColumn(ColumnModel col)
{
    AnnotationValues values;
    if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
    {
         col.DefaultValueSql = (string)values.NewValue;
    }
}

Daha sonra Geçiş Yapılandırması yapıcısında özel SQL üreticisini kaydedin.

SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());

6
Hatta [SqlDefaultValue(DefaultValue = "getutcdate()")]her varlığı giymeden global olarak da yapabilirsiniz . 1) Basitçe kaldırın modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue)); 2) EklemodelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Daniel Skowroński

1
Özel SqlGenerator nerede lütfen?
ʙᴀᴋᴇʀ ʙᴀᴋᴇʀ

3
Özel SqlGenerator buradan geldi: andy.mehalick.com/2014/02/06/…
ravinsp

1
@ ravinsp MigrationCodeGenerator sınıfını neden SqlGenerator kodu yerine doğru bilgilerle geçiş yapacak şekilde özelleştirmiyorsunuz? SQL kodu son adımdır ...
Alex

@Alex denemeye değer! Bu da işe yarayacak ve SQL kodunu enjekte etmekten daha zarif olacaktır. Ama C # MigrationCodeGenerator geçersiz kılma karmaşıklığı hakkında emin değilim.
ravinsp

73

Yukarıdaki cevaplar gerçekten yardımcı oldu, ancak çözümün sadece bir kısmını verdi. En büyük sorun, Varsayılan değer özniteliğini kaldırır kaldırmaz, veritabanındaki sütundaki kısıtlamanın kaldırılmamasıdır. Dolayısıyla, önceki varsayılan değer veritabanında kalmaya devam eder.

İşte öznitelik kaldırma SQL kısıtlamalarını kaldırma da dahil olmak üzere, sorunun tam bir çözüm. Ayrıca .NET Framework'ün yerel DefaultValueözniteliğini yeniden kullanıyorum .

kullanım

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }

Bunun çalışması için IdentityModels.cs ve Configuration.cs dosyalarını güncellemeniz gerekir

IdentityModels.cs dosyası

ApplicationDbContextSınıfınıza bu yöntemi ekleyin / güncelleyin

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);
}

Configuration.cs dosyası

ConfigurationSınıf yapıcısını aşağıdaki gibi özel Sql üretecini kaydederek güncelleyin :

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        // DefaultValue Sql Generator
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}

Ardından, özel Sql üreteci sınıfını ekleyin (bunu Configuration.cs dosyasına veya ayrı bir dosyaya ekleyebilirsiniz )

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;
    }
}

2
Bu yaklaşım benim için mükemmel çalıştı. Yaptığım bir geliştirme de Generate (CreateTableOperation createTableOperation) ve Generate (AddColumnOperation addColumnOperation) değerlerini aynı mantıkla geçersiz kılmaktı, böylece bu senaryolar da yakalandı. Ben de sadece değerleri kontrol ediyorum.NewValue varsayılan olarak boş bir dize olmasını istediğim gibi.
Delorian

2
@Delorian Cevabımı güncelledim, yorumlarınız için teşekkür ederim
Jevgenij Martynenko

1
Bir geri alma işleminin birden fazla kısıtlamayı düşürdüğü örnekleri desteklemek için yayınınızda bir düzenleme yaptım. Betiğiniz @con'un zaten bildirildiğini belirten bir hata verir. Sayaç tutmak ve basitçe artırmak için özel bir değişken oluşturdum. Ayrıca, kısıtlama oluştururken EF'nin SQL'e gönderdiklerini daha yakından eşleştirmek için bırakma sınırının biçimini de değiştirdim. Bu konuda harika çalışmalar!
Brad

1
Çözüm için teşekkürler ama iki sorun var: 1. Tablo adları parantez gerekir. 2. Güncellemede yeni değer ayarlanmadı ve bunun yerine varsayılan değer ayarlandı!
Omid-RH

1
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]Özelliğin ayarlanmasına ihtiyacım var mı? Öyleyse neden? Testlerimde, bunu dışarıda bırakmanın bir etkisi yok gibi görünüyordu.
RamNow

26

Daha kolay olmasına rağmen model özelliklerinizin 'otomatik özellikler' olması gerekmez. Ve DefaultValue özniteliği gerçekten sadece bilgilendirici meta verilerdir. Burada kabul edilen cevap , yapıcı yaklaşımına bir alternatiftir.

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; }        
}

Bu yalnızca bu sınıfı kullanarak veri oluşturan ve tüketen uygulamalar için geçerlidir. Veri erişim kodu merkezileştirilmişse genellikle bu bir sorun oluşturmaz. Değeri tüm uygulamalarda güncellemek için veri kaynağını varsayılan bir değer ayarlayacak şekilde yapılandırmanız gerekir. Devi'nin cevabı , taşıma işlemleri, sql veya veri kaynağınızın konuştuğu her dil kullanılarak nasıl yapılabileceğini gösterir.


21
Not: Bu , veritabanında varsayılan bir değer belirlemez . Varlığınızı kullanmayan diğer programlar bu varsayılan değeri alamaz.
Eric

Cevabın bu kısmı, ancak kayıtları Entity Framework dışında bir yolla ekliyorsanız işe yaramaz. Ayrıca, bir tablo üzerinde yeni bir null olmayan sütun oluşturuyorsanız, bu size varolan kayıtlar için varsayılan değeri ayarlama olanağı vermez. @devi'nin değerli bir eklentisi var.
d512

İlk yaklaşımınız neden "doğru" yol? Yapıcı yaklaşımında istenmeyen sorunlarla karşılaşacak mısınız?
WiteCastle

bir fikir meselesi ve bu değişim :) Ve muhtemelen hayır.
calebboyd

2
Belirli koşullar altında, yapıcı yaklaşımıyla ilgili sorunlar yaşadım; yedekleme alanında varsayılan bir değer belirlemek en az invaziv çözüm gibi görünüyor.
Lucent Fox

11

Ne yaptım, varlığın kurucusundaki değerleri başlattım

Not: DefaultValue özellikleri, özelliklerinizin değerlerini otomatik olarak ayarlamaz, bunu kendiniz yapmanız gerekir


4
Değeri ayarlayan kurucu ile ilgili sorun, EF işlemi gerçekleştirirken veritabanında bir güncelleme yapmasıdır.
Luka

Model ilk olarak varsayılan değerleri bu şekilde
Lucas

DefaultValue, yabancı topraklarda, mülklerinizle tek başına karşılaşamayacak kadar utangaç yaşayan bir türdür. Bir ses çıkarmayın, kolayca korkar - eğer duymak için yeterince yaklaşırsa. Açıkça belirtmek için +1.
Risadinha

8

@SedatKapanoglu yorumundan sonra, işe yarayan tüm yaklaşımımı ekliyorum, çünkü haklıydı, sadece akıcı API'yi kullanmak işe yaramıyor.

1- Özel kod oluşturucu oluşturun ve bir ColumnModel için üret seçeneğini geçersiz kılın.

   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- Yeni kod oluşturucuyu atayın:

public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
    public Configuration()
    {
        CodeGenerator = new ExtendedMigrationCodeGenerator();
        AutomaticMigrationsEnabled = false;
    }
}

3- Ek Açıklama oluşturmak için akıcı api kullanın:

public static void Configure(DbModelBuilder builder){    
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);            
}

Lütfen tam çözümümü görebiliyordum, nasıl çalıştığına dair tüm uygulamamı ekledim, teşekkürler.
Denny Puig

5

Basit! Sadece ek açıklama ekleyin.

[Required]
public bool MyField { get; set; }

sonuçta ortaya çıkan göç:

migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);

Doğru istiyorsanız, veritabanını güncellemeden önce geçişte defaultValue değerini true olarak değiştirin


otomatik oluşturulan geçiş daha sonra değiştirilebilir ve varsayılan değer hakkında affedeceksiniz
Fernando Torres

Basit ve çalışır, yeni sütunda yabancı anahtar kısıtlamasıyla mevcut bir tabloya hızla bir sütun eklemeye yardımcı olur. Teşekkürler
po10cySA

Ya varsayılan değerin olması gerekiyorsa true?
Christos Lytras

5

Yaklaşımımın "İlk Kod" kavramından kaçtığını itiraf ediyorum. Ancak, tablonun kendisinde sadece varsayılan değeri değiştirme yeteneğine sahipseniz ... yukarıda geçmeniz gereken uzunluklardan çok daha basit ... Tüm bu işleri yapmak için çok tembelim!

Neredeyse posterlerin orijinal fikri işe yarayacak gibi görünüyor:

[DefaultValue(true)]
public bool IsAdmin { get; set; }

Ben sadece tırnak ekleme hatası yaptığını düşündüm ... ama ne yazık ki böyle bir sezgisellik yok. Diğer öneriler benim için çok fazlaydı (tabloya girip değişiklikleri yapmak için gerekli ayrıcalıklara sahip olduğumu kabul ettim. Sonunda sadece eski moda bir şekilde yaptım. SQL Server tablosunda varsayılan değeri ayarladım ... Yani, zaten yeterli! Not: Ayrıca bir ekleme-taşıma ve güncelleştirme-veritabanı yaparken test ve değişiklikler sıkışmış. resim açıklamasını buraya girin


1

Sadece Model sınıfının varsayılan yapıcısını aşırı yükleyin ve kullanabileceğiniz veya kullanamayacağınız herhangi bir ilgili parametreyi geçirin. Bu şekilde, öznitelikler için kolayca varsayılan değerleri sağlayabilirsiniz. Aşağıda bir örnek verilmiştir.

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; }
    }

}

Bu öneri tamamen müşteri tarafı mantığıdır. Bu, yalnızca uygulamayı kullanarak veritabanıyla etkileşime gireceğiniz sürece çalışır. Birisi el ile veya başka bir uygulamadan kayıt eklemek istediği anda, şemada varsayılan bir ifade bulunmadığını ve bunun diğer istemcilere ölçeklenemeyeceğini fark edersiniz. Açıkça belirtilmemesine rağmen, bu meşru konu EF'in sütun tanımına varsayılan bir ifade yerleştiren bir geçiş oluşturma gereksinimi anlamına gelir.
Tom

0

Ürünler adında bir sınıf adınız ve bir IsActive alanınız olduğunu varsayalım. sadece bir oluşturucu oluşturmalısınız:

Public class Products
{
    public Products()
    {
       IsActive = true;
    }
 public string Field1 { get; set; }
 public string Field2 { get; set; }
 public bool IsActive { get; set; }
}

O zaman IsActive varsayılan değeriniz True!

Edite :

SQL ile bunu yapmak istiyorsanız şu komutu kullanın:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.IsActive)
        .HasDefaultValueSql("true");
}

Soru bu değildi. Veritabanının kendisindeki varsayılan değer kısıtlamaları hakkındaydı.
Gábor

4
@Hatef, düzenlemeniz yalnızca EF Core için geçerlidir. Soru EF 6 ile ilgili.
Michael

3
Bu HasDefaultValueSql, EF6
tatigo

0

27 Haziran 2016'da yayınlanan EF çekirdeğinde, varsayılan değeri ayarlamak için akıcı API kullanabilirsiniz. ApplicationDbContext sınıfına gidin, OnModelCreating yöntem adını bulun / oluşturun ve aşağıdaki akıcı API'yi ekleyin.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
        .Property(b => b.Active)
        .HasDefaultValue(true);
}

0

.NET Core 3.1'de model sınıfında aşağıdakileri yapabilirsiniz:

    public bool? Active { get; set; } 

DbContext OnModelCreating'de varsayılan değeri eklersiniz.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Foundation>()
            .Property(b => b.Active)
            .HasDefaultValueSql("1");

        base.OnModelCreating(modelBuilder);
    }

Sonuçta aşağıdaki veritabanında

resim açıklamasını buraya girin

Not: Mülkünüz için boş değer (bool?) Yoksa aşağıdaki uyarıyı alırsınız

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'.

-3

Ben varlık özelliği sadece Auto-Property Initializer kullanarak işi yapmak için yeterli olduğunu buldum.

Örneğin:

public class Thing {
    public bool IsBigThing{ get; set; } = false;
}

2
Bunun önce kodla işe yarayacağını düşünürdünüz. Ama EF 6.2 ile benim için olmadı.
user1040323

-4

Hmm ... Önce DB yapıyorum ve bu durumda, bu aslında çok daha kolay. EF6 değil mi? Sadece modelinizi açın, varsayılanı ayarlamak istediğiniz sütuna sağ tıklayın, özellikleri seçin ve bir "DefaultValue" alanı göreceksiniz. Sadece doldurun ve kaydedin. Kodu sizin için ayarlayacaktır.

Kilometre kodunuz ilk önce değişebilir, ancak bununla çalışmadım.

Diğer birçok çözümle ilgili sorun, başlangıçta çalışabilirken, modeli yeniden oluşturduğunuzda, makine tarafından oluşturulan dosyaya eklediğiniz herhangi bir özel kodu atayacağıdır.

Bu yöntem, edmx dosyasına ekstra bir özellik ekleyerek çalışır:

<EntityType Name="Thingy">
  <Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />

Ve kurucuya gerekli kodu ekleyerek:

public Thingy()
{
  this.Iteration = 1;

-5

MSSQL Server'daki tabloda ve sınıf kodu add özniteliğinde sütun için varsayılan değeri şu şekilde ayarlayın:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

aynı mülk için.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.