項目2.0上線,回想事後雜談總結基礎回顧一番

前言

項目2.0基本已經上線了,以前老大問我最近還有在更博客沒,我說沒,同事說是否是已經挖掘盡了沒有什麼可寫的了,其實否則,對於項目而言,咱們都只是負責項目中的冰山一角,沒有一個全局觀來看待整個項目,急急忙忙的趕着項目,項目中有不少優秀的地方都值得我去效仿和學習,尤爲是老大的技術令我折服,這兩天不是太忙,就隨手看了下本身寫的代碼,一臉懵逼,心想這是哪一個傻逼寫的代碼,就像新入職同事看着剛離職交接項目的同事的代碼同樣複雜的心情,可是項目已經劃了基線,除非是bug已經不能輕易再發布了,因此讓這份記憶暫且存放本身博客中吧。本篇屬於各類雜談,沒有一個統一方向,想到哪裏寫到哪裏。數據庫

EntityFramework/EntityFramework Core緩存

以前一直有在談不管是以前的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的方案甚是優雅別緻多了。

那麼問題就來了,Select和SelectMany有什麼區別喲?

咱們直接查看以下兩者返回值便可:

 

 或許將上述兩者返回值用更具體的返回值類型給出更加明瞭,以下:

            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區別

 在集合中須要對集合數據進行類型轉換有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數組就無需轉換了不是,均可以,辦法老是有的,就看如何簡便的解決不是,沒必要糾結於此。

判斷引用類型爲NULL

有些知識點都瞭解且都知道,可是沒有應用場景,等到有了應用場景卻忘卻了技術知識點,因此仍是要擦亮眼睛,因此須要多看看別人優秀的代碼或者開源的東西就知道何時該用,何時不應用,好比如何判斷引用類型是否爲空的狀況。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。

相關文章
相關標籤/搜索