如何在Entity Framework 6(Database First)中设置默认值

4
背景: 我要开始一个项目,将使用Linq2Sql的Web应用程序转换为使用Entity Framework(v6)。我对L2S有很多经验,但对EF完全不熟悉。由于我们的应用程序和数据库已经存在,因此我们采用“Database First”方法。此外,数据库正在不断演变,因此我们在模式中进行更改,并从更新后的数据库更新模型,每次都会重新生成EF模型的代码。

对于我们的许多实体(数据库表),我们在代码中设置默认值,每当构造实体时就会执行。在Linq2Sql中很容易实现:为实体定义一个部分类,并添加如下所示的类中的方法:

partial void OnCreated() { SomeProperty = SomeDefaultValue; }

每当Linq2Sql构造新的实体对象时,它会调用你定义的OnCreated()方法,并按预期设置默认值。这非常好用。

问题: 在EF中,我没有看到在Database First场景下实现这一点的方法。
  • 如果修改由EF生成的模型代码,则每当我们在数据库修订后更新模型时,模型代码都会被覆盖。

  • 如果在单独的文件中为实体定义一个部分类并定义构造函数,则编译器会抱怨构造函数已经定义了。

  • 也没有支持类似于L2S的OnCreated()方法的功能。

有什么建议吗?

编辑:感谢大家的有用评论,但我认为我需要指出一个重要的考虑因素:我的目标是使用“Database First”方法并坚持下去,而不是切换到“Code First”。随着数据库模式随时间改变,我希望EF Designer(或POCO Generator或任何其他工具)更新我的EF实体类以匹配。所有这些都不会丢失在应用程序中构造类属性时添加的内容。在Linq2Sql中很容易实现,但我只是看不到在EF数据库优先的情况下如何实现这一点。欢迎所有建议!


您可以修改T4模板,在构造函数中添加partial void OnCreated - Ivan Stoev
当您修改数据库并且EF重新生成EF模型时,T4(tt)模板不会被覆盖吗? - Sandy Gettings
事实上,T4(tt)模板正在生成您的实体模型。 - Ivan Stoev
我通过在tt文件中添加OnCreated()方法进行了一次测试,结果符合预期。不幸的是,当我更改表模式并从数据库更新模型(使用EF Designer)时,tt文件被覆盖并且OnCreated()方法丢失了。 - Sandy Gettings
5个回答

4

1. 打开.edmx文件

2. 选择具有默认值的字段并转到属性

3. 然后选择StoreGeneratedPattern

4. 然后将值更改为Computed

我认为它已经起作用了。


也许可以写一下它的工作原理。我认为它是这样的:“此解决方案在保存后从数据库获取默认值。” - Kobbe

3

我是OP - 我已经给ErikEJ的答案打了分,但我想总结一下我在这个主题上学到的东西以与他人分享。有三个目标:

  1. 采用基于数据库的方法,并坚持使用它,即使数据库模式随着时间的推移而改变。也就是说,让EF为每个数据库表实体生成代码,基于预先存在的数据库,并在数据库更改时更新代码。

  2. 提供一种机制来初始化每次构造对象时的实体对象属性,例如Employee.Dependents = 1。(我知道可以通过数据库模式设置简单的默认值,但更复杂的初始化必须由代码执行。)

  3. 当数据库模式发生变化并且EF重新生成模型代码时,必须保留自定义初始化代码。

在基于数据库的情况下,EF没有提供一种设置属性的方法,使得每次构造实体对象时都会被调用。编辑EF生成的代码行不通,因为每当EF在数据库模式更改后重新生成代码时,它都会被覆盖。到目前为止,我想到了四种解决方法:

  1. 一种方法是为每个实体类添加一个不同于默认零参数构造函数的构造函数。例如,c = new Customer(x)而不是默认的c = new Customer()。应用程序代码将调用新构造函数,该构造函数将继承默认构造函数并添加其他代码来初始化类属性。这避免了重复默认构造函数,这是C#不允许的。新构造函数位于单独的部分类文件中,因此在EF从数据库生成更新模型时不会被覆盖。
  2. 另一种解决方案是将实体类包装在另一个类中,比如称为Customer2的类包装在Customer类周围。新类将继承原始类并根据需要添加任何属性的初始化代码。
  3. 由第三方工具EntityFramework Reverse POCO Generator生成POCO模型代码,它产生类似EF的POCO模型代码,但它不是EF。它有一个选项来生成分部类,并且实体类包括类似于Linq2Sql的OnCreated()InitializePartial()方法。我认为这将对随着时间改变数据库而再生代码很好地发挥作用。我的担忧是这是一个第三方产品,总有风险它会变得过时或不受支持。
  4. 最后,您可以更改EF用于生成代码的模板。基本思想是让生成的代码添加“partial void OnCreated()”到每个类中,这使我们可以使用Linq2Sql内置的方便技术。我认为更新版本的EF可能会覆盖模板更改,但这只是模板中的一个更改,而不是对每个实体类的更改。此方法在这里描述(How to efficiently set default entity values in Entity Framework 6, Database First),YouTube视频在这里(https://www.youtube.com/watch?v=i8J2ipImMuU)。

感谢所有做出贡献的人!我希望这个页面对他人有所帮助,这样你就不必像我一样浪费大量时间寻找解决方案。


1
使用EF反向Poco模板-它将从数据库中派生默认值。您可以在部分类中覆盖InitializePartial方法以在代码中设置默认值。

考虑到我对EF还不熟悉,这个方法并没有完全奏效。从EF设计器中,我右键选择“添加代码生成项”,并运行DbContext生成器。这将为每个表实体(POCO文件)生成类。我编辑了我的测试实体文件以添加OnCreated方法。到目前为止都很好。然后我使用设计器来“从数据库更新模型”,结果编辑过的文件被覆盖了。我验证了CodeGenerationStrategy是否关闭。真是气死人了。 - Sandy Gettings
错误的模板!前往“工具和扩展”安装,并阅读Github网站上的文档。 - ErikEJ
好的,这个工具几乎完美地运行了。您只需运行一次以生成POCO类,然后修改它们以添加OnCreated()方法或类似内容。不幸的是,在此之后,看起来您必须采用Code-First方法。也就是说,如果数据库模式有任何更改,我必须自己修改代码,而不能仅仅告诉EF Designer(或POCO Generator)更新实体POCO类以匹配。重新运行Generator会覆盖我的更改。因此,除非我想切换到Code-First而不是Database-First(我真的不想),否则我仍然面临着同样的问题。 - Sandy Gettings
你可以使用部分类,它们不会被覆盖! - ErikEJ
1
好的,谢谢你的提示!看起来这个方法会行,只要我打开了部分类的选项。话虽如此,我希望EF本身能够处理这个问题,而不是使用另一个第三方工具。这是一个不错的备选方案。 - Sandy Gettings
显示剩余3条评论

0

刚刚发现这篇文章,正在寻找同样问题的答案。这是一个对我有效的解决方法。

为你想要指定默认值的实体(数据库表)创建一个部分类,例如:

namespace myApplication.MyModel
{
  public partial class myEntityName
  {
    public myEntityName(bool InitialiseOnConstruct) : this()
    {
      if (InitialiseOnConstruct) 
      {
        this.property1 = defaultValue1;
        this.property2 = defaultValue1;     
      }
    }
  }
}

然后在代码中,构建实体:

thisEntity = new EntityName(true);

好的,这是一个额外的步骤,但它有效。希望能有所帮助。


0

从EF背景出发,我通常手动进行代码优先迁移。在生成的迁移的up函数中,您可以像这样做:

AddColumn("dbo.Person", "IsActive", c => c.Boolean(nullable: false, defaultValue: true));
AddColumn("dbo.Person", "Name", c => c.String(nullable: false, defaultValue: "Mirza"));

或者添加默认的 SQL 值,使用

AddColumn("dbo.Person", "CreatedDate",
           c => c.String(nullable: false, defaultValueSql: "GETDATE()"));

然而,在我看来,这种方法有一个缺点,就是你必须跟踪你的(无用的)迁移。


1
谢谢,但对于数据库优先的情况有什么建议吗? - Sandy Gettings

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