EF Code First의 계산 된 열


80

데이터베이스에서 (행 합계)-(행 합계 b)로 계산 된 데이터베이스에 하나의 열이 있어야합니다. 코드 우선 모델을 사용하여 데이터베이스를 만들고 있습니다.

내가 의미하는 바는 다음과 같습니다.

public class Income {
      [Key]
      public int UserID { get; set; }
      public double inSum { get; set; }
}

public class Outcome {
      [Key]
      public int UserID { get; set; }
      public double outSum { get; set; }
}

public class FirstTable {
      [Key]
      public int UserID { get; set; }
      public double Sum { get; set; } 
      // This needs to be calculated by DB as 
      // ( Select sum(inSum) FROM Income WHERE UserID = this.UserID) 
      // - (Select sum(outSum) FROM Outcome WHERE UserID = this.UserID)
}

EF CodeFirst에서이를 어떻게 달성 할 수 있습니까?

답변:


137

데이터베이스 테이블에 계산 된 열 을 만들 수 있습니다 . EF 모델에서는 속성을 사용하여 해당 속성에 주석을 추가하기 만하면됩니다 DatabaseGenerated.

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public double Summ { get; private set; } 

또는 유창한 매핑 :

modelBuilder.Entity<Income>().Property(t => t.Summ)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)

Matija Grcic이 제안 하고 주석에서 제안했듯이 속성을 만드는 것이 좋습니다 private set. 왜냐하면 응용 프로그램 코드에서 설정하고 싶지 않기 때문입니다. Entity Framework에는 개인 setter에 문제가 없습니다.

참고 : EF .NET Core의 경우 HasDatabaseGeneratedOption이 없기 때문에 ValueGeneratedOnAddOrUpdate 를 사용해야 합니다. 예 :

modelBuilder.Entity<Income>().Property(t => t.Summ)
    .ValueGeneratedOnAddOrUpdate()

26
나는 그것에 대해 알고 있지만 EF를 통해 내 데이터베이스에 계산하는 수식을 추가하여 콘솔 commad update-database에 의해 생성되도록하려면 어떻게해야합니까?
CodeDemen 2013 년

10
질문에 이것을 명확하게 설명하십시오. 이는 마이그레이션에서 계산 된 열을 생성하기를 원한다는 것을 의미합니다. 여기에 예가 있습니다 .
Gert Arnold

2
세터는 비공개 여야합니까?
Cherven

1
@Cherven 예, 아마도 그렇게하는 것이 좋습니다.
Gert Arnold

6
이 답변은 EF Core에 대해 추가하도록 업데이트해야하며, 모델 작성기는 존재하지 않기 ValueGeneratedOnAddOrUpdate()때문에 메서드를 사용해야 HasDatabaseGeneratedOption합니다. 그렇지 않으면 훌륭한 대답입니다.
Max

34
public string ChargePointText { get; set; }

public class FirstTable 
{
    [Key]
    public int UserID { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]      
    public string Summ 
    {
        get { return /* do your sum here */ }
        private set { /* needed for EF */ }
    }
}

참조 :


비공개 세트 추가에 +1. 새 개체를 추가 할 때 계산 열을 설정하면 안됩니다.
Taher 2014-06-12

1
이 질문을 다시 보았습니다. 이제 해당 부분 /* do your sum here */이 적용되지 않는 것을 알았습니다 . 속성이 클래스 내에서 계산되는 경우 [NotMapped]. 그러나 값은 데이터베이스에서 제공되므로 단순한 get속성 이어야 합니다.
Gert Arnold

1
@GertArnold는 여기를 참조 하십시오 - "FullName 속성은 데이터베이스에 의해 계산되므로 FirstName 또는 LastName 속성을 변경하는 즉시 개체 측에서 동기화되지 않습니다. 운 좋게도 두 가지 장점을 모두 활용할 수 있습니다. 여기에 FullName 속성의 getter에 계산을 다시 추가하여 "
AlexFoxGill

@AlexFoxGill 그렇다면 요점은 무엇입니까? 계산 된 값이 "동기화되지 않은"경우 매번 동적으로 다시 계산할 경우 왜 계산 된 값을 저장해야합니까?
Rudey

LINQ 쿼리에서 계산 된 열을 사용할 수 있도록 @RuudLenders.
AlexFoxGill

15

2019 년부터 EF 코어를 사용하면 유창한 API를 사용하여 깔끔한 방식으로 열을 계산할 수 있습니다.

DisplayName정의하려는 계산 된 열 이라고 가정하고 평소와 같이 속성을 정의해야하며, 할당을 방지하기 위해 개인 속성 접근자를 사용하여 가능할 수 있습니다.

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    // this will be computed
    public string DisplayName { get; private set; }
}

그런 다음 모델 작성기에서 열 정의로 주소를 지정합니다.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .Property(p => p.DisplayName)
        // here is the computed query definition
        .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
}

자세한 내용은 MSDN을 참조하십시오 .


3

EF6에서는 다음과 같이 계산 된 속성을 무시하도록 매핑 설정을 구성 할 수 있습니다.

모델의 get 속성에 대한 계산을 정의합니다.

public class Person
{
    // ...
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName => $"{FirstName} {LastName}";
}

그런 다음 모델 구성에서 무시하도록 설정하십시오.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //...
    modelBuilder.Entity<Person>().Ignore(x => x.FullName)
}

1

한 가지 방법은 LINQ를 사용하는 것입니다.

var userID = 1; // your ID
var income = dataContext.Income.First(i => i.UserID == userID);
var outcome = dataContext.Outcome.First(o => o.UserID == userID);
var summ = income.inSumm - outcome.outSumm;

POCO 객체 내에서 할 수는 public class FirstTable있지만 좋은 디자인이 아니라고 생각 하기 때문에 권장 하지 않습니다.

또 다른 방법은 SQL보기를 사용하는 것입니다. Entity Framework를 사용하여 테이블과 같은보기를 읽을 수 있습니다. 그리고 뷰 코드 내에서 계산이나 원하는 것을 할 수 있습니다. 다음과 같은보기를 만드십시오.

-- not tested
SELECT FirstTable.UserID, Income.inCome - Outcome.outCome
  FROM FirstTable INNER JOIN Income
           ON FirstTable.UserID = Income.UserID
       INNER JOIN Outcome
           ON FirstTable.UserID = Outcome.UserID

0

뷰 모델을 사용하여 이에 대해 설명하겠습니다. 예를 들어 FirstTable 클래스를 db 엔터티로 사용하는 것보다 FirstTable이라는 뷰 모델 클래스를 갖고 계산 된 합계를 포함하는이 클래스를 반환하는 데 사용되는 함수를 갖는 것이 더 낫지 않을까요? 예를 들어 클래스는 다음과 같습니다.

public class FirstTable {
  public int UserID { get; set; }
  public double Sum { get; set; }
 }

그런 다음 계산 된 합계를 반환하는 호출하는 함수가 있습니다.

public FirsTable GetNetSumByUserID(int UserId)
{
  double income = dbcontext.Income.Where(g => g.UserID == UserId).Select(f => f.inSum);
  double expenses = dbcontext.Outcome.Where(g => g.UserID == UserId).Select(f => f.outSum);
  double sum = (income - expense);
  FirstTable _FirsTable = new FirstTable{ UserID = UserId, Sum = sum};
  return _FirstTable;
}

기본적으로 SQL 뷰와 동일하고 @Linus가 언급했듯이 데이터베이스에 계산 된 값을 유지하는 것이 좋은 생각이 아니라고 생각합니다. 약간의 생각.


+1 for- I don't think it would be a good idea keeping the computed value in the database특히 부하가 높을 때 교착 상태가 시작되는 Azure SQL을 사용하려는 경우.
Piotr Kula

1
@ppumkin 대부분의 경우 집계 계산은 DB에서 더 잘 수행됩니다. '가장 최근 댓글 ID'와 같은 것을 생각해보세요. 하나만 가져 오기 위해 모든 CommentID를 되돌릴 필요는 없습니다. 데이터와 메모리를 낭비 할뿐만 아니라 DB 자체에 대한 부하도 증가하고 실제로 공유 잠금을 더 많은 행에 더 오래 둡니다. 또한 항상 많은 행을 업데이트 해야하므로 검토가 필요한 디자인 일 것입니다.
JoeBrockhaus

확인. 나는 그것들을 메모리, 계산 된 값, 캐시에 보관하는 것을 의미했습니다. 모든 방문자에 대해 데이터베이스로 이동하지 마십시오. 문제를 일으킬 것입니다. 나는 이것이 너무 많이 일어나는 것을 보았다.
Piotr Kula

-2

문자열 열 "Slug"가있는 EF Code First 모델을 다른 문자열 열 "Name"에서 파생 시키려고 할 때이 질문을 우연히 발견했습니다. 제가 취한 접근 방식은 약간 달랐지만 잘 진행되었으므로 여기서 공유하겠습니다.

private string _name;

public string Name
{
    get { return _name; }
    set
    {
        _slug = value.ToUrlSlug(); // the magic happens here
        _name = value; // but don't forget to set your name too!
    }
}

public string Slug { get; private set; }

이 접근 방식의 좋은 점은 슬러그 세터를 노출하지 않고 자동 슬러그 생성을 얻는다는 것입니다. .ToUrlSlug () 메서드는이 게시물의 중요한 부분이 아닙니다. 필요한 작업을 수행하기 위해 그 자리에서 무엇이든 사용할 수 있습니다. 건배!


실제로 연결 Slug되는 모든 비트를 편집하지 않았습니까 Name? 현재 작성된대로 Namesetter는 컴파일도 안됩니다.
Auspex

아니요, 편집하기 전에는 실행 가능한 예였습니다. 편집 후에는 의미가 없습니다.
Auspex
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.