EntityFramework Core并发导致显式插入主键问题

简介:

前言

之前讨论过EntityFramework Core中并发问题,按照官网所给并发冲突解决方案以为没有什么问题,但是在做单元测试时发现too young,too simple,下面我们一起来看看。

.NET Core 1.1单元测试问题

我们循序渐进,首先从单元测试开始说起,可能其中就有你在.NET Core上进行单元测试会遇到的问题,别着急,不妨一看。我们需要创建.NET Core类库,,如下:

接下来对project.json进行如下修改。

复制代码
{
    "version": "1.0.0-*",
    "testRunner": "xunit",
    "dependencies": {
        "xunit": "2.2.0-beta2-build3300",
        "dotnet-test-xunit": "2.2.0-preview2-build1029"
    },
    "frameworks": {
        "netcoreapp1.0": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.0.0"
                }
            }
        }
    }
}
复制代码

此时运行单元测试肯定是好使的,由于.NET Core最新为1.1版本此时我们将其版本修改为1.1如下:

复制代码
"frameworks": {
    "netcoreapp1.1": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.1.0"
        }
      }
    }
  }
复制代码

此时我们写一个测试方法,如下:

复制代码
    public class Test
    {
        [Fact]
        public void PassingTest()
        {
            Assert.Equal(4, Add(2, 2));
        }

        int Add(int x, int y)
        {
            return x + y;
        }
    }
复制代码

此时运行会出现如下dotnet.exe出现异常关闭。

结果让我们大跌眼镜,根本不知道什么地方出错了,此时你再运行单元测试,因为dotnet.exe已经关闭将导致单元测试无法启动,于是乎我们通过 dotnet test 命令来运行看能否得到一点错误提示,结果还是让我找到了原因。

无法加载 Microsoft.DotNet.InternalAbstractions 程序集,此时我们添加该程序集再看看,如下。

"Microsoft.DotNet.InternalAbstractions": "1.0.1-beta-003206"

完美,结果测试通过,至此关于单元测试我们有必要做下结论:官网所给对应的是针对于.net core 1.0版本,运行测试没问题,若是.net core 1.1版本需要添加 Microsoft.DotNet.InternalAbstractions 包。我是从github上才找到解决方案https://github.com/xunit/xunit/issues/1031,需要添加上述依赖包才可。

EntityFramework Core 1.1并发导致显式插入主键问题 

我们从头讲起,在仓储接口中定义插入Blog的接口,如下:

    public interface IBlogRepository : IEntityBaseRepository<Blog>
    {

        void Create(Blog b);
    }

然后则是实现该接口了,如下:

复制代码
    public class BlogRepository : EntityBaseRepository<Blog>,
        IBlogRepository
    {
        private EFCoreContext _efCoreContext;
        public BlogRepository(EFCoreContext efCoreContext) : base(efCoreContext)
        {
            _efCoreContext = efCoreContext;
        }

        public void Create(Blog b)
        {
            try
            {
                using (var transaction = _efCoreContext.Database.BeginTransaction())
                {

                    _efCoreContext.Blogs.Add(b);
                    _efCoreContext.SaveChanges();
                    transaction.Commit();
                }

            }
            catch (DbUpdateConcurrencyException ex)
            {...}
         }
   }
复制代码

接下来一切准备就绪,我们来开始进行单元测试,我们开启两个线程来测试看看。

复制代码
        [Fact]
        public void TestEFCore()
        {
            var blog = new Blog()
            {
                Name = "Jeffcky",
                Url = "http://www.cnblogs.com/CreateMyself",
                Posts = new List<Post>() { new Post() { Title = "a", Content = "ss" } }

            };

            var tasks = new Task[2];
            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = Task.Factory.StartNew(() =>
                {
                    var _contextOptions = new DbContextOptionsBuilder()
             .UseSqlServer("server=WANGPENG;Database=EFCoreDb;Trusted_Connection=True;")
             .Options;
                    using (var efcoreContext = new EFCoreContext(_contextOptions))
                    {
                        var blogRepository = new BlogRepository(efcoreContext);
                        blogRepository.Create(blog);
                    }

                });
            }
            Task.WaitAll(tasks);
        }
复制代码

此时演示结果如下,测试也通过。

当修改测试所开线程,开启如下5个线程时。

 var tasks = new Task[5];

此时将抛出异常,具体演示结果如下:

具体错误信息显示如下:

当 IDENTITY_INSERT 设置为 OFF 时,不能为表 'Blog' 中的标识列插入显式值。

当然这种情况不是一定会发生,有可能开启两个线程不会出现上述以上异常,有可能会抛出异常。为什么会出现上述异常呢,请看如下图。

当一个线程过来时,正常提交肯定是没问题,但是此时该插入的Blog已经被追踪,仅接着又来一个线程,此时Blog中的Id是上一个线程插入的值,所以会导致我们的Id本来主键是自动增长的,而此时Id却有了值出现上述异常。在项目中很难把握这样的情况,也尝试去修改实体的变更追踪的状态,结果依然出现上述问题,最终采用写SQL语句的方式来实现,如果有能够修改变更追踪解决的方案请在评论中给出。在我们项目中,利用SQL语句的方式来解决EF Core的并发,同时开启200个线程没有出任何问题,当然我们的逻辑也还算有一点复杂,所以不用担心EF Core的性能问题,我们更多的是关心业务逻辑。一直在思考怎么通过不写SQL语句的方式去解决这样的并发问题,我能够想到的是既然传过来的实体插入后会被变更追踪,那么我将传过来的参数再实例化一个对象,然后将参数传给它这样应该就能解决问题。

复制代码
        public void Create(Blog b)
        {

            var copyBlog = new Blog() { Name = b.Name, Url = b.Url };
            try
            {
                using (var transaction = _efCoreContext.Database.BeginTransaction())
                {
                    _efCoreContext.Blogs.Add(copyBlog);
                    _efCoreContext.SaveChanges();
                    var posts = b.Posts.Select(d => new Post()
                    {
                        BlogId = copyBlog.Id,
                        Content = d.Content,
                        Title = d.Title
                    });
                    _efCoreContext.Set<Post>().AddRange(posts);
                    _efCoreContext.SaveChanges();
                    transaction.Commit();
                }

            }
            catch (DbUpdateConcurrencyException ex)
            {...}
       }
复制代码

此时我们开启200个线程来跑跑看看,此时测试通过,如下

我们再来看看数据库是否已经插入200条数据。

这个对于并发导致显式插入主键的问题比较另类的做法,如果有更好的方案请在评论区提出来。

总结 

本节我们讨论了有关EF Core中并发导致的问题,尚未找到更加可靠的方案,期待你阅读后给出最佳方案。






本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/6384125.html,如需转载请自行联系原作者

目录
相关文章
|
12月前
|
人工智能 算法 前端开发
我和通义灵码的一周年
通义灵码是阿里巴巴推出的一款AI插件,大小约5.8M,适用于IDEA开发环境。它通过@workspace和@terminal两大功能,显著提升代码开发效率和质量。@workspace帮助开发者快速理解项目结构,智能导航至关键代码;@terminal则提供命令行下的代码搜索与实时补全,加速开发流程。实际应用中,通义灵码大幅缩短了新项目熟悉时间,减少了试错成本,提升了代码的规范性和稳定性。
275 0
|
11月前
|
弹性计算 负载均衡 网络协议
slb健康检查方法
slb健康检查方法
269 4
|
9月前
|
人工智能 供应链 搜索推荐
大模型进化论:AI产业落地将卷向何方?
大模型进化论:AI产业落地将卷向何方?
148 11
|
IDE Oracle Java
day4:JDK、IntelliJ IDEA的安装和环境变量配置
【7月更文挑战第4天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
506 0
|
11月前
|
存储 数据可视化 数据挖掘
使用Python进行数据分析和可视化
本文将引导你理解如何使用Python进行数据分析和可视化。我们将从基础的数据结构开始,逐步深入到数据处理和分析的方法,最后通过实际的代码示例来展示如何创建直观的数据可视化。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。让我们一起探索数据的世界,发现隐藏在数字背后的故事!
323 5
|
12月前
|
SQL 存储 数据管理
SQL数据库的使用指南:从入门到精通
随着信息技术的飞速发展,数据库已成为各类企业和组织不可或缺的一部分。作为最流行的数据库管理系统之一,SQL数据库广泛应用于各种场景,如数据存储、数据管理、数据分析等。本文将详细介绍SQL数据库的使用方法,帮助初学者快速入门,并帮助有经验的开发者深化理解。一、SQL数据库基础首先,我们需要理解SQL数
548 2
|
测试技术 数据库
基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(四)
基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(四)
|
Linux
centos bond配置文件示例
centos bond配置文件示例
253 2
|
SQL 架构师 安全
精简总结:一文说明软件测试基础概念(一)
该内容是关于软件测试的介绍,包括软件测试的基础概念、特点、与开发的区别,以及与调试的差异。文中提到了测试是为了验证软件是否满足用户需求,确保软件质量,并举例说明了日常生活中测试的存在。还讨论了软件测试的不可穷尽性,并列举了软件测试与开发在内容、技能和职业发展路径上的不同。此外,提到了《软件测试的艺术》这本书作为学习资源。内容中包含了两张图片,分别展示了相关概念。
171 0
|
机器学习/深度学习 算法 数据挖掘
【Python机器学习】K-Means算法对人脸图像进行聚类实战(附源码和数据集)
【Python机器学习】K-Means算法对人脸图像进行聚类实战(附源码和数据集)
685 1