zoukankan      html  css  js  c++  java
  • 关于有默认值的字段在用EF做插入操作时的思考

    今天在用EF做插入操作的时候发现数据库中一个datetime类型的字段(CreateDate)的值居然全部为null。于是赶紧看表结构发现CreateDate字段居然是允许为空的。

    虽然为空,但是设置了默认值getdate(),按说不应该为null的。于是开始测试。

    字段允许Null值的情况

    Users表结构如下:

    假如一个字段有了默认值,并且又允许为Null,在做插入操作时会发生什么?

    如上图中的表结构,CreateDate是允许为null的,而又有默认值getdate()。这样在用传统SQL语句和EF做插入操作时分别会发生什么呢?

    先看传统SQL语句:

    insert into Users(Username,Password) values('test','123456')
    

    插入结果:

    使用SQL完全没有问题。

    再看使用EF:

    Users user = new Users();
    user.Username = "test2";
    user.Password = "123456";
    
    TestEntities entity = new TestEntities();
    entity.Users.Add(user);
    int result = entity.SaveChanges();
    

    插入结果:

    使用EF插入居然为NULL!!!于是立刻打开SQL Server Profiler监测生成的SQL,EF居然生成了下面的SQL:

    没赋值的字段,EF生成SQL的时候居然都置为NULL,管你有没有设默认值!

    可能是EF比较娇情,我字段允许为NULL,EF生成SQL时就置为null了。那我把字段设为不允许为NULL,EF又会生成怎样的SQL呢?

    字段不允许Null值的情况

    Users表结构如下:

    这一次CreateDate不允许为NULL了,使用SQL理所当然地一切正常:

    insert into Users(Username,Password) values('admin','123456')
    

    那再看下用EF:

    Users user = new Users();
    user.Username = "admin";
    user.Password = "123456";
    
    TestEntities entity = new TestEntities();
    entity.Users.Add(user);
    int result = entity.SaveChanges();

    结果。。。

    ”从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值。“

    EF很傲娇地抛了个异常!!为什么会抛出这个异常?这个异常神马意思?于是照妖镜SQL Server Profiler又出场了:

    日期类型的字段如果不赋值会给默认值“0001-01-01 00:00:00”,这和生成SQL无关,是在生成SQL之前CLR进行的初始化操作,同理String类型的字段如果不赋值会给默认值String.Empty!

    但是,那也不至于抛异常啊,顶多字段值存储为“0001-01-01 00:00:00”呗。

    于是查MSDN发现datetime类型的日期范围仅支持1750-01-01 00:00:00至9999-12-31 23:59:59.997,而0001-01-01 00:00:00不在此范围内,所以抛出异常。原来如此。

    datetime和datetime2支持的日期范围如下:

    结论

    如此看来无论字段有没有默认值,使用EF插入时都没有任何实际作用了。因此如果你使用EF建议干脆直接写死算了。比如user.CreateDate = DateTime.Now。

    ps.这里只是举个例子,事实上,在实际项目中不建议写成DateTime.Now,因为数据库系统时间和服务器时间一般是不同的,笔者实际项目中其实是封装了一个方法叫GetSQLServerSystemDateTime。

    除此之外是否还有别的方法呢?

    方法是有的,打开EF的.edmx文件,找到<EntityType Name="表名">节点,把如下节点:

    <Property Name="CreateDate" Type="datetime" Nullable="false"  />
    

    改成:

    <Property Name="CreateDate" Type="datetime" Nullable="false" StoreGeneratedPattern="Computed" />
    

    即可。

    StoreGeneratedPattern是一个枚举:
    • None 一个值,指示它不是服务器生成的属性。这是默认值。如果没有StoreGeneratedPattern属性,其值就默认为None。
    • Identity 执行插入时生成一个值,但在执行更新时保持不变。
    • Computed 执行插入和更新时都将生成一个值。

    笔者经过实际测试发现:

    1. 如果将StoreGeneratedPattern值设置为Identity,只要一修改CreateDate字段就会抛异常;
    2. 如果把StoreGeneratedPattern值设置为Computed不会抛异常,但值仍然没有被修改,即使你写了user.CreateDate = "xxx"。

    所以如果你使用EF,我还是建议采用第一种方式,直接在程序中赋值user.CreateDate = xx.GetSQLServerSystemDateTime()。这种方法省事且安全。修改edmx文件太麻烦,况且每增加一个datetime类型的字段都要修改一次edmx文件。

    最后感谢大家提出的建议,欢迎大家亲自测试并把结果反馈给我。

    延伸阅读关于有默认值的字段在用EF做插入操作时的思考(续)

  • 相关阅读:
    CnForums1.0 Alpha 开始试运行
    asp.net Forums2.0修改密码后无法登陆问题——都是Cache惹的祸
    CnForums1.0 Alpha RC1 发布
    Docker: Nvidia Driver, Nvidia Docker 推荐安装步骤
    Docker: docker pull, wget, curl, git clone 等如何更快?
    DL4J实战之三:经典卷积实例(LeNet5)
    纯净Ubuntu16安装CUDA(9.1)和cuDNN
    DL4J实战之四:经典卷积实例(GPU版本)
    JAVA 中静态块、静态变量加载顺序详解
    设计模式之单例模式
  • 原文地址:https://www.cnblogs.com/choon/p/3951312.html
Copyright © 2011-2022 走看看