【问题标题】:Entity Framework - default values doesn't set in sql server table实体框架 - sql server 表中未设置默认值
【发布时间】:2013-07-13 05:00:27
【问题描述】:

SQL Server 2005 数据库表有一个列“createdon”,其默认值设置为 getdate()。我正在尝试使用实体框架添加记录。 “createdon”列未更新。

我是否错过了实体框架中的任何属性,请提出建议。

【问题讨论】:

标签: c# sql-server-2005 entity-framework-4


【解决方案1】:

这是实体框架存在问题的少数问题之一。假设您有一个如下所示的类:

public class MyEntity
{
    // Id is a PK on the table with Auto-Increment
    public int Id { get; set; }

    // CreatedOn is a datetime, with a default value
    public DateTime CreatedOn { get; set; }
}

现在,您要插入一个新元素:

using(var context = new YourContext()) 
{
    context.MyEntities.Add(new MyEntity())
}

由于 EDMX 中的定义,Entity Framework 知道如何处理自增主键。它不会尝试为Id 属性插入值。然而,就实体框架而言,CreatedOn一个值:默认的DateTime。因为实体框架不能说“好吧,它有一个值,但我应该忽略它”,它会主动插入具有CreatedOn 属性值的记录,绕过表上列定义的默认值。

没有简单的方法可以做到这一点。您可以在插入该项目时主动将CreatedOn 属性设置为DateTime.Now。或者你可以创建一个接口和一个扩展方法对:

public interface ICreatedOn
{
    public DateTime CreatedOn { get; set; }
}

public partial class MyEntity : ICreatedOn
{

}

public static TEntity AsNew<TEntity>(this TEntity entity) where TEntity : ICreatedOn
{
    if(entity != null)
        entity.CreatedOn = DateTime.Now;

    return entity;
}

using(var context = new YourContext()) 
{
    context.MyEntities.Add(new MyEntity().AsNew())
}   

编辑:为了扩展这一点,这是一个无法解决的问题的原因是因为autoincrement 字段和具有default 值约束的字段背后的含义。根据定义,自动增量字段应始终由服务器处理,使用种子和所有爵士乐。 除非您使用了SET IDENTITY INSERT ON,否则您不能为插入的自动增量字段指定值。然而,默认值只是提示“如果我没有指定任何值,请使用它”。因为 .NET 中的值类型不能为 null,所以总会有一个值,而 Entity Framework 无法推断该字段的 default 值,当时,意味着您希望它在SQL 服务器。

【讨论】:

  • 谢谢 Simon,所以唯一的选择是在 C# 代码中显式设置“CreatedOn”,然后添加实体?
  • @Chris_web 据我所知,是的。这也是实体框架中Guid 主键的问题。或者完全绕过实体框架并使用您自己的 SQL 插入值。有关发生这种情况的更多信息,请参阅我的编辑。
  • @Chris_web “问题”不是 EF,而是 .Net。像 Datetime、int、Guid 等这样的结构总是有一个默认值(在 int 的情况下,它是 0,即使你没有分配它,而在 Datetime 的情况下,它是 default(DateTime),恰好是 DateTime。分钟)。您可以尝试向属性的 getter 添加一些逻辑,并检查它是否是 Datetime 的默认值并返回另一个,但如果它不触发 EF Changetracking(因为 EF 仅更新自加载)。
  • @BernhardKircher:Relevant suggestion,FWIW。
  • @SimonBelanger 只是为了补充您的答案,MS SQL 仅对not null 表字段使用默认值,即 MS SQL 字段必须不为空并接受空值以使用默认值一点也不。为了让 MS SQL 成为默认值的来源,EF 需要发送一个空值。对于 C# 使用任何 MS SQL 生成的默认值,C# 自身无法生成此值,因为它不是 SQL 生成的默认值。这很重要,例如,如果您有一个由数据库生成的 modified 日期时间字段,用于验证跨不同时区的更新操作。
【解决方案2】:

我通过告诉 EF 该列是“计算的”来解决这个问题,因此应该单独保留以进行插入。

如果您查看生成实体的配置

       namespace Data.Context
        {
            // Table
            internal partial class MyTableConfiguration : EntityTypeConfiguration<MyTable>
            {
                public MyTableConfiguration(string schema = "dbo")
                {    
                    ToTable(schema + ".MyTable");
                    HasKey(x => x.Id);

                    Property(x => x.ColumnName).HasColumnName("ColumnName").IsOptional().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
                    ....

【讨论】:

  • modified 日期时间字段上的伪或真实计算列规范的问题是 EF 无法发送空值来获取新的默认日期时间。换句话说,您不能将modified 字段设置为现在。一个真正的 MS SQL 计算列的问题是,像 getdate() 这样的非确定性函数(每次返回不同的值)不能被持久化。
【解决方案3】:

您也可以在实体数据模型的 GUI 中将 StoreGeneratedPattern 设置为 Computed(按照 Malcolm 的建议)。

  1. 在 Visual Studio 中打开您的 .edmx 文件
  2. 打开字段的属性(点击字段->点击 F4 或者右键->属性)
  3. 在属性窗口中将StoreGeneratedPattern 设置为Computed 如下图:

【讨论】:

    【解决方案4】:

    除了使用设计器和已经展示的一些更漂亮的东西之外,您还可以通过简单地在字段上设置DatabaseGenerated 属性来将列标记为正在计算:

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime CreatedOn { get; set; }
    

    【讨论】:

    • 这是最好的答案,因为您可以轻松地将其放入部分类中。即使您重新生成模型,它也会继续工作。
    猜你喜欢
    • 2019-10-10
    • 1970-01-01
    • 2012-05-12
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多