C#实现如何判断一个数组中是否有重复的元素 返回一个数组升序排列后的位置信息--C#程序举例 求生欲很强的数据库 别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework...

C#实现如何判断一个数组中是否有重复的元素

 

 如何判断一个数组中是否有重复的元素 

实现判断数组中是否包含有重复的元素方法 这里用C#代码给出实例

方法一:可以新建一个hashtable利用hashtable的Contains方法进行查找

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

方法二:使用for循环进行比较  需要注意的是j<=i    如果只是等于,实际上效率偏低,有重复计算可以自己摸索,有时间我画个图出来,^_^(这里感谢面试官的提醒)

复制代码
 1         /// <summary>
 2         /// for循环
 3         /// </summary>
 4         /// <param name="yourValue"></param>
 5         /// <returns></returns>
 6         public static bool IsRepeat2(int[] array)
 7         {
 8             for (int i = 0; i < array.Length; i++)
 9             {
10                 for (int j = 0; j < array.Length; j++)
11                 {
12                     if (j <= i)
13                     {
14                         continue;
15                     } 
16                     if (array[i] == array[j])
17                     {
18                         return true;
19                     }
20                 }
21             }
22             return false;
23         }
复制代码

测试代码:

复制代码
 1         static void Main(string[] args)
 2         {
 3             int[] array = new int[] { 1,2,3,4,3,6,7,8};
 4             int[] array2 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 5             Console.WriteLine("---------------包含重复元素----------------");
 6             bool isrepeat = IsRepeat(array);
 7             bool isrepeat2 = IsRepeat2(array);
 8             Console.WriteLine(isrepeat);
 9             Console.WriteLine(isrepeat2);
10 
11             Console.WriteLine("---------------不包含重复元素----------------");
12             bool isrepeat3 = IsRepeat(array2);
13             bool isrepeat4 = IsRepeat2(array2);
14             Console.WriteLine(isrepeat3);
15             Console.WriteLine(isrepeat4);
16             Console.Read();
17         }
复制代码

运行结果:

程序源代码工程下载

各位好汉如果有更好的方法能够优化程序,减少计算的次数,麻烦给出,感激!

 

 

 

返回一个数组升序排列后的位置信息--C#程序举例

 

返回一个数组升序排列后的位置信息--C#程序举例

返回某一个数组升序排序后的位置  比如:{8,10,9,11}排序后应该是{8,9,10,11},但是需要返回{1,3,2,4}   大概记忆里是这么个意思很明显这是一个算法题

同样这是一个面试题做倒是做出来了,但是效率太低了,我都看不上自己做的,感谢面试官给出的提示:这为什么要排序呢?

是的,这根本不需要排序,只需要找出比 对比的元素小的元素个数即可(有了这个思路)可以写程序了

程序方法如下

复制代码
 1         /// <summary>
 2         /// 返回一个数组升序后的位置信息
 3         /// </summary>
 4         /// <param name="array"></param>
 5         /// <returns></returns>
 6         private static int[] getAscendingIndex(int[] array)
 7         {
 8             int[] indexArray = new int[array.Length];
 9             for (int i = 0; i < array.Length; i++)
10             {
11                 int index = 0;
12                 for (int j = 0; j < array.Length; j++)
13                 {
14                     if (array[j]<= array[i])
15                     {
16                         index++;
17                     }
18                 }
19                 indexArray[i] = index;
20             }
21             return indexArray;
22         }
复制代码

测试程序如下:

复制代码
 1         static void Main(string[] args)
 2         {
 3             int[] ArrayTest = new int[4]{8,10,9,11};
 4             int[] resultArray = getAscendingIndex(ArrayTest);
 5             for (int i = 0; i < resultArray.Length; i++)
 6             {
 7                 Console.WriteLine(resultArray[i]);
 8             }
 9 
10             Console.Read();
11         }
复制代码

输出结果:

源程序有需要的可以下载

源代码工程文件下载

各位大侠,如果有更好的方法算法复杂度更低,麻烦给出,感激!欢迎留言

 

 

求生欲很强的数据库

 

 

最近一个月来一直在做某局的项目,涉及到了微信支付相关的业务。业务本身也是一套完整的从下单到支付到退款的全流程,我司和三方联调开发,个中滋味不表。

其中从最开始就遇到一个反复出现(偶发性)的问题,有时候数据库无法快速且正常返回查询结果。我先后从多个方面进行尝试调试,寻求解决办法。

我先还原一下场景。

服务器:2台a和b。

负载均衡:访问任意到打到a或者b。

数据库:前期没有使用持续化存储,用的热数据缓存。此次使用mongo,最开始版本3.0.x,后升级到我司其他线上项目运行版本3.2.6。a和b公用同一台database service,且mongo安装在b上。

 

现象:数据库有时候能返回查询结果,有时候好像卡死在那里,从接口调用到最终数据库服务本身皆无错误日志。我查询的那个集合只有50多条记录,并且我增加了索引在我的查询字段上,按照道理来说应该是毫秒级别的耗时。

 

首先我先从项目数据库调用入手,写了最小化的脚本,单独去读取某一条订单记录,能快速且正常返回。至此说明项目是用的mongo driver是能正常调用到mongo的。后我开启mongoose debug模式,开启方式为在我操作类里添加如下语句:

mongoose.set('debug', true);

开启后,每当调用到数据库操作会输出类似mysql的sql语句,更方便调试跟踪问题。然而我看到的现象是,有时候能正常执行查询的时候就有该debug日志吞吐,而有时候没有任何输出,也无数据返回。

期间,我反复尝试安装并部署不同版本的mongo和对应版本的mongoose,甚至调整了项目框架版本,先排除了各模块之间兼容性问题。

后我怀疑到是否是负载均衡到了a(mongo是安装在b上),而a不能访问到b上的mongo呢?

我查询相关资料,修改了mongo的配置文件,将bind_ip从127.0.0.1改为0.0.0.0,且把maxConns改为2000。试探性地临时关闭了iptables服务,然而没有解决该问题。

在这个过程里面我有意识到,每当重启服务之后短暂的十几秒里面,可以正常进行查询,最多能成功2次,到第三次的时候就会前端调用超时报错。

后为了能顺利进行后续开发(因为很多接口是用到数据库存储本地数据的),我暂时关闭了a上的项目服务,先排除因为网络不通造成的问题。到这个时候数据库查询就开始正常了一段时间,我的开发也能顺利进行下去。

我那个时候暂时把问题归结于网络,因为脚本本地调用是ok的,关闭了负载,b上本地部署mongo调用也是正常的。我和某科的老师一起排查问题,a和b两台机子相互ping和telnet均成功,说明数据链路层是通的,问题应该是在应用层上的。通过尝试添加output设置,将27017端口暴露出去。然后重启a上的服务,再次进行负载均衡,惊喜地发现两边都可以正常使用mongo查询了。到这个时候,我再次把问题归结于网络设置导致每次请求转到a的时候无法连接到mongo,而有时候负载到b是正常的,所以才会偶发性地出现查询结果无法返回导致超时的问题。

Xx大会如期上线了我们的项目,大会期间非常稳定,此后的一周我再也没想起这段为了解决诡异bug而烦恼的时光。直到后面和某海联调退费接口的时候,再次出现该问题。

说实话,到这次复现问题的时候,我是有点没有思路。因为之前几乎把所有可能性都尝试过了。在同事探讨和帮助的过程,我想到了会不会是有其他代码引起mongo操作阻塞呢?

 

在网上也有人遇到过类似问题,讨论回复如下:

有个比较常见的坑是mongodb使用数据库级的锁,在写数据的时候要快,否则很容易把其他mongodb的请求阻塞。你可以尝试在mongodb起来的时候是就开起mongostat,然后查看locked%和qr|qw列的值是否出现异常,飙升。 

还有如果collection中记录很多的话,一定要建立索引。你可以直接在mongo shell下验证你的查询是否有用到索引,我记得mongo语句后跟explain()可以显示索引是否起作用的。

简单说就是有频繁写入的操作和频繁未命中索引(包括无索造成引)查询(被查询的集合数据量较大)的逻辑代码导致数据库级别锁,让其他请求一直排队等待。我想起了每次查看日志的时候总有调用人社接口的日志大量输出,而在那个公共调用的代码里面有一段读写mongo的操作代码。

我仔细看了一下,这段历史遗留代码是为了缓存从人社接口拉取到的数据,其他的我不说了,缓存这段代码毫无意义,首先从量级来看全cd人的数据有无必要存是其一,其二查询没有索引,业务场景上是存储的数据是只增不减的,数据数量越来越大,查询会非常慢。其三我全文搜索也没有找到用到这个数据的其他地方。经过查询,该集合数据量已经超过200万条。

我先屏蔽了该段代码,重启服务后发现之前timeout的接口马上毫秒级返回了数据。多次尝试跑通了全支付流程,再无mongo查询异常出现。之后和写这段代码的前同事沟通后确认该代码是可以删除的,至此该问题尘埃落地。

所以,之前时好时坏,只是因为数据库顽强的求生欲而已。本质是因为那段历史遗留代码会非常频繁地读写数据库,导致其他请求排队等待。通过在这次排查问题,我又一次对分析问题有了新的思考:

  1. 对一些临时接手后有大量历史遗留问题的项目,一定要深入细节,不能因为是继承的代码,默认它是正常无错的,关于那段存储人社数据的代码我也曾经怀疑过,但是没有去深究,默认了它一直使用就是不能轻易改动的。
  2. 结对编程是很重要的。这在一些其他公司已经很常见了,对于小的创业公司,我们考虑更多的是一个工程师去support一个或者多个完整的项目,有时候个人的思路是非常有限的,常见的问题是很容易通过自身努力解决,而一些思路有死角的疑难杂症可能需要群策。我这里说的结对编程特指对该项目负责且全程参与的人。因为往往一些google都很难找到特别接近的解决方案的问题,往往有项目自身特殊原因。

God is in details.God bless my code.

 

 

 

别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework

 

前言

一直以来写的博文都是比较温婉型的博文,今天这篇博文算是一篇批判性博文,有问题欢迎探讨,如标题,你到底会不会用EntityFramework啊。

你到底会不会用EntityFramework啊

  面试过三年至六年的同行,作为过面试者到如今作为面试官也算是老大对我的信任,对来面试的面试者的任何一位同行绝没有刁难之意,若还装逼那就没有什么意义。我也基本不看面试者的项目经历,因为我个人觉得每个面试者所在公司所做项目都不一样,可能面试者项目所做的业务我一点都不知道,而我所关心的是项目当中所用到的技术,稍微看下了简历让面试者简单做个自我介绍,这算是基本流程吧。然后直接问面试者最擅长的技术是哪些?比如ASP.NET MVC、比如ASP.NET Web APi、比如EntityFramework,再比如数据库等等。如果面试者没有特别擅长的技术那我就简历上提出所熟悉和项目当中用到的技术进行提问。这里暂且不提其他技术,单单说EntityFramework,面试的面试者大部分都有用过EntityFramework,我就简单问了下,比如您用的EntityFramework版本是多少?答案是不知道,这个我理解,可能没去关心过这个问题,再比如我问您知道EntityFramework中有哪些继承策略,然后面试者要么是一脸懵逼,要么是不知道,要么回了句我们不用。这个我也能理解,重点来了,我问您在EntityFramwork中对于批量添加操作是怎么做的,无一例外遍历循环一个一个添加到上下文中去,结果令我惊呆了,或许是只关注于实现,很多开发者只关注这个能实现就好了,这里不过多探讨这个问题,每个人观点不一样。

  大部分人用EntityFramework时出现了问题,就吐槽EntityFramework啥玩意啊,啥ORM框架啊,各种问题,我只能说您根本不会用EntityFramework,甚至还有些人并发测试EntityFramework的性能,是的,没错,EntityFramework性能不咋的(这里我们只讨论EF 6.x),或者说在您实际项目当中有了点并发发现EF出了问题,又开始抱怨EF不行了,同时对于轻量级、跨平台、可扩展的EF Core性能秒杀EF,即使你并发测试EF Core性能也就那么回事,我想说的是你并发测试EF根本没有任何意义,请好生理解EF作为ORM框架出现的意义是什么,不就是为了让我们关注业务么,梳理好业务对象,在EF中用上下文操作对象就像直接操作表一样。然后我们回到EF抵抗并发的问题,有的童鞋认为EF中给我提供了并发Token和行版本以及还有事务,这不就是为了并发么,童鞋对于并发Token和行版本这是对于少量的请求可能存在的并发EF团队提出的基本解决方案,对于事务无论是同一上文抑或是跨上下文也好只是为了保证数据一致性罢了。要是大一点的并发来了,您难道还让EF不顾一切冲上去么,这无疑是飞蛾扑火自取灭亡,你到底会不会用EntityFramework啊。EF作为概念上的数据访问层应该是处于最底层,如果我们项目可预见没有所谓的并发问题,将上下文直接置于最上层比如控制器中并没有什么问题,但是项目比较大,随着用户量的增加,我们肯定是可预知的,这个我们需要从项目架构层面去考虑,此时在上下文上游必定还有其他比如C#中的并发队列或者Redis来进行拦截使其串行进行。

  有些人号称是对EntityFramwork非常了解,认为不就是增、删、该、查么,但是有的时候用出了问题就开始自我开解,我这么用没有任何问题啊,我们都知道在EF 6.x中确实有很多坑,这个时候就借这个缘由洗白了,这不是我的锅,结果EF背上了无名之锅,妄名之冤。是的,您们没有说错,EF 6.x是有很多坑,您避开这些坑不就得了,我只能说这些人太浮于表面不了解基本原理就妄下结论,你到底会不会用EntityFramework啊。好了来,免说我纸上谈兵,我来举两个具体例子,您们看你到底会不会用。

EntityFramework 6.x查询

复制代码
        static void Main(string[] args)
        {
            using (var ctx = new EfDbContext())
            {
                ctx.Database.Log = Console.WriteLine;

                var code = "Jeffcky";
                var order = ctx.Orders.FirstOrDefault(d => d.Code == code);           
            };
            Console.ReadKey();
        }
复制代码

这样的例子用过EF 6.x的童鞋估计用烂了吧,然后查询出来的结果让我们也非常满意至少是达到了我们的预期,我们来看看生成的SQL语句。

 

请问用EF的您们发现什么没有,在WHERE查询条件加上了一堆没有用的东西,我只是查询Code等于Jeffcky的实体数据,从生成的SQL来看可查询Code等于Jeffcky的也可查询Code等于空的数据,要是我们如下查询,生成如上SQL语句我觉得才是我们所预期的对不对。

复制代码
            using (var ctx = new EfDbContext())
            {
                ctx.Database.Log = Console.WriteLine;

                var code = "Jeffcky";
                var orders = ctx.Orders.Where(d => d.Code == null || d.Code == code).ToList();

            };
复制代码

如果您真的会那么一点点用EntityFramework,那么请至少了解背后生成的SQL语句吧,这是其中之一,那要是我们直接使用值查询呢,您们觉得是否和利用参数生成的SQL语句是一样的呢?

复制代码
            using (var ctx = new EfDbContext())
            {
                ctx.Database.Log = Console.WriteLine;

                var order = ctx.Orders.FirstOrDefault(d => d.Code == "Jeffcky");

            };
复制代码

出乎意料吧,利用值查询在WHERE条件上没有过多的条件过滤,而利用参数查询则是生成过多的条件筛选,到这里是不是就到此为止了呢,如果您们对于参数查询不想生成对空值的过滤,我们在上下文构造函数中可关闭这种所谓【语义可空】判断,如下:

复制代码
    public class EfDbContext : DbContext
    {
        public EfDbContext() : base("name=ConnectionString")
        {
            Configuration.UseDatabaseNullSemantics = true;
        }
     }
复制代码

// 摘要:
// 获取或设置一个值,该值指示当比较两个操作数,而它们都可能为 null 时,是否展示数据库 null 语义。默认值为 false。例如:如果 UseDatabaseNullSemantics
// 为 true,则 (operand1 == operand2) 将转换为 (operand1 = operand2);如果 UseDatabaseNullSemantics
// 为 false,则将转换为 (((operand1 = operand2) AND (NOT (operand1 IS NULL OR operand2
// IS NULL))) OR ((operand1 IS NULL) AND (operand2 IS NULL)))。
//
// 返回结果:
// 如果启用数据库 null 比较行为,则为 true;否则为 false。

在EF 6.x中对于查询默认情况下会进行【语义可空】筛选,通过如上分析,不知您们是否知道如上的配置呢。

EntityFramework 6.x更新

EF 6.x更新操作又是用熟透了吧,在EF中没有Update方法,而在EF Core中存在Update和UpdateRange方法,您们是否觉得更新又是如此之简单呢?我们下面来首先看一个例子,看看您们是否真的会用。

复制代码
        static Customer GetCustomer()
        {
            var customer = new Customer()
            {
                Id = 2,
                CreatedTime = DateTime.Now,
                ModifiedTime = DateTime.Now,
                Email = "2752154844@qq.com",
                Name = "Jeffcky1"
            };
            return customer;
        }
复制代码

如上实体如我们请求传到后台需要修改的实体(假设该实体在数据库中存在哈),这里我们进行写死模拟。接下来我们来进行如下查询,您们思考一下是否能正常更新呢?

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers.FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    ctx.Customers.Attach(customer);
                    ctx.Entry(customer).State = EntityState.Modified;
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

首先我们根据传过来的实体主键去数据库中查询是否存在,若存在则将传过来的实体附加到上下文中(因为此时请求过来的实体还未被跟踪),然后将其状态修改为已被修改,最后提交,解释的是不是非常合情合理且合法,那是不是就打印更新成功了呢?

看到上述错误想必有部分童鞋一下子就明白问题出在哪里,当我们根据传过来的实体主键去数据库查询,此时在数据库中存在就已被上下文所跟踪,然后我们又去附加已传过来的实体且修改状态,当然会出错因为在上下文已存在相同的对象,此时必然会产生已存在主键冲突。有的童鞋想了直接将传过来的实体状态修改为已修改不就得了么,如下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                ctx.Entry(customer).State = EntityState.Modified;
                if (ctx.SaveChanges() > 0)
                {
                    Console.WriteLine("更新成功");
                }
                else
                {
                    Console.WriteLine("更新失败");
                }
            };
复制代码

如此肯定能更新成功了,我想都不会这么干吧,要是客户端进行传过来的主键在数据库中不存在呢(至少我们得保证数据是已存在才修改),此时进行如上操作将抛出如下异常。

此时为了解决这样的问题最简单的方法之一则是在查询实体是否存在时直接通过AsNoTracking方法使其不能被上下文所跟踪,这样就不会出现主键冲突的问题。

 var dataBaseCustomer = ctx.Customers
                    .AsNoTracking()
                    .FirstOrDefault(d => d.Id == customer.Id);

我们继续往下探讨 ,此时我们将数据库Email修改为可空(映射也要对应为可空,否则抛出验证不通过的异常,你懂的),如下图:

 

然后将前台传过来的实体进行如下修改,不修改Email,我们注释掉。

复制代码
        static Customer GetCustomer()
        {
            var customer = new Customer()
            {
                Id = 2,
                CreatedTime = DateTime.Now,
                ModifiedTime = DateTime.Now,
                //Email = "2752154844@qq.com",
                Name = "Jeffcky1"
            };
            return customer;
        }
复制代码

我们接着再来进行如下查询试试看。

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .AsNoTracking()
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    ctx.Customers.Attach(customer);
                    ctx.Entry(customer).State = EntityState.Modified;
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

此时Email为可空,因为我们设置实体状态为Modified,此时将对实体进行全盘更新,所以对于设置实体状态为Modified是针对所有列更新,要是我们只想更新指定列,那这个就不好使了,此时我们可通过Entry().Property()...来手动更新指定列,比如如下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .AsNoTracking()
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    ctx.Customers.Attach(customer);
                    ctx.Entry(customer).Property(p => p.Name).IsModified = true;
                    ctx.Entry(customer).Property(p => p.Email).IsModified = true;
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

我们继续往下走。除了上述利用AsNoTracking方法外使其查询出来的实体未被上下文跟踪而成功更新,我们还可以使用手动赋值的方式更新数据,如下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    dataBaseCustomer.CreatedTime = customer.CreatedTime;
                    dataBaseCustomer.ModifiedTime = customer.ModifiedTime;
                    dataBaseCustomer.Email = customer.Email;
                    dataBaseCustomer.Name = customer.Name;
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

如上也能更新成功而不用将查询出来的实体未跟踪,然后将前台传过来的实体进行附加以及修改状态,下面我们删除数据库中创建时间和修改时间列,此时我们保持数据库中数据和从前台传过来的数据一模一样,如下:

复制代码
        static Customer GetCustomer()
        {
            var customer = new Customer()
            {
                Id = 2,
                Email = "2752154844@qq.com",
                Name = "Jeffcky1"
            };
            return customer;
        }
复制代码

接下来我们再来进行如下赋值修改,你会发现此时更新失败的:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    dataBaseCustomer.Email = customer.Email;
                    dataBaseCustomer.Name = customer.Name;
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

这是为何呢?因为数据库数据和前台传过来的数据一模一样,但是不会进行更新,毫无疑问EF这样处理是明智且正确的,无需多此一举更新,那我们怎么知道是否有不一样的数据进行更新操作呢,换句话说EF怎样知道数据未发生改变就不更新呢?我们可以用上下文属性中的ChangeTacker中的HasChanges方法,如果上下文知道数据未发生改变,那么直接返回成功,如下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    dataBaseCustomer.Email = customer.Email;
                    dataBaseCustomer.Name = customer.Name;
                    if (!ctx.ChangeTracker.HasChanges())
                    {
                        Console.WriteLine("更新成功");
                        return;
                    }
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

好了到此为止我们已经看到关于更新已经有了三种方式,别着急还有最后一种,通过Entry().CurrentValues.SetValues()方式,这种方式也是指定更新,将当前实体的值设置数据库中查询出来所被跟踪的实体的值。如下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = GetCustomer();
                var dataBaseCustomer = ctx.Customers
                    .FirstOrDefault(d => d.Id == customer.Id);
                if (dataBaseCustomer != null)
                {
                    ctx.Entry(dataBaseCustomer).CurrentValues.SetValues(customer);
                    if (ctx.SaveChanges() > 0)
                    {
                        Console.WriteLine("更新成功");
                    }
                    else
                    {
                        Console.WriteLine("更新失败");
                    }
                }

            };
复制代码

关于EF更新方式讲了四种,其中有关细枝末节就没有再细说可自行私下测试,不知道用过EF的您们是否四种都知道以及每一种对应的场景是怎样的呢?对于数据更新我一般直接通过查询进行赋值的形式,当然我们也可以用AutoMapper,然后通过HasChanges方法来进行判断。

EntityFramework 6.x批量添加

对于批量添加已经是EF 6.x中老掉牙的话题,但是依然有很多面试者不知道,我这里再重新讲解一次,对于那些私下不学习,不与时俱进的童鞋好歹也看看前辈们(不包括我)总经的经验吧,不知道为何这样做,至少回答答案是对的吧。看到下面的批量添加数据代码是不是有点想打人。

复制代码
           using (var ctx = new EfDbContext())
            {
                for (var i = 0; i <= 100000; i++)
                {
                    var customer = new Customer
                    {
                        Email = "2752154844@qq.com",
                        Name = i.ToString()
                    };
                    ctx.Customers.Add(customer);
                    ctx.SaveChanges();
                }
            };
复制代码

至于原因无需我过多解释,如果您这样操作,那您这一天的工作大概也就是等着数据添加完毕,等啊等。再不济您也将SaveChanges放在最外层一次性提交啊,这里我就不再测试,浪费时间在这上面没必要,只要您稍微懂点EF原理至少会如下这么使用。

复制代码
            var customers = new List<Customer>();
            using (var ctx = new EfDbContext())
            {
                for (var i = 0; i <= 100000; i++)
                {
                    var customer = new Customer
                    {
                        Email = "2752154844@qq.com",
                        Name = i.ToString()
                    };
                    customers.Add(customer);
                }
                ctx.Customers.AddRange(customers);
                ctx.SaveChanges();
            };
复制代码

如果您给我的答案如上,我还是认可的,要是第一种真的说不过去了啊。经过如上操作依然有问题,我们将所有记录添加到同一上下文实例,这意味着EF会跟踪这十万条记录, 对于刚开始添加的几个记录,会运行得很快,但是当越到后面到达到十万时,EF正在追踪更大的对象图,您们觉得恐怖不,这就是您不懂EF原理的代价,还对其进行诟病,吐槽性能可以,至少保证你写的代码没问题吧,我们进一步优化需要关闭自调用的DetectChanges方法无需进行对每一个添加的实体进行扫描。

复制代码
            var customers = new List<Customer>();
            using (var ctx = new EfDbContext())
            {
                bool acd = ctx.Configuration.AutoDetectChangesEnabled;
                try
                {
                    ctx.Configuration.AutoDetectChangesEnabled = false;
                    for (var i = 0; i <= 100000; i++)
                    {
                        var customer = new Customer
                        {
                            Email = "2752154844@qq.com",
                            Name = i.ToString()
                        };
                        customers.Add(customer);
                    }
                    ctx.Customers.AddRange(customers);
                    ctx.SaveChanges();
                }
                finally
                {
                    ctx.Configuration.AutoDetectChangesEnabled = acd;
                }
            };
复制代码

此时我们通过局部关闭自调用DetectChanges方法,此时EF不会跟踪实体,这样将不会造成全盘扫描而使得我们不会处于漫长的等待,如此优化将节省大量时间。如果在我们了解原理的前提下知道添加数据到EF上下文中,随着数据添加到集合中也会对已添加的数据进行全盘扫描,那我们何不创建不同的上下文进行批量添加呢?未经测试在这种情况下是否比关闭自调用DetectChanges方法效率更高,仅供参考,代码如下:

复制代码
    public static class EFContextExtensions
    {
        public static EfDbContext BatchInsert<T>(this EfDbContext context, T entity, int count, int batchSize) where T : class
        {
            context.Set<T>().Add(entity);

            if (count % batchSize == 0)
            {
                context.SaveChanges();
                context.Dispose();
                context = new EfDbContext();
            }
            return context;
        }
    }
复制代码
复制代码
        static void Main(string[] args)
        {
            var customers = new List<Customer>();
            EfDbContext ctx;
            using (ctx = new EfDbContext())
            {
                for (var i = 0; i <= 100000; i++)
                {
                    var customer = new Customer
                    {
                        Email = "2752154844@qq.com",
                        Name = i.ToString()
                    };
                    ctx = ctx.BatchInsert(customer, i, 100);
                }
                ctx.SaveChanges();
            };
            Console.ReadKey();
        }    
复制代码

总结

不喜勿喷,敢问您们到底会不会用EntityFramework啊,EF 6.x性能令人诟病但是至少得保证您们写的代码没问题吧,您们可结合Dapper使用啊,担心EF 6.x性能不行,那请用EntityFramework Core吧,你值得拥有。谨以此篇批判那些不会用EF的同行,还将EF和并发扯到一块,EF不是用来抵抗并发,它的出现是为了让我将重心放在梳理业务对象,关注业务上,有关我对EF 6.x和EF Core 2.0理解全部集成到我写的书《你必须掌握的EntityFramework 6.x与Core 2.0》最近即将出版,想了解的同行可关注下,谢谢。

转载于:https://www.cnblogs.com/cjm123/p/9004541.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值