项目2.0基本已经上线了,以前老大问我最近还有在更博客没,我说没,同事说是否是已经挖掘尽了没有什么可写的了,其实否则,对于项目而言,咱们都只是负责项目中的冰山一角,没有一个全局观来看待整个项目,急急忙忙的赶着项目,项目中有不少优秀的地方都值得我去效仿和学习,尤为是老大的技术令我折服,这两天不是太忙,就随手看了下本身写的代码,一脸懵逼,心想这是哪一个傻逼写的代码,就像新入职同事看着刚离职交接项目的同事的代码同样复杂的心情,可是项目已经划了基线,除非是bug已经不能轻易再发布了,因此让这份记忆暂且存放本身博客中吧。本篇属于各类杂谈,没有一个统一方向,想到哪里写到哪里。数据库
以前一直有在谈不管是以前的EntityFramework仍是现已经跨平台的EntityFramework Core都有其变动追踪,说白了就是缓存,那么到底怎么知道是缓存了仍是没有缓存呢,下面咱们来看看例子。首先看看数据库中id=2的数据数组
上述咱们只需知道id = 2和Name = "Jeffcky",下面咱们进行以下查询:缓存
public void Query() { var blog1 = _efCoreContext.Blogs.Find(2); blog1.Name = "Jeff"; var blog2 = _efCoreContext.Blogs.FirstOrDefault(d => d.Name == "Jeffcky"); var blog2Name = blog2.Name; var compareResult = ReferenceEquals(blog1, blog2); var blog3 = _efCoreContext.Blogs.FromSql(@"SELECT TOP (1) [Id], [Name], [Url], [Status], [CreatedTime] FROM dbo.[Blog]").Single(); var compareResult1 = ReferenceEquals(blog1, blog3); }
若是不存在缓存那么blog2Name = "Jeffcky"且compareResult = false,compareResult1 = false,可是请看以下演示结果:post
若是关闭变动追踪,此时以下则compareResult = false:性能
var blog1 = _efCoreContext.Blogs.Find(2); blog1.Name = "Jeff"; var blog2 = _efCoreContext.Blogs .AsNoTracking() .FirstOrDefault(d => d.Name == "Jeffcky"); var blog2Name = blog2.Name; var compareResult = ReferenceEquals(blog1, blog2);
这样的场景应该很常见,查出一个集合中包含另一个集合,可是呢,查询出数据后须要对该集合中的另一个集合数据进行处理,好比查询出中Blog集合中存在Post集合,此时须要将Posts中的集合数据中的Title进行修改,此时你会怎样作呢?怎样作才会更加优雅呢,下面是我原始作法:学习
public void Query() { var blogs = _efCoreContext.Blogs .Include(d => d.Posts) .ToList(); foreach (var blog in blogs) { foreach (var post in blog.Posts) { post.Title = "[置顶]EntityFramework之DetectChanges's Secrets(三)(我为EF正名)"; } } }
此时到这里任务算是完成了,过后返回再看感受作法是否是有点low,说到底仍是对集合中各类方法了解太少的缘故去看了看对集合操做的扩展方法有两种方法就可规避这种循环遍历问题,不少时候咱们须要查找判断一个集合中是否存在知足条件的数据而后进行下一步操做这个时候咱们想到会利用Any解决以下:this
var blogs = _efCoreContext.Blogs .Include(d => d.Posts) .ToList(); var isExist = blogs.Any(d => d.Status == 0); if (isExist) { }
又或者所有知足才进行下一步操做,此时利用All判断:spa
var blogs = _efCoreContext.Blogs .Include(d => d.Posts) .ToList(); var isAllSatisfy = blogs.All(d => d.Status == 0); if (isAllSatisfy) { }
可是咱们只是仅止于此对两者的使用,对于上述循环遍历修改数据的问题就可用All来优雅解决上述遍历恶心的问题:3d
var blogs = _efCoreContext.Blogs .Include(d => d.Posts) .ToList(); blogs.All(b => { b.Posts.All(p => { p.Title = "[置顶]EntityFramework之DetectChanges's Secrets(三)(我为EF正名)";return true; }); return true; });
如此优雅的修改集合中集合的数据,多是我的感受吧,有了几层循环就感受特别恶心就想着是否是有更加简洁或者优雅的解决方式,咱们追求的是代码的优雅和简洁而不是繁琐和臃肿。 下面咱们再来看看其余解决方案,经过SelectMany投影解决:code
var blogs = _efCoreContext.Blogs .Include(d => d.Posts) .ToList(); var posts = blogs.SelectMany(b => b.Posts); foreach (var post in posts) { post.Title = "[置顶]EntityFramework之DetectChanges's Secrets(三)(我为EF正名)"; }
如此一看也就一层循环比最土最low的方案甚是优雅别致多了。
咱们直接查看以下两者返回值便可:
或许将上述两者返回值用更具体的返回值类型给出更加明了,以下:
IEnumerable<IEnumerable<Post>> selectBlogs = blogs.Select(d => d.Posts);
IEnumerable<Post> selectManyPosts = blogs.SelectMany(b => b.Posts);
由上知Select返回的是Posts集合的集合,即将Blogs中的每一项Posts做为一个集合最外围是这整个每一项的集合,而SelectMany则是返回Blogs中的全部Posts将其做为一个集合而返回。
在项目中有这样一个场景:一个产品有许多属性,好比颜色,内存,大小,第一次则是进行建立生成惟一sku,下次能够从新添加属性中的值,好比第一次建立时只添加了属性是颜色,属性值为金色的手机,第二次再来添加属性为颜色,属性值为白色的手机,如此一来则和其余属性值进行从新生成新的sku,可是此时又要保证不能和以前已建立的sku重复,此时就要将每组属性中属性值组成的数据和已组合的属性值进行比较,若存在则跳过,不然则建立sku,因为此时生成的属性值顺序可能又不一样,因此此时我将每一组组合的属性值放在List集合中,而后再排序,而后再来比较,可是如何比较两个集合中的数字类型的数据是同样的呢,因而最终转换成了字符串的判断:
var data1 = new List<int>() { 2, 3, 4, 6, 8, 9 }; var data2 = new List<int>() { 3, 6, 8, 2, 4, 9 }; data2.Sort(); var str1 = string.Join("-", data1.ToArray()); var str2 = string.Join("-", data2.ToArray()); var isEquals = str1.Equals(str2);
也算是达到了预期,可是看起来仍是有点low,因而今天又去看了看集合中的扩展方法,竟然能够直接判断两个集合是否相等的方法,固然这是证对于值类型而言,如果引用类型,引用地址都不同即便数据同样确定是不相等的,这点你们都明白就无需我废话了。
var data1 = new List<int>() { 2, 3, 4, 6, 8, 9 }; var data2 = new List<int>() { 3, 6, 8, 2, 4, 9 }; data2.Sort(); var isSequenEqual = data1.SequenceEqual(data2);
这样一写又比上述将集合转换成数组,而后转换成字符串的形式更加优雅,一步到位,我竟然没想到,shit。
在集合中须要对集合数据进行类型转换有Cast和OfType两种形式,例如以下皆可:
var list = new List<int>() { 2, 3, 4, 6, 8, 9 }; IEnumerable<string> casList = list.Cast<string>(); IEnumerable<string> ofTypeList = list.OfType<string>();
上述两者转换皆可,那给出两者转换的意义在哪里呢?这个须要好好想一想。
OfType:只是将集合中的数据能/能够转换成须要转换的类型进行转换。
Cast:将集合中全部数据转换成须要转换的类型。
Cast相对OfType而言将鼠标放在此方法上会发现多了以下转换异常的类(InvalidCastException)
例如对以下数据进行类型转换:
object[] objArray = new object[] { "12345", 12 }; var objCast = objArray.Cast<string>().ToArray(); var objOfType = objArray.OfType<string>().ToArray();
上述咱们已经讨论过两者的区别,此时利用Cast则转换失败出现InvalidCastException。而OfType则将字符串“12345”进行转换。
如上Cast和OfType内部本质实现原理以下:
public IEnumerable<T> Cast<T>(this IEnumerable source) { foreach (object o in source) yield return (T)o; } public IEnumerable<T> OfType<T>(this IEnumerable source) { foreach (object o in source) if (o is T) yield return (T)o; }
这样就不难解释Cast是全盘转换,而OfType是知足条件类型才转换。
对于客户端传过来的数据永不可信,在客户端调用接口时第一时间就要对参数进行校验才进行下一步操做,有这样一个场景,客户端将数据拼接成字符串,咱们须要获取其中整型且过滤其中重复以避免返回重复数据,以下一个字符串:
var str = "1,2,4,eee,4,7,8,s,j,1";
自从有了新语法特性出现后,对于TryParse无需再额外定义out类型参数,若转换失败则out类型参数为默认值,如此一来进行以下几行代码便可解决问题。
var list = new List<int>(); var str = "1,2,4,eee,4,7,8,s,j,1"; var splitArray = str.Split(','); foreach (var data in splitArray) { int.TryParse(data, out int intData); if (list.Contains(intData) || intData <= 0) { continue; }; list.Add(intData); }
固然则是客户端传来的为字符串,直接返回int数组就无需转换了不是,均可以,办法老是有的,就看如何简便的解决不是,没必要纠结于此。
有些知识点都了解且都知道,可是没有应用场景,等到有了应用场景却忘却了技术知识点,因此仍是要擦亮眼睛,因此须要多看看别人优秀的代码或者开源的东西就知道何时该用,何时不应用,好比如何判断引用类型是否为空的状况。50%以上的人判断类型是否为NULL,经过以下判断.
var list = new List<int>() { 1, 2, 3, 4, 5 }; if (list == null) { }
你是否还记得ReferenceEquals,它只判断引用类型,值类型永远为false,估计学过就知道这么回事,然而判断引用类型是否为空就能够用它来判断。
var list = new List<int>() { 1, 2, 3, 4, 5 }; if (ReferenceEquals(list, null)) { }
关于利用ReferenceEquals来判断为NULL的状况仍是看的EntityFramework Core源码,里面判断为NULL都是这么判断,很差听一点就是装装逼,好听一点则是优雅一点,C#语法就是两个字【优雅】,固然在这里并非想推翻什么或者建议什么,两者皆可,只是想代表任何语法的出现都有其应用场景,有些你不多用到的语法就遗漏了,有时候要适当的去补补基础,去对基础回回炉,这是我想说的观点,且勿断章取义。
不管是平常自学仍是项目事后也好,都须要抽时间去整理和总结一下,要否则下次仍是会采起一样不合理的方案去解决,上述说述各类方案且不说本质上性能是同样的,至少看起来更加优雅且代码书写量更少不是,而不是一眼放去几层遍历,有时候咱们以为代码有点看不下去这个时候就要想一想重构或者是否有更加简洁的方式来实现,这样才能更快成长起来,才能走得更远,技术才能积累的更多,日积月累,总结的多了,技术也就上了,不过是花费半天的功夫而已,哪有现成的事情,有些东西没接触过,亲身验证或者走过坑,也就长见识了,后续会陆陆续续更新项目当中遇到的问题和开始学习VUE,项目一直在用VUE,接下来可能会开始更新VUE,不止于此其余也会同步更新,see u。