如何在Entity Framework Code First中访问计算列?

10

我在我的ASP.NET MVC应用程序中使用Entity Framework Code First。我的某个类有几个列需要相加。我通过在数据库初始化器中运行修改表脚本来将这些列存储为计算列。假设这个类长这样:

public class Bond
{
        public decimal ParAmountOfIssuance { get; set; }
        public decimal AccruedInterest { get; set; }
        public decimal Premium { get; set; }
        public decimal OriginalIssueDiscount { get; set; }
}

修改脚本大致如下:

alter table Bonds
add TotalSources as (ParAmountOfIssuance + AccruedInterest + Premium - OriginalIssueDiscount)

我希望在Web应用程序中查看Total Sources列。有什么最好的方法可以实现这一点吗?[DatabaseGenerated(DatabaseGeneratedOption.Computed)]属性不起作用,因为EF Code First会在运行修改脚本之前从类创建表格。

欢迎提出任何建议。


4
你可以使用[NotMapped]在代码中计算这个值吗?这种方式并不是很符合"Code First"的思想。为什么需要在脚本中修改架构? - Goran Obradovic
1
我很抱歉告诉你,你已经找到的就是最终结果:除了时间戳或rowversion属性之外,让EF创建带有计算列的数据库是不可能的。但是你必须在属性上放置“Computed”属性,以便让EF正确处理计算列(只读,永不写入)。其结果是它仅适用于映射到现有数据库,而不适用于由EF Code-First创建的数据库。请参见此处:http://msdn.microsoft.com/en-us/library/gg193958.aspx中的“DatabaseGenerated”部分第二段。 - Slauma
@obrad,我不想在代码中执行计算,因为我无法确定我的应用程序是唯一更改列值的应用程序。更改脚本会创建计算列。也就是说,除非您询问我是否应该在编辑/查看数据时在代码中执行计算,这正是我目前正在执行的操作,但这似乎不是最好的做法。 - Robert S.
1
@robert-s 如果您需要在数据库中添加一些逻辑,那么如果您接受权衡,这是可以的。如果您使用CodeFirst并生成数据库,则意味着您的数据库取决于您的应用程序。但是,如果您不确定是否需要在多个应用程序中使用数据库,则权衡是CodeFirst。如果您正在使用CodeFirst,则应完全不了解数据库,它是抽象的,如果您需要共享数据,则odata或其他服务是选项。由于在当前版本的CF中无法在没有脚本的情况下完成此操作,这意味着CF不支持此操作:S - Goran Obradovic
如果您使用[DatabaseGenerated(DatabaseGeneratedOption.Computed)]或[DatabaseGenerated(DatabaseGeneratedOption.None)]属性,然后仅从脚本更改此列的公式,会发生什么?这样CF就不会写入此字段,而您将能够读取它。这种方式与使用现有数据库没有区别,并且得到支持。 - Goran Obradovic
没关系。我现在明白了,EF会在那种情况下抛出异常。 - Goran Obradovic
3个回答

12

我有一个折中的解决方法。

你只能在现有数据库上使用计算字段。

如果将你的属性添加到CF对象中,如下所示:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public decimal TotalSources { get; set; }

如果您在脚本中添加了一行代码,该代码将删除有关该数据库生成的信息:

DELETE FROM [dbo].[EdmMetadata]

我刚试过,CF会认为这是一个现有的数据库并且可以工作。

更新 我忘了,如果你像这样给你的Bond实体添加属性,那么在你的脚本中需要将其修改为计算属性,而不是添加它 :) 你甚至可以手动“同步”数据库和模型——在没有此字段的情况下使一切正常工作的时候,将其作为计算属性添加到模型中,在表中作为计算属性添加。当您从edm元数据表中删除哈希后,CF将在不尝试重新生成与数据库相关的模型的情况下正常工作。


6
这是绝对不支持的——在自定义属性上定义Computed选项将引发异常。代码优先=代码中的逻辑。如果需要自定义计算属性,请使用数据库优先。代码优先支持的唯一数据库逻辑是标识列和时间戳。
问题在于需要将列标记为计算列,但创建数据库将不允许这样做。如果未将列标记为计算列,则可更新=EF将生成更新语句,尝试更新此列,但在数据库中会失败。

0

我正在CF(WinForms)中进行计算列,就像这样(我不知道这是否是最好的方法):

这是一个实体:

public class Result
{
    public int ResultId { get; set; }
    public int StudentId { get; set; }
    public Student Student { get; set; }
    public float Arabic { get; set; }
    public float English { get; set; }
    public float Math { get; set; }
    public float Science { get; set; }
    public float Total { get; set; }
}

这是Form2:

    private void Form2_Load(object sender, EventArgs e)
    {
        Model1 model = new Model1();//Model1 : DbContext
        model.Database.CreateIfNotExists();
        model.Database.ExecuteSqlCommand("alter table Results drop column Total; alter table Results add Total AS (Arabic + English + Math + Science)");

        var r1 = (from s in model.Students
                  join r in model.Results
                  on s.StudentId equals r.StudentId
                  select new { s.StudentName, r.Arabic, r.English, r.Science, r.Math, r.Total }).ToList();
        dataGridView1.DataSource = r1;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接