EF Code First中的计算列

时间:2013-03-23 09:44:42

标签: c# sql-server ef-code-first entity-framework-5 calculated-columns

我需要在我的数据库中有一列由数据库计算为(行数之和) - (行数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中实现这一目标?

7 个答案:

答案 0 :(得分:119)

您可以在数据库表中创建computed columns。在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是个好主意,因为您可能永远不想在应用程序代码中设置它。私有制定者没有实体框架。

答案 1 :(得分:27)

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 */ }
    }
}

参考文献:

答案 2 :(得分:2)

一种方法是使用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

答案 3 :(得分:2)

截至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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

我会通过使用视图模型来解决这个问题。例如,不是将FirstTable类作为数据库实体而不是更好的只是拥有一个名为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提到我不认为将计算值保存在数据库中是个好主意。只是一些想法。

答案 6 :(得分:-1)

我在尝试使用带有字符串列“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; }

这种方法有什么好处,你可以获得自动slug生成,而不会暴露slug setter。 .ToUrlSlug()方法不是这篇文章的重要部分,您可以使用任何代替它来完成您需要完成的工作。干杯!