Entity Framework 6 코드 첫 번째 기본값


203

특정 속성에 기본값을 부여하는 "우아한"방법이 있습니까?

아마도 DataAnnotations에 의해 다음과 같습니다.

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

감사합니다.


아마도 생성자에서 시도해보십시오 this.Active = true;. 가져 오기 할 때 DB 값이 우선하지만, 변경 추적 을 통해 값을 업데이트하려고 할 때 변경 내용을 볼 수 있으므로 새 항목을 가져 와서 가져 오기없이 업데이트를 위해 엔티티를 먼저 첨부하는 경우주의해야한다고 생각합니다 . 오랜 시간 동안 EF를 사용하지 않았기 때문에 코멘트인데, 이것이 어둠 속에서 촬영 된 것 같은 느낌이 듭니다.
AaronLS

3
답변 주셔서 감사합니다. 지금 까지이 방법을 사용 했지만 stackoverflow.com/a/5032578/2913441 더 나은 방법이 있다고 생각했습니다.
marino-krk

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

Microsoft 문서에서 "데이터 주석을 사용하여 기본값을 설정할 수 없습니다"라고 말합니다.
hmfarimani

답변:


166

코드를 먼저 마이그레이션하여 수동으로 편집하면됩니다.

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

6
영업 이익은 특별히 설정하지 않는 경우 확실이 작동하고 있지 않다 Activetrue만들 때 Event개체뿐만 아니라. 기본값은 항상 falsenullable이 아닌 bool 속성에 있으므로 변경하지 않으면 엔터티 프레임 워크가 db에 저장하는 것입니다. 아니면 뭔가 빠졌습니까?
GFoley83

6
@ GFoley83, 그렇습니다. 이 방법은 데이터베이스 수준에서만 기본 제약 조건을 추가합니다. 완전한 솔루션을 위해서는 엔티티 생성자에 기본값을 할당하거나 위의 답변과 같이 백업 필드와 함께 속성을 사용해야합니다
gdbdable

1
이것은 기본 유형에 적용됩니다. DATETIMEOFFSET과 같은 경우, defaultValueSql : "SYSDATETIMEOFFSET"을 사용하고 defaultValue가 아닌이 defaultValue로 System.DateTimeOffset.Now를 사용하여 현재 시스템 datetimeoffset 값의 문자열로 해석합니다.
OzBob

3
AFAIK 수동 변경은 재 비계 마이그레이션의 경우 분실 것
알렉산더 Powolozki에게

1
난 당신이 다음 첫 번째 데이터베이스 수준에서 제거하여 DAL 매핑 변경 마이그레이션을 쓰기 shuld 생각 @ninbit
gdbdable을

74

오랜 시간이 지났지 만 다른 사람들에게는 메모를 남깁니다. 속성에 필요한 것을 달성하고 원하는대로 모델 클래스 필드를 해당 속성으로 장식했습니다.

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

이 두 기사의 도움을 받았습니다.

제가 한:

속성 정의

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

컨텍스트의 "OnModelCreating"에서

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

사용자 정의 SqlGenerator에서

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

그런 다음 마이그레이션 구성 생성자에서 사용자 정의 SQL 생성기를 등록하십시오.

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

6
[SqlDefaultValue(DefaultValue = "getutcdate()")]모든 엔터티 를 착용하지 않고도 전 세계적으로 할 수 있습니다 . 1) 간단히 제거 modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue)); 2) 추가modelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));
Daniel Skowroński

1
사용자 지정 SqlGenerator는 어디에 있습니까?
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

3
사용자 정의 SqlGenerator 여기에서 온 : andy.mehalick.com/2014/02/06/...
ravinsp

1
@ravinsp SqlGenerator 코드가 아닌 올바른 정보로 마이그레이션하도록 MigrationCodeGenerator 클래스를 사용자 정의하지 않는 이유는 무엇입니까? SQL 코드는 마지막 단계입니다 ...
Alex

@ 알렉스 가치! 그것은 또한 작동하고 SQL 코드를 주입하는 것보다 더 우아 할 것입니다. 그러나 C # MigrationCodeGenerator를 재정의하는 복잡성이 확실하지 않습니다.
ravinsp

73

위의 답변은 실제로 도움이되었지만 솔루션의 일부만 제공했습니다. 가장 큰 문제는 기본값 속성을 제거하자마자 데이터베이스 열의 제약 조건이 제거되지 않는다는 것입니다. 따라서 이전 기본값은 여전히 ​​데이터베이스에 남아 있습니다.

다음은 속성 제거에 대한 SQL 제한 조건 제거를 포함하여 문제점에 대한 완전한 솔루션입니다. 또한 .NET Framework의 기본 DefaultValue특성을 재사용하고 있습니다.

용법

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

이것이 작동하려면 IdentityModels.csConfiguration.cs 파일 을 업데이트해야 합니다.

IdentityModels.cs 파일

ApplicationDbContext클래스 에서이 메소드 추가 / 업데이트

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 파일

다음 Configuration과 같이 사용자 지정 SQL 생성기를 등록 하여 클래스 생성자를 업데이트하십시오 .

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

그런 다음 사용자 지정 Sql 생성기 클래스를 추가합니다 ( Configuration.cs 파일 또는 별도의 파일에 추가 할 수 있음 ).

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
이 접근법은 나에게 완벽하게 작동했습니다. 내가 만든 한 가지 개선 사항은 동일한 시나리오로 Generate (CreateTableOperation createTableOperation) 및 Generate (AddColumnOperation addColumnOperation)을 재정 의하여 해당 시나리오도 포착하는 것이 었습니다. 또한 값만 확인합니다. 기본값이 빈 문자열이되기를 원했기 때문에 NewValue는 null입니다.
Delorian

2
@Delorian 내 답변을 업데이트했습니다. 귀하의 의견에 감사드립니다
Jevgenij Martynenko

1
롤백이 둘 이상의 제약 조건을 삭제하는 인스턴스를 지원하도록 게시물을 수정했습니다. @con이 이미 선언되었다는 스크립트에서 오류가 발생합니다. 카운터를 보유하고 단순히 증가시키기 위해 개인 변수를 만들었습니다. 또한 제약 조건을 만들 때 EF가 SQL에 보내는 내용과 더 일치하도록 드롭 제약 조건의 형식을 변경했습니다. 이것에 대단한 일!
Brad

1
솔루션에 감사하지만 두 가지 문제가 있습니다. 1. 테이블 이름에는 대괄호가 필요합니다. 2. 업데이트시 새 값이 설정되지 않고 기본값이 설정되었습니다!
Omid-RH

1
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]속성을 설정해야 합니까? 그렇다면 왜 그렇습니까? 내 테스트에서 그것을 떠나는 데 아무런 효과가없는 것 같습니다.
RamNow

26

모델 속성이 '자동 속성'일 필요는 없지만 더 쉽습니다. 그리고의 DefaultValue 속성은 정말 인정 대답 만 정보를 메타 데이터입니다 여기가 생성자 접근 한 대안이다.

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

이 특정 클래스를 사용하여 데이터를 작성하고 소비하는 응용 프로그램에서만 작동합니다. 데이터 액세스 코드가 중앙 집중화 된 경우 일반적으로 문제가되지 않습니다. 모든 애플리케이션 에서 값을 업데이트하려면 기본값을 설정하도록 데이터 소스를 구성해야합니다. Devi의 답변 은 마이그레이션, SQL 또는 데이터 소스가 사용하는 언어를 사용하여 수행하는 방법을 보여줍니다.


21
참고 : 데이터베이스 의 기본값은 설정되지 않습니다 . 엔터티를 사용하지 않는 다른 프로그램은 해당 기본값을 얻지 못합니다.
Eric J.

대답 의이 부분이지만 Entity Framework 이외의 방법으로 레코드를 삽입하는 경우 작동하지 않습니다. 또한 테이블에 널이 아닌 새 컬럼을 작성하는 경우 기존 레코드의 기본값을 설정할 수 없습니다. @devi는 아래에 귀중한 추가 사항이 있습니다.
d512

첫 번째 접근 방식이 왜 "올바른"방법입니까? 생성자 접근 방식으로 의도하지 않은 문제가 발생합니까?
WiteCastle

의견의 문제와 그 변화 :) 아마 아닐 것입니다.
calebboyd

2
특정 상황에서는 생성자 접근 방식에 문제가있었습니다. 백업 필드에 기본값을 설정하는 것이 가장 침습적 인 솔루션 인 것 같습니다.
루슨트 폭스

11

내가 한 일, 엔티티의 생성자에서 값을 초기화했습니다.

참고 : DefaultValue 속성은 속성 값을 자동으로 설정하지 않으므로 직접해야합니다.


4
생성자가 값을 설정하는 문제는 트랜잭션을 커밋 할 때 EF가 데이터베이스를 업데이트한다는 것입니다.
Luka

모델은 먼저 이런 식으로 기본값을 작성합니다
Lucas

DefaultValue는 멀리 떨어져있는 외국 땅에 서식하며 너무 부끄러워서 재산을 직접 접할 수없는 종입니다. 소리를 내지 마십시오. 쉽게 두려워 할 수 있습니다. 소리가 들릴 정도로 가까이 오면 말입니다. 전혀 명백하지 않다고 말하면 +1.
Risadinha

8

@SedatKapanoglu 의견 후에 유창한 API를 사용하면 작동하지 않는 모든 접근 방식을 추가하고 있습니다. 유창한 API를 사용하면 작동하지 않습니다.

1- 사용자 정의 코드 생성기를 작성하고 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- 새로운 코드 생성기를 할당하십시오 :

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

3- 유창한 API를 사용하여 주석을 작성하십시오.

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

내 완전한 솔루션을 볼 수 있었고 작동 방식에 대한 모든 구현을 추가했습니다. 감사합니다.
Denny Puig

5

간단 해! 필수로 주석을 달기 만하면됩니다.

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

결과 마이그레이션은 다음과 같습니다.

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

true를 원하면 데이터베이스를 업데이트하기 전에 마이그레이션에서 defaultValue를 true로 변경하십시오.


그러면 자동 생성 된 마이그레이션을 변경할 수 있으며 기본값에 대해 잊어 버릴 것입니다
Fernando Torres

간단하고 작동하여 새 열에 외래 키 제약 조건을 적용하여 기존 테이블에 열을 빠르게 추가 할 수 있습니다. 감사
po10cySA

만약 우리가 기본값이 필요하다면 true어떨까요?
Christos Lytras

5

본인의 접근 방식이 "코드 우선"개념 전체를 벗어남을 인정합니다. 그러나 테이블 자체의 기본값을 변경할 수있는 능력이 있다면 ... 위에서 수행 해야하는 길이보다 훨씬 간단합니다 ... 나는 모든 작업을 수행하기에는 너무 게으르다!

포스터 독창적 인 아이디어가 작동하는 것처럼 보입니다.

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

나는 그들이 단지 따옴표를 추가하는 실수를했다고 생각했지만 ... 아직 직관적이지 않습니다. 다른 제안은 나에게 너무 많았습니다 (테이블에 가서 변경을 수행하는 데 필요한 권한이 있음을 부여했습니다 ... 모든 개발자가 모든 상황에 처할 수는 없습니다). 결국 나는 방금 구식으로했습니다. SQL Server 테이블에서 기본값을 설정했습니다 ... 이미 충분합니다. 참고 : 추가 마이그레이션 및 데이터베이스 업데이트를 수행 한 결과 변경 사항이 적용되었습니다. 여기에 이미지 설명을 입력하십시오


1

Model 클래스의 기본 생성자를 오버로드하고 사용하거나 사용하지 않을 관련 매개 변수를 전달하십시오. 이를 통해 속성에 대한 기본값을 쉽게 제공 할 수 있습니다. 아래는 예입니다.

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

}

이 제안은 전적으로 고객 측 논리입니다. 응용 프로그램을 사용하여 데이터베이스와 만 상호 작용하는 한 "작동"합니다. 누군가가 수동으로 또는 다른 응용 프로그램에서 레코드를 삽입하려고하면 스키마에 기본식이없고 다른 클라이언트로 확장 할 수 없다는 단점이 있습니다. 명시 적으로 언급되어 있지는 않지만이 합법적 인 주제는 EF가 기본 표현식을 열 정의에 배치하는 마이그레이션을 작성하도록 요구합니다.
Tom

0

Products라는 클래스 이름이 있고 IsActive 필드가 있다고 가정합니다. 그냥 생성자가 필요합니다.

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

그런 다음 IsActive 기본값은 True입니다!

에디 테 :

SQL 로이 작업을 수행하려면 다음 명령을 사용하십시오.

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

단순히 이것은 문제가되지 않았습니다. 데이터베이스 자체의 기본값 제약에 관한 것입니다.
Gábor

4
@Hatef, 편집 내용은 EF Core에만 적용됩니다. 문제는 EF 6에 관한 것입니다.
Michael

3
이 HasDefaultValueSql은 EF6에서 사용할 수 없습니다
tatigo

0

2016 년 6 월 27 일에 릴리스 된 EF core에서는 유창한 API를 사용하여 기본값을 설정할 수 있습니다. ApplicationDbContext 클래스로 이동하여 메소드 이름 OnModelCreating을 찾거나 작성하고 다음과 같은 유창한 API를 추가하십시오.

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

0

.NET Core 3.1에서는 모델 클래스에서 다음을 수행 할 수 있습니다.

    public bool? Active { get; set; } 

DbContext OnModelCreating에서 기본값을 추가하십시오.

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

        base.OnModelCreating(modelBuilder);
    }

데이터베이스에서 다음 결과

여기에 이미지 설명을 입력하십시오

참고 : 속성에 nullable (bool?)이 없으면 다음 경고가 표시됩니다

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

엔터티 속성에 자동 속성 이니셜 라이저를 사용하는 것만으로도 작업을 완료 할 수 있습니다.

예를 들면 다음과 같습니다.

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

2
먼저 코드와 함께 작동한다고 생각할 것입니다. 그러나 EF 6.2에서는 그렇지 않았습니다.
user1040323

-4

흠 ... 나는 DB를 먼저하고,이 경우 실제로는 훨씬 쉽다. EF6 맞습니까? 모델을 열고 기본값을 설정하려는 열을 마우스 오른쪽 버튼으로 클릭하고 속성을 선택하면 "DefaultValue"필드가 표시됩니다. 그냥 작성하고 저장하십시오. 코드가 설정됩니다.

마일리지는 코드마다 먼저 다를 수 있지만 그와 함께 일하지 않았습니다.

다른 많은 솔루션의 문제점은 모델을 다시 빌드하자마자 처음에는 작동 할 수 있지만 시스템에서 생성 된 파일에 삽입 한 모든 사용자 정의 코드는 버리게된다는 것입니다.

이 방법은 edmx 파일에 추가 속성을 추가하여 작동합니다.

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

그리고 필요한 코드를 생성자에 추가하여 :

public Thingy()
{
  this.Iteration = 1;

-5

다음과 같이 MSSQL Server 및 클래스 코드 add 속성에서 테이블의 열 기본값을 설정하십시오.

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

같은 속성에 대해.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.