EF Code First中的计算列

问题描述 投票:67回答:6

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

c# sql-server ef-code-first entity-framework-5 calculated-columns
6个回答
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是一个好主意,因为你可能永远不想在应用程序代码中设置它。私有制定者没有实体框架。


28
投票
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 */ }
    }
}

参考文献:


3
投票

从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


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

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提到我认为将计算值保存在数据库中并不是一个好主意。只是一些想法。


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

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

© www.soinside.com 2019 - 2024. All rights reserved.