zoukankan      html  css  js  c++  java
  • EntityFramework的天坑

    先看以下代码:

            public void Index()
            {
                //新增一条数据
                _testDbContext.Add(new User { UserName = "admin_test" });
                //从数据库查询,并修改UserName字段的值,但不显式提交修改
                var model = _testDbContext.Users.FirstOrDefault(m => m.Id == 1);
                model.UserName = "我的测试2";
                //保存
                _testDbContext.SaveChanges();
            }

    按照常理来说,从数据库查询到的数据,虽然对其字段做了修改,但没显式提交,应该不会保存。但实际是保存了。
    那么为何会这样?我们先把sql语句打印出来,看下都执行了什么语句。要想打印sql,需要做一些配置。
    在Startup类中定义一个字段:

            public static readonly ILoggerFactory MyLoggerFactory
            = LoggerFactory.Create(builder =>
            {
                #if DEBUG
                    builder.AddConsole();
                #endif
            });

    然后修改ConfigureServices方法,添加UseLoggerFactory:

                var connection = Configuration.GetConnectionString("MysqlConnection");
                services.AddDbContext<TestDbContext>(options => options
                            .UseMySql(connection, ServerVersion.AutoDetect(connection))
                            .UseLoggerFactory(MyLoggerFactory)//输出sql日志
                );

    主要是.UseLoggerFactory(MyLoggerFactory) 这一句,然后运行程序,查看打印结果:

    info: Microsoft.Hosting.Lifetime[0]
          Now listening on: http://localhost:5001
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    info: Microsoft.Hosting.Lifetime[0]
          Hosting environment: Development
    info: Microsoft.Hosting.Lifetime[0]
          Content root path: C:UsersBenDesktopEFTestEFTest
    info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
          Entity Framework Core 5.0.10 initialized 'TestDbContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: ServerVersion 5.7.22-mysql
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (45ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
          SELECT `u`.`Id`, `u`.`UserName`
          FROM `Users` AS `u`
          WHERE `u`.`Id` = 1
          LIMIT 1
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (40ms) [Parameters=[@p1='?' (DbType = Int32), @p0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
          UPDATE `Users` SET `UserName` = @p0
          WHERE `Id` = @p1;
          SELECT ROW_COUNT();
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (23ms) [Parameters=[@p0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
          INSERT INTO `Users` (`UserName`)
          VALUES (@p0);
          SELECT `Id`
          FROM `Users`
          WHERE ROW_COUNT() = 1 AND `Id` = LAST_INSERT_ID();

    上面的打印结果,显示:
    1.查询id==1的数据
    2.额外执行了一条update语句
    3.执行了一条insert语句
    由此可以得出结论:被dbcontext追踪的数据,在被修改后,即使不显式调用update方法,ef也会自动保存数据。


    代码不变,再运行一次,看结果:

    info: Microsoft.Hosting.Lifetime[0]
          Now listening on: http://localhost:5001
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    info: Microsoft.Hosting.Lifetime[0]
          Hosting environment: Development
    info: Microsoft.Hosting.Lifetime[0]
          Content root path: C:UsersBenDesktopEFTestEFTest
    info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
          Entity Framework Core 5.0.10 initialized 'TestDbContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: ServerVersion 5.7.22-mysql
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (46ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
          SELECT `u`.`Id`, `u`.`UserName`
          FROM `Users` AS `u`
          WHERE `u`.`Id` = 1
          LIMIT 1
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (24ms) [Parameters=[@p0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
          INSERT INTO `Users` (`UserName`)
          VALUES (@p0);
          SELECT `Id`
          FROM `Users`
          WHERE ROW_COUNT() = 1 AND `Id` = LAST_INSERT_ID();

    怎么没有修改语句了?原因是:model.UserName虽然赋值了,但是实际值和数据库中的值相同,没有发生变化,所以ef不会修改。
    个人建议:在不了解ef原理的情况下不要这么写:

            public void Index()
            {
                //新增一条数据
                _testDbContext.Add(new User { UserName = "admin_test" });
                //从数据库查询,并修改UserName字段的值,但不显式提交修改
                var model = _testDbContext.Users.FirstOrDefault(m => m.Id == 1);
                model.UserName = "我的测试2";
                //保存
                _testDbContext.SaveChanges();
            }

    如果想避免这种情况,其实也是可以的,只要在查询的时候,添加AsNoTracking就行了:

            public void Index()
            {
                //新增一条数据
                _testDbContext.Add(new User { UserName = "admin_test" });
                //从数据库查询,并修改UserName字段的值,但不显式提交修改
                var model = _testDbContext.Users.AsNoTracking().FirstOrDefault(m => m.Id == 1);
                model.UserName = "我的测试22";
                //保存
                _testDbContext.SaveChanges();
            }

    AsNoTracking的作用是:告诉EntityFramework不对查询结果进行追踪。

    测试代码:https://gitee.com/subendong/EFTest

    有篇文章写的很好,可以仔细阅读:https://www.cnblogs.com/sheng-jie/p/7416302.html

  • 相关阅读:
    ASP.NET结合COM组件发送Email
    木马生成技术
    AJAX中使用Session
    对象模型文档对象模型DOM简介
    何遍历数据源中的表名称
    AJAX.NET用户开发指南
    用ASP.NET上传大文件
    .net反编译利器
    Equals和GetHashcode
    Factory Method模式
  • 原文地址:https://www.cnblogs.com/subendong/p/15410720.html
Copyright © 2011-2022 走看看