创建数据库生成的计算字段 CodeFirst/EntityFramework

Creating Database Generated Computed Field CodeFirst/EntityFramework

我正在尝试通过 Initial : Migration 文件在数据库中创建一个列,类似于以下 this guide

我有一个 Transaction table 和一个 Account table。我正在尝试生成 Account 属性 ReconciledBalance。它应该计算交易中所有余额的总和,其中 IsReconciled 为真且 IsActive 为真。

// Example of calculation
ReconciledBalance = Transactions.Where(t => t.IsActive == true && t.IsReconciled == true).Sum(x => x.Amount)

这就是我要在计算列中完成的。我想知道我是否正在接近,或者我是否正朝着正确的方向前进。如有任何帮助,我们将不胜感激!

// Initial.cs file
public partial class Initial : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Accounts",
        c => new
        {
            Id = c.Int(nullable: false, identity: true),
            IsActive = c.Boolean(),
            Name = c.String(),
            Balance = c.Decimal(nullable: false, precision: 18, scale: 2),
            //ReconciledBalance = c.Decimal(),
            HouseholdId = c.Int(),
        })
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.Households", t => t.HouseholdId);
        Sql("ALTER TABLE dbo.Accounts NOT SURE WHAT TO PUT HERE");
    }

    public override void Down()
    {
        //AlterColumn("dbo.Accounts", "ReconciledBalance", c => c.Decimal(nullable: false, precision: 18, scale: 2));
        DropTable("dbo.Accounts");
    }
}

//Account Model
public class Account
{
    public Account()
    {
        this.Transactions = new HashSet<Transaction>();
    }

    public int Id { get; set; }
    public bool IsActive { get; set; }
    public string Name { get; set; }
    [Range(double.MinValue, double.MaxValue)]
    public decimal Balance { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    [Range(double.MinValue, double.MaxValue)]
    public decimal ReconciledBalance{ get; set; }
    }

    //FKs
    public int HouseholdId { get; set; }

    //Virtual Properties
    public virtual Household Household { get; set; }// One to one
    public virtual ICollection<Transaction> Transactions { get; set; }
}

// Account Transaction
public class Transaction
{
    public Transaction()
    {
    }

    public int Id { get; set; }
    public bool IsActive { get; set; }
    [DisplayFormat(DataFormatString = "{0:g}", ApplyFormatInEditMode = true)]
    public DateTimeOffset Date { get; set; }
    [StringLength(150, ErrorMessage ="Description cannot exceed 150 characters.")]
    public string Description { get; set; }
    [Range(double.MinValue, double.MaxValue)]
    public decimal Amount { get; set; }
    public bool IsReconciled { get; set; }
    public bool IsExpense { get; set; }
    [Range(double.MinValue, double.MaxValue)]
    public decimal ReconciledAmount { get; set; }

    //FKs
    public int CategoryId { get; set; }
    public string EnteredById { get; set; }
    public int AccountId { get; set; }

    //Virtual Properties
    public virtual Category Category { get; set; }
    public virtual ApplicationUser EnteredBy { get; set; }
    public virtual Account Account { get; set; }

}

另一种方法:

public class Account
{
    public Account()
    {
        this.Transactions = new HashSet<Transaction>();
    }

    public int Id { get; set; }
    public bool IsActive { get; set; }
    public string Name { get; set; }
    [Range(double.MinValue, double.MaxValue)]
    public decimal Balance { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    [Range(double.MinValue, double.MaxValue)]
    public decimal ReconciledBalance
    {
        get 
        { 
             return Transactions.Where(t => t.IsActive == true && t.IsReconciled == true).Sum(x => x.Amount) 
        }
        private set { /* needed for EF */ }
    }

    //FKs
    public int HouseholdId { get; set; }

    //Virtual Properties
    public virtual Household Household { get; set; }// One to one
    public virtual ICollection<Transaction> Transactions { get; set; }
}

希望您不需要覆盖 Up() 或 Down()。 (编辑:或者只是从中删除 Sql("ALTER TABLE dbo.Accounts NOT SURE WHAT TO PUT HERE");

您可以 link 使用 Entity Framework 期望的名称而不是 table:

的数据库视图实体
    public override void Up()
    {
        CreateTable(
            "dbo.Accounts_BackingTable",
        c => new
        {
            Id = c.Int(nullable: false, identity: true),
            IsActive = c.Boolean(),
            Name = c.String(),
            Balance = c.Decimal(nullable: false, precision: 18, scale: 2),
            HouseholdId = c.Int(),
        })
        .PrimaryKey(t => t.Id)

        Sql(@"EXECUTE('CREATE view dbo.Accounts 
with schemabinding as (SELECT
    a.id,
    a.isactive,
    a.name,
    a.balance,
    a.householdid,
    SUM(t.amount) AS ReconciledBalance
FROM dbo.accounts_backingtable a
    LEFT JOIN dbo.transactions t
    ON t.accountid = a.id AND t.isactive = 1 AND t.isreconciled = 1
    --It's not clear what logic you need for the join
    --ON t.accountid = a.id AND (t.IsReconciled = 1 or t.IsActive = 1 or t.IsVoid=0)
)
GROUP BY a.id,
    a.isactive,
    a.name,
    a.balance,
    a.householdid')");

    }

GROUP BY 子句可能意味着您的视图不是 updateable,在这种情况下,您要么需要两个实体,其中一个是可更新的。或者您可以切换到使用存储过程并修改存储过程中的 sql。

注意,这仍在进行动态计算,只是在数据库内部而不是在应用程序中。如果要存入数据库,需要在创建、更新、删除交易时进行计算。