Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Entity Framework——并发策略

Entity Framework——并发策略

作者头像
甜橙很酸
发布于 2018-04-17 06:25:38
发布于 2018-04-17 06:25:38
1.2K00
代码可运行
举报
文章被收录于专栏:DOTNETDOTNET
运行总次数:0
代码可运行

使用EF框架遇到并发时,一般采取乐观并发控制。

1支持并发检验

为支持并发检验,需要对实体进行额外的设置。默认情况下是不支持并发检验的。有以下两种方式:

方式名称

说明

时间戳注解/行版本

使用TimestampAttribute特性,实体的属性必须是byte数组类型

非时间戳注解

使用ConcurrencyCheckAttribute

Fluent API

使用StringPropertyConfiguration.IsConcurrencyToken方法

注释

1)时间戳注解

  • 一个类只能有一个属性可以配置为TimeStamp特性。
  • 任何时候行内数据被修改时,数据库都会自动为此属性创建新值。
  • 只要对相应的表执行更新操作,EF框架就会执行并发检测。

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Timestamp]
public byte[] RowVersion { get; set; }

2)非时间戳注解

  • 此方式,是对表的一个或多个字段进行并发检测
  • 当更改一行时,EF框架就会执行并发检测。

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ConcurrencyCheck]
public string Email { get; set; }

3)Fluent API

  • 此方式,是对表的一个或多个字段进行并发检测
  • 当更改一行时,EF框架就会执行并发检测。

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void Set(DbModelBuilder modelBuilder)
{
            //其他配置
            modelBuilder.Entity<User>().Property(u => u.Email)
                .IsRequired()
                .IsUnicode(false)
                .HasMaxLength(100)
                .IsConcurrencyToken();
}

2乐观并发控制

2.1使用数据库中的数据(服务端胜)

使用DbEntityEntry.Reload方法加载数据库中的数据而不是使用当前实体的值。

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            using (CustomDbContext context = new CustomDbContext())
            {
                var user = context.Users.Find(1);
                user.Email = "eftxt8326@163.com";
                bool saveFailed;
                do
                {
                    saveFailed = false;

                    try
                    {
                        context.SaveChanges();
                    }
                    catch (DbUpdateConcurrencyException ex)
                    {
                        saveFailed = true;
                        ex.Entries.Single().Reload();
                    }

                } while (saveFailed);
            }

分析:

当发生并发冲突时,context.SaveChanges();这行代码抛出异常DbUpdateConcurrencyException ,执行catch块的代码,ex.Entries.Single().Reload()这行代码作用是从数据库取出对应的一条记录然后用这条记录对当前实体赋值,又由于saveFailed = true,do语句块又执行一次,调用context.SaveChanges();将数据保存到数据库中,若这次执行do语句块,不抛出异常,由于 saveFailed = false,所以循环结束。

2.2使用当前实体数据(客户端胜)

使用当前实体数据覆盖数据库中的数据。

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            using (CustomDbContext context = new CustomDbContext())
            {
                var user = context.Users.Find(1);
                user.Email = "eftxt8326@163.com";

                bool saveFailed;
                do
                {
                    saveFailed = false;
                    try
                    {
                        context.SaveChanges();
                    }
                    catch (DbUpdateConcurrencyException ex)
                    {
                        saveFailed = true;

                        var entry = ex.Entries.Single();
                        entry.OriginalValues.SetValues(entry.GetDatabaseValues());
                    }

                } while (saveFailed);
            }

分析:

当发生并发冲突时,抛出DbUpdateConcurrencyException 异常,执行catch 块,ex.Entries.Single()这条语句的作用是从当前实体集中取出唯一的一个实体,然后调用DbEntityEntry.GetDatabaseValues,在数据库中查找这条记录,若能够找到这条记录,返回当前值的属性值集合。

entry.OriginalValues.SetValues这条语句的作用是:DbEntityEntry.OriginalValues指的是最后一次访问数据库时获得那条记录,调用DbPropertyValues.SetValues方法用一个词典给另一个词典赋值,entry.OriginalValues.SetValues(entry.GetDatabaseValues());是将当前数据库中的值赋给从数据库最后一次查出的值。由于saveFailed = true所以再次执行do语句块,将当前实体值写入数据库。

2.3结合当前实体值和数据库中的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            using (CustomDbContext context = new CustomDbContext())
            {
                var user = context.Users.Find(1);
                user.Email = "eftxt8326@163.com";

                bool saveFailed;
                do
                {
                    saveFailed = false;
                    try
                    {
                        context.SaveChanges();
                    }
                    catch (DbUpdateConcurrencyException ex)
                    {
                        saveFailed = true;

                        var entry = ex.Entries.Single();
                        //获得当前实体值
                        var currentValues = entry.CurrentValues;
                        //获得数据库中的值
                        var databaseValues = entry.GetDatabaseValues();

                        //拷贝一份
                        var resolvedValues = databaseValues.Clone();

                        //对数据加工处理 
                        HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);

                        entry.OriginalValues.SetValues(databaseValues);
                        entry.CurrentValues.SetValues(resolvedValues);
                    }
                } while (saveFailed);
            }
        public void HaveUserResolveConcurrency(DbPropertyValues currentValues,                                       DbPropertyValues databaseValues,
                                       DbPropertyValues resolvedValues)
        {
            //对数据加工处理
        }

也可以使用DbPropertyValues的public object ToObject()方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            using (CustomDbContext context = new CustomDbContext())
            {
                var user = context.Users.Find(1);
                user.Email = "eftxt8326@163.com";

                bool saveFailed;
                do
                {
                    saveFailed = false;
                    try
                    {
                        context.SaveChanges();
                    }
                    catch (DbUpdateConcurrencyException ex)
                    {
                        saveFailed = true;

                        //获得当前实体值
                        var entry = ex.Entries.Single();
//获得数据库中的值
                        var databaseValues = entry.GetDatabaseValues();
                        var databaseValuesAsBlog = (User)databaseValues.ToObject();

                        //拷贝一份
                        var resolvedValuesAsBlog = (User)databaseValues.ToObject();

                        //对数据加工处理 
                        HaveUserResolveConcurrency((User)entry.Entity,
                                                   databaseValuesAsBlog,
                                                   resolvedValuesAsBlog);

                        entry.OriginalValues.SetValues(databaseValues);
                        entry.CurrentValues.SetValues(resolvedValuesAsBlog);
                    }

                } while (saveFailed);
            } 

        public void HaveUserResolveConcurrency(User entity,
                                       User databaseValues,
                                       User resolvedValues)
        {
            //对数据加工处理

        }

3观察并发现象

本次实验选择观察“客户端胜”这种策略,选取这种策略的原因在不但可以通过试验观察到并发检测的情况,还可以观察到调用DbEntityEntry.GetDatabaseValues()、DbEntityEntry.OriginalValues、DbEntityEntry.CurrentValues的返回值,有助于深入理解这些概念

实体:使用ConcurrencyCheck特性标记实体属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public class User
 {
        public int Id { get; set; }
        /// <summary>
        /// 账号
        /// </summary>
        public string Account { get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        [ConcurrencyCheck]        
        public string Email { get; set; }
        /// <summary>
        /// 昵称
        /// </summary>
        public string Nickname { get; set; }
        /// <summary>
        /// 头像
        /// </summary>
        public string AvatarId { get; set; }
        /// </summary>
        /// 收藏
        /// </summary>
        public virtual ICollection<CollectionUser> CollectionUsers { get; set; }
        /// <summary>
        /// 记录插入时间
        /// </summary>
        public DateTime InsertTime { get; set; }
        /// <summary>
        /// 记录修改时间
        /// </summary>
        public DateTime UpdateTime { get; set; }
 }

更新表users的Email字段:

为了可以观察到并发现象,采用多线程,测试发现,双核四线程处理器,两个并行任务,很难捕捉到并发现象;当并行任务数为三个以上时,可以很轻易地发现并发现象。同时我们会打印执行的SQL,来说明并发检测所依赖的基本原理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        public void ConALL()
        {
            var p = new ParallelOptions();
            p.MaxDegreeOfParallelism = 4;
            Parallel.Invoke(p,() =>
            {

                ConM("1@163.com");
            },
            () =>
            {
                ConM("2@163.com");
            },
            () =>
            {
                ConM("3@163.com");
            });
        }
        public void ConM(string s)
        {
            using (CustomDbContext context = new CustomDbContext())
            {
                var user = context.Users.Find(1);
                
                user.Email = s;
                
                bool saveFailed;
                do
                {
                    saveFailed = false;
                    try
                    {
                        context.SaveChanges();
                        Trace.WriteLine(string.Format("【正常线程{1}】数据库中原值:{0}", user.Email, s));
                        Trace.WriteLine(string.Format("【正常线程{1}】客户端传值:{0}", s, s));
                    }
                    catch (DbUpdateConcurrencyException ex)
                    {
                        saveFailed = true;

                        var entry = ex.Entries.Single();
                        var databaseValues = entry.GetDatabaseValues();
                        string em = databaseValues["Email"].ToString();
                        string or = entry.OriginalValues["Email"].ToString();
                        Trace.WriteLine(string.Format("【线程{1}】数据库中原值:{0}", user.Email, s));
                        Trace.WriteLine(string.Format("【线程{1}】客户端传值:{0}", s, s));
                        Trace.WriteLine(string.Format("【线程{1}】DbEntityEntry.GetDatabaseValues:{0}", em, s));
                        Trace.WriteLine(string.Format("【线程{1}】DbEntityEntry.OriginalValues:{0}", or, s));
                        entry.OriginalValues.SetValues(databaseValues);
                    }

                } while (saveFailed);
            }
       }

查看当前Mysql中的users表Email字段值为:1@163.com

执行程序,并记录结果:

执行的SQL

SELECT

`Extent1`.`Id`

`Extent1`.`Account`

`Extent1`.`Email`

`Extent1`.`Nickname`

`Extent1`.`AvatarId`

`Extent1`.`InsertTime`

`Extent1`.`UpdateTime`

FROM `Users` AS `Extent1`

 WHERE `Extent1`.`Id` = @p0 LIMIT 2

-- p0: '1' (Type = Int32)

-- Executing at 2018/3/30 17:04:20 +08:00

-- Completed in 9 ms with result: EFMySqlDataReader

UPDATE `Users` SET `Email`=@gp1 WHERE (`Id` = 1) AND (`Email` = @gp2)

-- @gp1: '2@163.com' (Type = String IsNullable = false Size = 9)

-- @gp2: '1@163.com' (Type = String IsNullable = false Size = 9)

-- Executing at 2018/3/30 17:04:21 +08:00

-- Completed in 3 ms with result: 1

SELECT

`Extent1`.`Id`

`Extent1`.`Account`

`Extent1`.`Email`

`Extent1`.`Nickname`

`Extent1`.`AvatarId`

`Extent1`.`InsertTime`

`Extent1`.`UpdateTime`

FROM `Users` AS `Extent1`

 WHERE `Extent1`.`Id` = @p0 LIMIT 2

SELECT

`Extent1`.`Id`

`Extent1`.`Account`

`Extent1`.`Email`

`Extent1`.`Nickname`

`Extent1`.`AvatarId`

`Extent1`.`InsertTime`

`Extent1`.`UpdateTime`

FROM `Users` AS `Extent1`

 WHERE `Extent1`.`Id` = @p0 LIMIT 2

SELECT

`Extent1`.`Id`

`Extent1`.`Account`

`Extent1`.`Email`

`Extent1`.`Nickname`

`Extent1`.`AvatarId`

`Extent1`.`InsertTime`

`Extent1`.`UpdateTime`

FROM `Users` AS `Extent1`

 WHERE `Extent1`.`Id` = @p0 LIMIT 2

-- p0: '1' (Type = Int32)

-- p0: '1' (Type = Int32)

-- p0: '1' (Type = Int32)

-- Executing at 2018/3/30 17:06:12 +08:00

-- Executing at 2018/3/30 17:06:12 +08:00

-- Executing at 2018/3/30 17:06:12 +08:00

-- Completed in 8 ms with result: EFMySqlDataReader

-- Completed in 8 ms with result: EFMySqlDataReader

-- Completed in 8 ms with result: EFMySqlDataReader

UPDATE `Users` SET `Email`=@gp1 WHERE (`Id` = 1) AND (`Email` = @gp2)

UPDATE `Users` SET `Email`=@gp1 WHERE (`Id` = 1) AND (`Email` = @gp2)

-- @gp1: '3@163.com' (Type = String IsNullable = false Size = 9)

-- @gp1: '1@163.com' (Type = String IsNullable = false Size = 9)

-- @gp2: '2@163.com' (Type = String IsNullable = false Size = 9)

-- Executing at 2018/3/30 17:06:12 +08:00

-- @gp2: '2@163.com' (Type = String IsNullable = false Size = 9)

-- Executing at 2018/3/30 17:06:12 +08:00

-- Completed in 3 ms with result: 1

-- Completed in 3 ms with result: 0

SELECT

`Limit1`.`Id`

`Limit1`.`Account`

`Limit1`.`Email`

`Limit1`.`Nickname`

`Limit1`.`AvatarId`

`Limit1`.`InsertTime`

`Limit1`.`UpdateTime`

FROM (SELECT

`Extent1`.`Id`

`Extent1`.`Account`

`Extent1`.`Email`

`Extent1`.`Nickname`

`Extent1`.`AvatarId`

`Extent1`.`InsertTime`

`Extent1`.`UpdateTime`

FROM `Users` AS `Extent1`

 WHERE `Extent1`.`Id` = @p0 LIMIT 2) AS `Limit1`

-- p0: '1' (Type = Int32)

-- Executing at 2018/3/30 17:06:12 +08:00

-- Completed in 1 ms with result: EFMySqlDataReader

UPDATE `Users` SET `Email`=@gp1 WHERE (`Id` = 1) AND (`Email` = @gp2)

-- @gp1: '1@163.com' (Type = String IsNullable = false Size = 9)

-- @gp2: '3@163.com' (Type = String IsNullable = false Size = 9)

-- Executing at 2018/3/30 17:06:14 +08:00

-- Completed in 0 ms with result: 1

分析SQL

日志中出现Completed in 0 ms with result: 0,这说明某一次更新任务是失败的,这应该就出现并发更新的那一次,由于创建了三个并行的任务,所以从打印的日志中比较难以分辨是哪两次更新时发生并发,但是可以通过后面观察打印变量值来判断。这里的日志信息还展示了每条SQL执行的时。

观察上面的SQL语句,发现每个UPDATE 语句都有一个WHERE条件,尤为特别的是`Email` = @gp2,并发检测就是依赖这条语句实现的。当两个线程同时向数据库提交更新任务时,由于其中一个线程已将Email字段值更改,那么另一个线程执行的SQL由于不满足Email字段的匹配条件而修改失败,进而抛出OptimisticConcurrencyException异常。如果查看未配置并发检测生成的UPDATE 语句会更清楚这一点。

未配置并发检测生成的UPDATE 语句:

UPDATE `Users` SET `Email`=@gp1 WHERE `Id` = 1

各个变量的值

【正常线程2@163.com】数据库中原值:2@163.com

【正常线程2@163.com】客户端传值:2@163.com

“System.Data.Entity.Core.OptimisticConcurrencyException”类型的第一次机会异常在 EntityFramework.dll 中发生

“System.Data.Entity.Core.OptimisticConcurrencyException”类型的第一次机会异常在 EntityFramework.dll 中发生

“System.Data.Entity.Core.OptimisticConcurrencyException”类型的第一次机会异常在 EntityFramework.dll 中发生

“System.Data.Entity.Infrastructure.DbUpdateConcurrencyException”类型的第一次机会异常在 EntityFramework.dll 中发生

【正常线程3@163.com】数据库中原值:3@163.com

【正常线程3@163.com】客户端传值:3@163.com

【线程1@163.com】数据库中原值:1@163.com

【线程1@163.com】客户端传值:1@163.com

【线程1@163.com】DbEntityEntry.GetDatabaseValues:3@163.com

【线程1@163.com】DbEntityEntry.OriginalValues:2@163.com

【正常线程1@163.com】数据库中原值:1@163.com

【正常线程1@163.com】客户端传值:1@163.com

分析各个变量值

打印【正常线程】这行文本的代码在context.SaveChanges();这行代码之后,这说明如果能够打印出这行代码,那么就没有发生并发异常,所以上面在发生并发异常之前2@163.com和3@163.com这两个值都成功更新了Email字段,当要使用值1@163.com更新Email字段时,发生了并发异常。使用值2@163.com更新字段发生在使用3@163.com更新字段之前,所以发生并发异常时,数据库中的Email字段值为3@163.com,因此DbEntityEntry.GetDatabaseValues值为3@163.com,而DbEntityEntry.OriginalValues的值为2@163.com。

参考:

https://docs.microsoft.com/en-us/ef/ 

-----------------------------------------------------------------------------------------

转载与引用请注明出处。

时间仓促,水平有限,如有不当之处,欢迎指正。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Entity Framework 并发冲突解决方案
在大多数的应用中都会出现客户端同时发送多个请求对同一条数据就行修改,这个时候就会出现并发冲突。我们一般的做法会有如下两种:
喵叔
2020/09/08
9270
Entity Framework 并发冲突解决方案
Entity Framework——常见报错总结
1 实体属性配置为IsRequired()对更新的影响 抛出异常类型DbEntityValidationException 表结构: 实体: public class User {
甜橙很酸
2018/03/08
8180
Entity Framework——常见报错总结
浅析Entity Framework Core中的并发处理
本文讲述如何使用Entity Framework Core来实现一个乐观并发控制,并介绍在.NET Core中使用DbContext进行数据库操作时的一些常见问题和解决方案。
GuZhenYin
2018/01/04
2.9K0
浅析Entity Framework Core中的并发处理
EntityFramework系列:MySql的RowVersion
无需修改实体和配置,在MySql中使用和SqlServer一致的并发控制。修改RowVersion类型不可取,修改为Timestamp更不可行。Sql Server的RowVersion生成一串唯一的二进制保证Row的版本,无关TimeStamp,更无论TimeStamp的精度问题。使用MySql触发器只能解决uuid的插入的默认值和更新的随机值,由于MySql的自身为了防止无限递归的策略,它的触发器无法在当前表的触发器中更新当前表,所以触发器无法实现更新在SqlServer中由数据库生成的RowVersion字段的值。所以MySql中的RowVersion只能由应用程序赋值。
皇上得了花柳病
2020/05/04
1.4K0
02-EF Core笔记之保存数据
EF Core通过ChangeTracker跟踪需要写入数据库的更改,当需要保存数据时,调用DbContext的SaveChanges方法完成保存。
拓荒者IT
2019/09/23
1.9K0
Entity Framework 数据访问浅谈
在现代的软件开发中,数据库操作是必不可少的一部分。无论是简单的数据读取还是复杂的事务处理,都需要与数据库进行交互。在这个过程中,Entity Framework (EF) 作为 .NET 平台上的一款优秀 ORM(对象关系映射)框架,提供了强大的功能来简化数据库操作。本文将带你快速了解 EF 的基本用法,并探讨一些常见的问题以及如何避免这些错误。
Jimaks
2024/10/19
2100
Entity Framework 数据访问浅谈
Entity Framework快速入门--一对零到一关系处理
很久不更新blog了,正好趁着端午节的空,把之前一段时间使用关于EF以及工作上经验总结一下。
老马
2022/05/10
4200
Entity Framework快速入门--一对零到一关系处理
初探领域驱动设计(2)Repository在DDD中的应用
概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体、值类型和领域服务,也稍微讲到了DDD中的分层结构。但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的,也是我本人之前有一些疑问的地方就是Repository。我之前觉得IRepository和三层里面的IDAL很像,为什么要整出这么个东西来;有人说用EF的话就不需要Repository了;IRepository是鸡肋等等。 我觉得这些问题都很好,我自己也觉得有问题,带着这些问题我们就来看一看Repositor
用户1153966
2018/03/14
1.5K0
初探领域驱动设计(2)Repository在DDD中的应用
Entity Framework复杂类型属性映射
以上代码在ORM中称为组合类,EF会将这两个类映射在一张表中。当Code First发现不能推断出类的主键,并且没有通过Data Annotations或Fluent API注册主键,那么该类型将被自动注册为复杂类型。
喵叔
2020/09/08
7860
Entity Framework复杂类型属性映射
asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦
  学习asp.net 已经有近三个月的时间了,在asp.net mvc上花的时间最多,但个人真是有些菜,不得不说,asp.net mvc的水真的还是蛮深的。目前在公司实习,也见过公司几个项目的代码了。对项目的代码始终停留在一知半解的地步,能改一些简单的bug,但关于项目的来龙去脉始终云里雾里。对于asp.net mvc的架构始终看不懂。因此,照着传智博客的学习视频,学了一下简单的架构搭建。真个架构的搭建我看了将近两遍视频,才稍稍有些头绪,今天在这里记录一下,一方面加深理解,一方面如果以后忘记了,还能快速的想起来,当然如果我的这篇简陋的随笔能有幸被有需要的人看见,并对他们产生一些帮助,我心里肯定也是非常欢欣的。
CherishTheYouth
2019/09/11
1.1K0
asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦
Entity Framework——建模建库
1数据库初始化策略选择 三种初始化策略: 1)CreateDatabaseIfNotExists:默认的策略。如果数据库不存在,那么就创建数据库。但是如果数据库已存在,而且实体发生了变化,就会出现异常。 2)DropCreateDatabaseIfModelChanges:模型改变时,原来的数据库会被删除,自动重新创建一个新的数据库。 3)DropCreateDatabaseAlways:每次运行都会删除原来的数据库,然后重新生成数据库。 4)Null:在Codefirst模式下,当实体结构改变时,运行程序
甜橙很酸
2018/03/08
1.3K0
Entity Framework——建模建库
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
乐观处理:系统认为数据的更新在大多数情况下是不会产生冲突的,只在数据库更新操作提交的时候才对数据作冲突检测(推荐)
郑子铭
2020/12/31
6180
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
Entity Framework Repository模式
如果我们用最原始的EF进行设计对每个实体类的“C(增加)、R(读取)、U(修改)、D(删除)”这四个操作。
aehyok
2018/09/11
1.2K0
Entity Framework Repository模式
Entity Framework Core 总结
dotnet ef migrations add InitialCreate 创建描述表结构的代码文件
yiyun
2022/04/01
1.3K0
Entity Framework Core 总结
EntityFramework 外键值映射
如果在 EF OnModelCreating 中配置了实体外键映射,也就是 SQL Server 中的 ForeignKey,那么我们在添加实体的时候,主实体的主键值会自动映射到子实体的外键值,并且这个操作在一个 SaveChanges 中,但如果没有在 OnModelCreating 中进行外键映射配置,我们添加实体的时候,就不会自动映射外键值了,什么意思呢?我们先看一个示例代码: public class SchoolDbContext : DbContext{ public SchoolDbCo
逸鹏
2018/04/09
4.5K0
EntityFramework 外键值映射
在 Entity Framework Core 中优化查询:实现.NET 中的高性能数据访问
在现代 Web 应用程序中,数据访问在性能方面起着至关重要的作用。Entity Framework Core (EF Core) 是适用于 .NET 的常用 ORM(对象关系映射器),可简化数据库交互。但是,如果不有效使用,可能会导致性能瓶颈。在本文中,我们将探讨在 EF Core 中优化查询的关键策略,以确保应用程序平稳运行。
郑子铭
2024/12/23
5050
在 Entity Framework Core 中优化查询:实现.NET 中的高性能数据访问
Entity Framework 4.1 Code-First 学习笔记
  CodeFirst提供了一种先从代码开始工作,并根据代码直接生成数据库的工作方式。Entity Framework 4.1在你的实体不派生自任何基类、不添加任何特性的时候正常的附加数据库。另外呢,实体的属性也可以添加一些标签,但这些标签不是必须的。下面是一个简单的示例:
拓荒者IT
2019/09/25
1.8K0
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
继续讨论EF中使用存储过程的问题,这回着重讨论的是为存储过程的参数进行赋值的问题。说得更加具体一点,是如何为实体映射的Delete存储过程参数进行赋值的问题。关于文中涉及的这个问题,我个人觉得是EF一个有待改进的地方,不知道各位看官是否同意? 目录 一、EF存储过程参数赋值的版本策略 二、Delete存储参数就一定是Original值吗? 三、如果直接修改.edmx模型的XML呢? 四、为Delete存储过程参数赋上Current值,如何做得到? 一、
蒋金楠
2018/02/07
2K0
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
Entity Framework——性能测试
内容提要 一、对EF框架的性能测试 增、删、改,查测试及性能优化 二、使用sql执行 增、删、改,查测试 三、对以上两种方式对比分析 一 对EF框架的测试 1插入操作测试 测试代码(关键部分) List<Collection> list = new List<Collection>(); int i = 0; while (i < count) { Collection
甜橙很酸
2018/03/08
2K0
Entity Framework——性能测试
Entity Framework Core 捕获数据库变动
在实际项目中我们往往需要记录存储在数据库中数据的变动(例如修改数据前记录下数据的原始值),这样一来在发生误操作时可以将数据恢复到变动前的状态,也可以追溯到数据的修改人。大部分开发人员会自己定义记录数据变动的代码,但是这样不仅费时费力有时还会影响到这个业务的性能。当然,我们也可以利用数据库触发器来记录这些操作,在 SQL Server 数据库 2017 以上版本中给我们提供了跟踪数据库数据更改的功能,利用这个功能可以准确的记录数据库数据的变动。这个功能虽然强大但是某些时候我们使用的数据库并不是 SQL Server 数据库,或者某些情况下我们不适合使用 SQL Server 数据库所提供的这个功能。那么这个时候该怎么办呢?如果你使用的是 Entity Framework Core 2.0 及以上版本来开发项目的话,那这个问题就好解决了。在 Entity Framework Core 中,只要捕获到了数据变更记录,我们就可以将数据随时还原到变更前的状态,在这里数据库变更记录被称为审计数据。那么我们先来看两个问题:
喵叔
2020/09/08
6630
推荐阅读
相关推荐
Entity Framework 并发冲突解决方案
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验