탐색 속성이없는 EF 코드 첫 번째 외래 키


91

다음 엔티티가 있다고 가정 해 보겠습니다.

public class Parent
{
    public int Id { get; set; }
}
public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
}

탐색 속성을 가질 필요없이 Parents 테이블에 대한 외래 키 제약 조건을 사용하여 ParentId가 데이터베이스에 생성되도록하는 코드 첫 번째 유창한 API 구문은 무엇입니까 ?

탐색 속성 Parent를 Child에 추가하면 다음과 같이 할 수 있습니다.

modelBuilder.Entity<Child>()
    .HasRequired<Parent>(c => c.Parent)
    .WithMany()
    .HasForeignKey(c => c.ParentId);

하지만이 특별한 경우에는 탐색 속성을 원하지 않습니다.


1
나는 이것이 실제로 EF만으로 가능하다고 생각하지 않는다. 당신은 아마도 그것을 설정하기 위해 수동 마이그레이션에서 원시 SQL을 사용해야 할 필요가있을 것이다.
Not loved

@LukeMcGregor 내가 두려워했던 것입니다. 당신이 그 대답을한다면 나는 그것이 옳다고 가정하고 기꺼이 받아 들일 것입니다. :-)
RationalGeek

탐색 속성이없는 특별한 이유가 있습니까? 탐색 속성을 비공개로 만들면 엔티티 외부에서 볼 수 없지만 EF를 기쁘게 할 것입니다. (참고 나는 이것을 시도하지 않았지만 이것이 효과가 있다고 생각합니다-개인 속성 매핑에 대한이 게시물을보세요 romiller.com/2012/10/01/… )
Pawel

6
나는 그것을 필요로하지 않기 때문에 그것을 원하지 않는다. 프레임 워크의 요구 사항을 충족하기 위해 불필요한 요소를 디자인에 추가하는 것을 좋아하지 않습니다. nav prop에 넣으면 나를 죽일까요? 아뇨. 사실 그게 당분간 제가 한 일입니다.
RationalGeek

관계를 구축하려면 항상 적어도 한쪽에 탐색 속성이 필요합니다. 자세한 내용은 stackoverflow.com/a/7105288/105445
Wahid Bitar 2014 년

답변:


63

EF Code First Fluent API로는 불가능합니다. 데이터베이스에서 외래 키 제약 조건을 만들려면 항상 하나 이상의 탐색 속성이 필요합니다.

Code First 마이그레이션을 사용하는 경우 패키지 관리자 콘솔 ( add-migration SomeNewSchemaName) 에서 새 코드 기반 마이그레이션을 추가하는 옵션이 있습니다 . 모델 또는 매핑으로 변경 한 경우 새 마이그레이션이 추가됩니다. 아무것도 변경하지 않은 경우 add-migration -IgnoreChanges SomeNewSchemaName. 이 경우 마이그레이션에는 비어있는 UpDown메소드 만 포함됩니다 .

그런 Up다음 다음을 추가하여 메서드를 수정할 수 있습니다 .

public override void Up()
{
    // other stuff...

    AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id",
        cascadeDelete: true); // or false
    CreateIndex("ChildTableName", "ParentId"); // if you want an index
}

이 마이그레이션을 실행하면 ( update-database패키지 관리 콘솔에서) 다음과 유사한 SQL 문이 실행됩니다 (SQL Server의 경우).

ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName]
FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id])

CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])

또는 마이그레이션없이 다음을 사용하여 순수 SQL 명령을 실행할 수 있습니다.

context.Database.ExecuteSqlCommand(sql);

여기서는 context파생 된 컨텍스트 클래스의 인스턴스이며 sql위의 SQL 명령 (문자열)입니다.

이 모든 EF에는 ParentId관계를 설명하는 외래 키라는 단서가 없습니다 . EF는이를 일반 스칼라 속성으로 만 간주합니다. 어떻게 든 위의 모든 것은 SQL 관리 도구를 열고 수동으로 제약 조건을 추가하는 것에 비해 더 복잡하고 느린 방법입니다.


2
단순화는 자동화에서 비롯됩니다. 내 코드가 배포되는 다른 환경에 액세스 할 수 없습니다. 코드에서 이러한 변경을 수행 할 수 있다는 것은 저에게 좋습니다. 하지만 나는 snark를 좋아합니다 :)
pomeroy

나는 또한 엔티티에 속성을 설정했다고 생각하므로 부모 ID에 [ForeignKey("ParentTableName")]. 그러면 부모 테이블에있는 키에 속성이 연결됩니다. 이제 하드 코딩 된 테이블 이름이 있습니다.
Triynko

2
그것은 왜 아래에 다른 의견이도 정답으로 표시됩니다 분명히 볼 불가능 아니다
이고르 수

111

이 게시물에 대한하지만 Entity Framework하지 Entity Framework Core, 그것은 (나는 V1.1.2을 사용하고 있습니다) 엔티티 프레임 워크 코어를 사용하여 같은 일을 달성하기 위해 원하는 사람을 위해 유용 할 수 있습니다.

나는 (비록 그들이있는 거 좋은) 탐색 속성을하지 않아도 내가 DDD를 연습하고 내가 원하기 때문에 Parent그리고 Child두 개의 별도의 집계 뿌리가 될 수 있습니다. 인프라 별 Entity Framework탐색 속성이 아닌 외래 키를 통해 서로 대화 할 수 있기를 바랍니다 .

당신이해야 할 모든 이용하여 한쪽의 관계를 구성하는 것입니다 HasOne하고 WithMany(그들은 결국이 아니에요) 탐색 속성을 지정하지 않고.

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}

    protected override void OnModelCreating(ModelBuilder builder)
    {
        ......

        builder.Entity<Parent>(b => {
            b.HasKey(p => p.Id);
            b.ToTable("Parent");
        });

        builder.Entity<Child>(b => {
            b.HasKey(c => c.Id);
            b.Property(c => c.ParentId).IsRequired();

            // Without referencing navigation properties (they're not there anyway)
            b.HasOne<Parent>()    // <---
                .WithMany()       // <---
                .HasForeignKey(c => c.ParentId);

            // Just for comparison, with navigation properties defined,
            // (let's say you call it Parent in the Child class and Children
            // collection in Parent class), you might have to configure them 
            // like:
            // b.HasOne(c => c.Parent)
            //     .WithMany(p => p.Children)
            //     .HasForeignKey(c => c.ParentId);

            b.ToTable("Child");
        });

        ......
    }
}

엔티티 속성을 구성하는 방법에 대한 예제도 제공하지만 여기서 가장 중요한 것은 HasOne<>, WithMany()HasForeignKey()입니다.

도움이 되었기를 바랍니다.


8
이것은 EF Core에 대한 정답이며 DDD를 연습하는 사람들에게는 필수입니다.
티아고 실바

1
탐색 속성을 제거하기 위해 무엇이 변경되었는지 명확하지 않습니다. 명확히 해 주시겠습니까?
andrew.rockwell

3
@ andrew.rockwell 다음을 참조 HasOne<Parent>()하고 .WithMany()THIE 자식 구성합니다. 어쨌든 정의 된 탐색 속성이 없기 때문에 탐색 속성을 전혀 참조하지 않습니다. 내 업데이트로 더 명확하게 만들려고 노력할 것입니다.
David Liang

대박. 덕분에 @DavidLiang
andrew.rockwell

2
@ mirind4는 OP의 특정 코드 샘플과 관련이 없습니다. DDD에 따라 다른 집계 루트 엔터티를 각 DB 테이블에 매핑하는 경우 해당 루트 엔터티는 ID를 통해서만 서로 참조해야하며 전체 참조를 포함해서는 안됩니다. 다른 AR 엔티티에. 사실, DDD에서는 int / log / guid (특히 AR 엔티티)와 같은 기본 유형이있는 props를 사용하는 대신 엔티티의 ID를 값 객체로 만드는 것이 일반적이며, 원시 집착을 피하고 값을 통해 다른 AR이 엔티티를 참조 할 수 있도록 허용합니다. 개체 ID 유형. HTH
Thiago Silva

21

DataAnotations를 사용하고 탐색 속성을 노출하고 싶지 않은 사람들을위한 작은 힌트-사용 protected

public class Parent
{
    public int Id { get; set; }
}
public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }

    protected virtual Parent Parent { get; set; }
}

그것은 그게 전부 -와 외래 키 cascade:true후가 Add-Migration생성됩니다.


1
개인으로도 가능합니다
Marc Wittke 2015-08-20

2
자식을 만들 때 지정해야 Parent하거나 ParentId?
George Mauer 2015

5
@MarcWittke virtual속성은 private.
LINQ

@GeorgeMauer : 좋은 질문입니다! 기술적으로 둘 중 하나가 작동 할뿐만 아니라 개발자 (특히 새로운 올 사람이) 있는지 무엇에 통과하지 않기 때문에 당신이이 같은 모순 코드가있을 때 문제가된다.
데이비드 리앙

14

EF Core의 경우 탐색 속성을 제공 할 필요가 없습니다. 관계의 한쪽에 간단히 외래 키를 제공 할 수 있습니다. Fluent API를 사용한 간단한 예 :

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.Configuring.FluentAPI.Samples.Relationships.NoNavigation
{
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
             modelBuilder.Entity<Post>()
                .HasOne<Blog>()
                .WithMany()
                .HasForeignKey(p => p.BlogId);
        }
    }

    public class Blog
    {
         public int BlogId { get; set; }
         public string Url { get; set; }
    }

    public class Post
    {
         public int PostId { get; set; }
         public string Title { get; set; }
         public string Content { get; set; }

        public int BlogId { get; set; }
    }
}

2

.Net Core 3.1, EntityFramework 3.1.3을 사용하고 있습니다. 나는 주변을 찾고 있었고 내가 생각해 낸 해결책은 HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty). 다음과 같이 일대일 관계를 만들 수 있습니다.

builder.entity<Parent>()
.HasOne<Child>()
.WithOne<>()
.HasForeginKey<Child>(c => c.ParentId);

builder.entity<Child>()
    .Property(c => c.ParentId).IsRequired();

이것이 도움이되거나 적어도 방법을 사용하는 HasForeginKey방법 에 대한 다른 아이디어를 제공하기를 바랍니다 .


0

탐색 속성을 사용 하지 않는 이유 는 클래스 종속성입니다. 내 모델을 몇 개의 어셈블리로 분리하여 다른 프로젝트에서 어떤 조합으로도 사용하거나 사용할 수 없습니다. 따라서 다른 어셈블리의 클래스에 대한 nagivation 속성이있는 엔터티가있는 경우 해당 어셈블리를 참조해야합니다.이 어셈블리는 피하고 싶습니다 (또는 전체 데이터 모델의 일부를 사용하는 모든 프로젝트는 모든 것을 포함합니다).

그리고 마이그레이션 (자동 마이그레이션 사용)과 초기 DB 생성에 사용되는 별도의 마이그레이션 앱이 있습니다. 이 프로젝트는 명백한 이유로 모든 것을 참조합니다.

해결책은 C 스타일입니다.

  • 링크를 통해 마이그레이션 프로젝트에 대상 클래스가있는 "복사"파일 ( altVS의 키로 끌어서 놓기 )
  • nagivation 속성 (및 FK 속성) 비활성화 #if _MIGRATION
  • 마이그레이션 앱에서 해당 전 처리기 정의를 설정하고 모델 프로젝트에서 설정하지 않으므로 아무것도 참조하지 않습니다 ( Contact예제에서 클래스로 어셈블리를 참조하지 않음 ).

견본:

    public int? ContactId { get; set; }

#if _MIGRATION
    [ForeignKey(nameof(ContactId))]
    public Contact Contact { get; set; }
#endif

물론 동일한 방법으로 using지시문을 비활성화 하고 네임 스페이스를 변경해야합니다.

그 후 모든 소비자는 해당 속성을 일반적인 DB 필드로 사용할 수 있지만 (필요하지 않은 경우 추가 어셈블리를 참조하지 않음) DB 서버는 이것이 FK이고 캐스 케이 딩을 사용할 수 있음을 알게됩니다. 매우 더러운 솔루션. 하지만 작동합니다.

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