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.