背景:我正在开始一个项目,将使用Linq2Sql的web应用程序转换为使用实体框架(v6)。我在L2S方面有很多经验,但我对EF来说是全新的。由于我们的应用程序及其数据库已经存在,所以我们使用"数据库优先"的方法。此外,数据库也在不断发展,因此我们正在对模式进行更改,并且从修订后的数据库中更新模型,每次都会重新生成EF模型的代码。
对于我们的许多实体(数据库表),每当构建实体时,我们都会在代码中设置默认值。在Linq2Sql中,很容易:为实体定义一个分部类,并向该类添加一个方法,如下所示:
partial void OnCreated() { SomeProperty = SomeDefaultValue; }
每当Linq2Sql构造一个新的实体对象时,它都会调用您定义的OnCreated()方法,并根据需要设置默认值。它非常有效。
问题:在EF中,我看不出在数据库优先的场景中有什么方法可以做到这一点。
-
如果我修改EF生成的模型代码,每当我们在数据库修订后更新模型时,模型代码都会被覆盖。
-
如果我在一个单独的文件中为实体定义一个分部类并定义一个构造函数,编译器会抱怨已经定义了构造函数。
-
似乎也不支持L2S的OnCreated()方法。
有什么建议吗?
编辑:感谢大家的有益评论,但我认为我需要指出一个重要的考虑因素:我的目标是使用数据库优先的方法并坚持下去,而不是先切换到代码。当数据库模式随时间变化时,我希望EF设计器(或POCO生成器或任何工具)更新我的EF实体类以匹配。所有这些都不会丢失我在应用程序中构造类时为初始化类属性所添加的内容。这在Linq2Sql中很容易,但我只是看不到首先在EF数据库中实现这一点的方法。欢迎所有建议!
1。打开.edmx文件
2。选择具有默认值的字段,转到属性
3。然后选择StoreGeneratedPattern
4。然后将该值更改为计算
我认为它奏效了。
OP在这里-我对ErikEJ的回答给予了赞扬,但我想回顾一下我在这个主题上学到的东西,与其他人分享。有三个目标:
-
使用数据库优先的方法并坚持使用它,即使数据库模式会随着时间的推移而改变。也就是说,让EF根据预先存在的数据库为每个数据库表实体生成代码,并在数据库更改时更新代码。
-
提供一种机制,在每次构造对象时初始化实体对象属性,例如
Employee.Dependents = 1
。(我知道数据库模式可以设置简单的默认值,但更复杂的初始化必须由代码执行。) -
当数据库模式发生更改并且EF重新生成模型代码时,必须保留自定义初始化代码。
EF没有提供每次在数据库优先场景中构造实体对象时设置属性的方法。编辑EF生成的代码不起作用,因为每当EF在更改数据库模式后重新生成代码时,它就会被覆盖。到目前为止,我想到了四个解决方案:
-
一个想法是为每个实体类添加一个具有多个默认零参数构造函数的构造函数。例如,
c = new Customer(x)
而不是默认的c = new Customer()
。应用程序代码将调用新的构造函数,该构造函数将继承默认构造函数并添加额外的代码来初始化类属性。这避免了复制默认构造函数,这是C#不允许的。新的构造函数在一个单独的分部类文件中,因此当EF从数据库生成更新的模型时,它不会被覆盖。然而,应用程序程序员可能会意外调用默认构造函数,从而导致微妙的错误,这是有风险的。
-
另一种解决方案是将实体类封装在另一个类中,例如,封装在
Customer
类周围的Customer2
类。新类将继承原始类,并根据需要为任何属性添加初始化代码。由于这些新类与原始实体类代码是分开的,因此当EF重新生成模型代码时,它们不会被覆盖。可以从顶级应用程序代码中隐藏实体类,以避免意外引用原始类。如果是这样的话,这应该是一个很好的技术,尽管我还没有测试过。
-
第三方工具EntityFramework反向POCO生成器是一个帮助。它和EF一样生成POCO模型代码,但它是而不是EF。它有一个生成分部类的选项,实体类包括一个
InitializePartial()
方法,很像Linq2Sql的OnCreated()
。我认为随着时间的推移,随着数据库的更改,这将适用于重新生成代码。我担心的是,这是第三方产品,它总是有过时或不受支持的风险。 -
最后,您可以更改EF用于生成代码的模板。其基本思想是让生成的代码将"
partial void OnCreated()
"添加到每个类中,这使我们可以使用内置在Linq2Sql中的同样方便的技术。我认为EF的新版本可能会覆盖模板的更改,但这只是模板中的一个更改,而不是对每个实体类的更改。这里描述了这种方法(如何在实体框架6中有效地设置默认实体值,数据库优先),YouTube视频在这里(https://www.youtube.com/watch?v=i8J2ipImMuU)。
感谢所有贡献者!我希望这个页面对其他人有帮助,这样你就不必像我那样浪费太多时间来寻找解决方案。
使用EF Reverse poco模板-它将从数据库派生默认值。您可以重写分部类中的InitializePartial方法,以在代码中设置默认值。
来自EF后台,我通常在代码第一次迁移时手动完成。在生成的迁移的向上功能中,您可以执行类似于以下的操作
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()"));
然而,在我看来,这种方法有一个缺点,那就是你必须跟踪你的(无用的)迁移。
刚刚发现这篇文章,正在寻找同一问题的答案。这是一个对我有效的变通办法。
为要指定默认值的实体(DB表)创建一个分部类,例如:
namespace myApplication.MyModel
{
public partial class myEntityName
{
public myEntityName(bool InitialiseOnConstruct) : this()
{
if (InitialiseOnConstruct)
{
this.property1 = defaultValue1;
this.property2 = defaultValue1;
}
}
}
}
然后在代码中,构造实体:
thisEntity = new EntityName(true);
好吧,这是一个额外的步骤,但它是有效的。希望能有所帮助。