標籤: 銷售文案

  • ASP.NET Core中間件與HttpModule有何不同

    前言

    在ASP.NET Core中最大的更改之一是對Http請求管道的更改,在ASP.NET中我們了解HttpHandlerHttpModule但是到現在這些已經被替換為中間件那麼下面我們來看一下他們的不同處。

    HttpHandler

    Handlers處理基於擴展的特定請求,HttpHandlers作為進行運行,同時做到對ASP.NET響應請求。他是一個實現System.Web.IHttphandler接口的類。任何實現IHttpHandler接口的類都可以作為Http請求處理響應的目標程序。
    它提供了對文件特定的擴展名處理傳入請求,
    ASP.NET框架提供了一些默認的Http處理程序,最常見的處理程序是處理.aspx文件。下面提供了一些默認的處理程序。

    Handler Extension Description
    Page Handler .aspx handle normal WebPages
    User Control Handler .ascx handle Web user control pages
    Web Service Handler .asmx handle Web service pages
    Trace Handler trace.axd handle trace functionality

    創建一個自定義HttpHandler

    
    public class CustomHttpHandler:IHttpHandler
    {
        
        public bool IsReusable
        {
            //指定是否可以重用處理程序
            get {return true;}
        }
        
        public void ProcessRequest(HttpContext context)
        {
            //TODO
            throw new NotImplementedException();
        }
    }
    
    

    在web.config中添加配置項

    <!--IIS6或者IIS7經典模式-->  
      <system.web>  
        <httpHandlers>  
          <add name="mycustomhandler" path="*.aspx" verb="*" type="CustomHttpHandler"/>  
        </httpHandlers>  
      </system.web>  
       
    <!--IIS7集成模式-->  
      <system.webServer>  
        <handlers>  
           <add name="mycustomhandler" path="*.aspx" verb="*" type="CustomHttpHandler"/>  
        </handlers>  
      </system.webServer>  
    

    異步HttpHandlers

    異步的話需要繼承HttpTaskAsyncHandler類,HttpTaskAsyncHandler類實現了IHttpTaskAsyncHandlerIHttpHandler接口

    public class CustomHttpHandlerAsync:HttpTaskAsyncHandler
    {
        
        public override Task ProcessRequestAsync(HttpContext context)
        {
    
            throw new NotImplementedException();
        }
    }
    

    HttpModule

    下面是來自MSDN

    Modules are called before and after the handler executes. Modules enable developers to intercept, participate in, or modify each individual request. Modules implement the IHttpModule interface, which is located in the System.Web namespace.

    HttpModule類似過濾器,它是一個基於事件的,在應用程序發起到結束的整個生命周期中訪問事件

    自定義一個HttpModule

    public class CustomModule : IHttpModule
        {
            public void Dispose()
            {
                throw new NotImplementedException();
            }
    
            public void Init(HttpApplication context)
            {
                context.BeginRequest += new EventHandler(BeginRequest);
                context.EndRequest += new EventHandler(EndRequest);
            }
            void BeginRequest(object sender, EventArgs e)
            {
                ((HttpApplication)sender).Context.Response.Write("請求處理前");
            }
    
            void EndRequest(object sender, EventArgs e)
            {
                ((HttpApplication)sender).Context.Response.Write("請求處理結束后");
            }
        }
    
    
    

    web.config中配置

    <!--IIS6或者IIS7經典模式-->  
    <system.web>  
        <httpModules>  
          <add name="mycustommodule" type="CustomModule"/>  
        </httpModules>  
      </system.web>  
    <!--IIS7集成模式-->  
    <system.webServer>  
        <modules>  
          <add name="mycustommodule" type="CustomModule"/>  
        </modules>  
    </system.webServer>  
    

    中間件

    中間件可以視為集成到Http請求管道中的小型應用程序組件,它是ASP.NET中HttpModule和HttpHandler的結合,它可以處理身份驗證、日誌請求記錄等。

    中間件和HttpModule的相似處

    中間件和HttpMoudle都是可以處理每個請求,同時可以配置進行返回我們自己的定義。

    中間件和httpModule之間的區別

    HttpModule 中間件
    通過web.config或global.asax配置 在Startup文件中添加中間件
    執行順序無法控制,因為模塊順序主要是基於應用程序生命周期事件 可以控制執行內容和執行順序按照添加順序執行。
    請求和響應執行順序保持不變 響應中間件順序與請求順序相反
    HttpModules可以附件特定應用程序事件的代碼 中間件獨立於這些事件

    中間件示例

     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
    
          app.UseHttpsRedirection();
    
          app.UseRouting();
    
          app.UseAuthorization();
    
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapControllers();
          });
      }
    

    在如上代碼片段中我們有一些中間件的添加,同時也有中間件的順序。

    Reference

    How ASP.NET Core 1.0 Middleware is different from HttpModule

    https://support.microsoft.com/en-us/help/307985/info-asp-net-http-modules-and-http-handlers-overview

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計最專業,超強功能平台可客製化

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家公司費用怎麼算?

  • 小師妹學JavaIO之:文件系統和WatchService

    小師妹學JavaIO之:文件系統和WatchService

    目錄

    • 簡介
    • 監控的痛點
    • WatchService和文件系統
    • WatchSerice的使用和實現本質
    • 總結

    簡介

    小師妹這次遇到了監控文件變化的問題,F師兄給小師妹介紹了JDK7 nio中引入的WatchService,沒想到又順道普及了一下文件系統的概念,萬萬沒想到。

    監控的痛點

    小師妹:F師兄最近你有沒有感覺到呼吸有點困難,后領有點涼颼颼的,說話有點不順暢的那種?

    沒有啊小師妹,你是不是秋衣穿反了?

    小師妹:不是的F師兄,我講的是心裏的感覺,那種莫須有的壓力,還有一絲悸動纏繞在心。

    別繞彎子了小師妹,是不是又遇到問題了。

    更多精彩內容且看:

    • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
    • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
    • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
    • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

    更多內容請訪問www.flydean.com

    小師妹:還是F師兄懂我,這不上次的Properties文件用得非常上手,每次修改Properties文件都要重啟java應用程序,真的是很痛苦。有沒有什麼其他的辦法呢?

    辦法當然有,最基礎的辦法就是開一個線程定時去監控屬性文件的最後修改時間,如果修改了就重新加載,這樣不就行了。

    小師妹:寫線程啊,這麼麻煩,有沒有什麼更簡單的辦法呢?

    就知道你要這樣問,還好我準備的比較充分,今天給你介紹一個JDK7在nio中引入的類WatchService。

    WatchService和文件系統

    WatchService是JDK7在nio中引入的接口:

    監控的服務叫做WatchService,被監控的對象叫做Watchable:

    WatchKey register(WatchService watcher,
                          WatchEvent.Kind<?>[] events,
                          WatchEvent.Modifier... modifiers)
            throws IOException;
    WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)
            throws IOException;
    

    Watchable通過register將該對象的WatchEvent註冊到WatchService上。從此只要有WatchEvent發生在Watchable對象上,就會通知WatchService。

    WatchEvent有四種類型:

    1. ENTRY_CREATE 目標被創建
    2. ENTRY_DELETE 目標被刪除
    3. ENTRY_MODIFY 目標被修改
    4. OVERFLOW 一個特殊的Event,表示Event被放棄或者丟失

    register返回的WatchKey就是監聽到的WatchEvent的集合。

    現在來看WatchService的4個方法:

    1. close 關閉watchService
    2. poll 獲取下一個watchKey,如果沒有則返回null
    3. 帶時間參數的poll 在等待的一定時間內獲取下一個watchKey
    4. take 獲取下一個watchKey,如果沒有則一直等待

    小師妹:F師兄,那怎麼才能構建一個WatchService呢?

    上次文章中說的文件系統,小師妹還記得吧,FileSystem中就有一個獲取WatchService的方法:

    public abstract WatchService newWatchService() throws IOException;
    

    我們看下FileSystem的結構圖:

    在我的mac系統上,FileSystem可以分為三大類,UnixFileSystem,JrtFileSystem和ZipFileSystem。我猜在windows上面應該還有對應的windows相關的文件系統。小師妹你要是有興趣可以去看一下。

    小師妹:UnixFileSystem用來處理Unix下面的文件,ZipFileSystem用來處理zip文件。那JrtFileSystem是用來做什麼的?

    哎呀,這就又要扯遠了,為什麼每次問問題都要扯到天邊….

    從前當JDK還是9的時候,做了一個非常大的改動叫做模塊化JPMS(Java Platform Module System),這個Jrt就是為了給模塊化系統用的,我們來舉個例子:

    public void useJRTFileSystem(){
            String resource = "java/lang/Object.class";
            URL url = ClassLoader.getSystemResource(resource);
            log.info("{}",url);
        }
    

    上面一段代碼我們獲取到了Object這個class的url,我們看下如果是在JDK8中,輸出是什麼:

    jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/Object.class
    

    輸出結果是jar:file表示這個Object class是放在jar文件中的,後面是jar文件的路徑。

    如果是在JDK9之後:

    jrt:/java.base/java/lang/Object.class
    

    結果是jrt開頭的,java.base是模塊的名字,後面是Object的路徑。看起來是不是比傳統的jar路徑更加簡潔明了。

    有了文件系統,我們就可以在獲取系統默認的文件系統的同時,獲取到相應的WatchService:

    WatchService watchService = FileSystems.getDefault().newWatchService();
    

    WatchSerice的使用和實現本質

    小師妹:F師兄,WatchSerice是咋實現的呀?這麼神奇,為我們省了這麼多工作。

    其實JDK提供了這麼多類的目的就是為了不讓我們重複造輪子,之前跟你講監控文件的最簡單辦法就是開一個獨立的線程來監控文件變化嗎?其實…..WatchService就是這樣做的!

    PollingWatchService() {
            // TBD: Make the number of threads configurable
            scheduledExecutor = Executors
                .newSingleThreadScheduledExecutor(new ThreadFactory() {
                     @Override
                     public Thread newThread(Runnable r) {
                         Thread t = new Thread(null, r, "FileSystemWatcher", 0, false);
                         t.setDaemon(true);
                         return t;
                     }});
        }
    

    上面的方法就是生成WatchService的方法,小師妹看到沒有,它的本質就是開啟了一個daemon的線程,用來接收監控任務。

    下面看下怎麼把一個文件註冊到WatchService上面:

    private void startWatcher(String dirPath, String file) throws IOException {
            WatchService watchService = FileSystems.getDefault().newWatchService();
            Path path = Paths.get(dirPath);
            path.register(watchService, ENTRY_MODIFY);
    
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    watchService.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }));
    
            WatchKey key = null;
            while (true) {
                try {
                    key = watchService.take();
                    for (WatchEvent<?> event : key.pollEvents()) {
                        if (event.context().toString().equals(fileName)) {
                            loadConfig(dirPath + file);
                        }
                    }
                    boolean reset = key.reset();
                    if (!reset) {
                        log.info("該文件無法重置");
                        break;
                    }
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
        }
    

    上面的關鍵方法就是path.register,其中Path是一個Watchable對象。

    然後使用watchService.take來獲取生成的WatchEvent,最後根據WatchEvent來處理文件。

    總結

    道生一,一生二,二生三,三生萬物。一個簡簡單單的功能其實背後隱藏着…道德經,哦,不對,背後隱藏着道的哲學。

    本文的例子https://github.com/ddean2009/learn-java-io-nio

    本文作者:flydean程序那些事

    本文鏈接:http://www.flydean.com/java-io-file-watchservice/

    本文來源:flydean的博客

    歡迎關注我的公眾號:程序那些事,更多精彩等着您!

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

  • 這些國產車都用寶馬發動機了!賣不好因為我們不識貨?

    這些國產車都用寶馬發動機了!賣不好因為我們不識貨?

    還是那句話,用什麼發動機並不是決定銷量好壞的首要因素。有“東瀛寶馬”之稱的馬自達,之所以被不少車迷們津津樂道,除了擁有媲美寶馬的操控,更是因為馬自達掌握不少令“黑科技”眾多的本田都羡慕的技術。創馳藍天(SKYACTIV Technology)則是其中的代表。

    叫獸常說選車主要看三大件 – 發動機、變速箱和底盤。作為汽車的“心臟”,發動機的重要性不言而喻,如今各品牌之間都會選擇共享發動機的戰略,以實現共贏。

    不少自主品牌車型搭載了其它合資品牌發動機,甚至還有用上寶馬發動機的,但大部分只是空有一顆“最強心”,銷量並沒有達到預期,一起來看看吧。

    華頌,這個非常陌生的名字,是華晨集團旗下一個新的自主品牌,其首款車型是進軍中高檔市場的MpV – 華頌7。這款車搭載的引擎相信每一位汽車愛好者都不會陌生,來自BMW非常經典的N20系列發動機,如今居然“下放”到一款國產MpV身上,的確出乎意料。

    在這裏就不過多介紹N20的技術亮點,大家只需要知道這款曾榮獲“2011年全球十佳引擎”的發動機近幾年為寶馬立下了汗馬功勞,不過從去年開始BMW的新車型開始陸續換裝全新的“B系列”發動機。

    華頌7能搭載BMW的發動機無疑是令華頌非常得意的宣傳點,但在一個全新的國產品牌面前,23.77 – 28.77萬的指導價顯然是蒼白無力的,這也註定了其銷量慘淡的命運。

    叫獸說

    既然有這樣的好背景,更應該腳踏實地從基礎做起,飯得一口一口的吃,路要一步一步的走。一個全新的國產品牌想憑一台發動機走天下未免有些天真。

    說到BMW發動機,就不得不提到由pSA集團(標緻雪鐵龍)與BMW共同研發的THp發動機。

    雪鐵龍C4L、標緻408以及MINI等車型均搭載了這款1.6THp發動機,似乎是裝上了有BMW基因的發動機令雪鐵龍很自信,宣傳C4L的時候喜歡弄“百公里加速”、“汽車拔河”這些營銷手段,眼球是賺到了,但對銷量和口碑似乎並沒有起到太大作用。

    合作都是為了實現共贏,這款發動機的確融合了兩大集團眾多技術優勢。站在市場角度,BMW減少了研發入門發動機的成本,而能攀上BMW這個“高枝兒”對於標緻雪鐵龍自然是件求之不得的事情。

    叫獸說

    豪華品牌與普通品牌之間合作本是一件互補的事情,但例如C4L,有一個勁吹噓發動機的功夫,倒不如在其它地方多花心思,以做到全面發展從而贏得銷量。還是那句話,用什麼發動機並不是決定銷量好壞的首要因素。

    有“東瀛寶馬”之稱的馬自達,之所以被不少車迷們津津樂道,除了擁有媲美寶馬的操控,更是因為馬自達掌握不少令“黑科技”眾多的本田都羡慕的技術。創馳藍天(SKYACTIV Technology)則是其中的代表。

    來自一汽的奔騰便與馬自達有着扯不斷的關係,估計你很難想到奔騰B70這款毫無特色的車居然與非常經典的馬自達6同平台打造。除了發動機之外(非創馳藍天),變速箱、底盤等等均出自馬6,可以說B70就是一款換殼換標的馬6。但事實上從行駛質感到做工品質以及油耗等方面B70並沒有表現出馬6的水準,在不少一線自主品牌對手面前,與馬6同平台打造這個“老掉牙”的賣點也顯得底氣不足。

    叫獸說

    作為自主品牌里資源最豐富的一汽,如果能少些“腐氣”,多些用心造車的士氣,未來在競爭中自然更有底氣。

    說到一汽,不免想到另一位“皇城下”的北汽。北汽紳寶高價收購了瀕臨破產的薩博的大部分技術,隨後推出了自主品牌中又一款進軍中高級車市場的紳寶D70。

    D70全系搭載的均是渦輪增壓發動機,從動力參數上看在眾多自主品牌競品里算得上是優秀水平,但隨之而來較高的油耗是網友普遍反映的問題。除此外,較高的售價、無特色的外觀和較為粗糙的做工註定了這款車的命運。

    叫獸說

    看看捷豹路虎之於奇瑞、沃爾沃之於吉利,反觀薩博之於紳寶,自主品牌在收購國外品牌之後究竟應該怎樣將價值發揮到最大是北汽需要深思並解決的問題。

    除了上述幾個品牌外,還有不少自主車型直接向國外品牌採購發動機的例子。來自瀋陽航天三菱發動機出品的“4A”系列和“4G”系列發動機普遍搭載於自主品牌車型上。

    (搭載於陸風X5上的三菱4G63S4T發動機)

    不僅有陸風、眾泰、海馬、東南等等品牌車型採購過三菱發動機外,哈弗H6是最典型也是最成功的例子。憑藉中庸耐看的外觀、合理的售價以及較可靠的品質,H6成為月銷超5萬的“神車”。為何同樣都是“三菱心”,命運卻有如此大的差別,這是其它品牌需要思考和學習的地方。

    希望中國品牌能早日研發出真正屬於自己的優秀髮動機,屆時掌核心技術后,才能挺直了腰板說一聲“Made in China”而不是“OEM in China”。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    ※別再煩惱如何寫文案,掌握八大原則!

  • 又大又硬!每個男人都想擁有的硬派SUV

    又大又硬!每個男人都想擁有的硬派SUV

    G-class擁有的超強非鋪裝行駛能力,折服了不少喜歡越野但又想買買菜的買家。前中后三把電控差速鎖,在野外擁有了全時四驅,你唯一需要擔心的就是自己的車技了。從軍車轉型至民用車,G-class一共發展了三代車型,而換代車型也即將在明年亮相。

    全車方方正正,車燈也是極其簡單的圓形,價格不是一般消費者能承擔得起的,擁有三把差速鎖,超強的越野能力,整車所帶來硬派的風格,卻是它最大的魅力。

    沒錯,要說的就是奔馳G-class。字母G本義是德語Gelndewagen的縮寫,也就是越野車的意思。從1979年誕生到現在,G-class還是一副方盒子的模樣。模樣沒變,其非承載式車身和梯形大梁的設定也沒變。

    G-class擁有的超強非鋪裝行駛能力,折服了不少喜歡越野但又想買買菜的買家。前中后三把電控差速鎖,在野外擁有了全時四驅,你唯一需要擔心的就是自己的車技了。

    從軍車轉型至民用車,G-class一共發展了三代車型,而換代車型也即將在明年亮相。沒錯,還是那個模樣,但整車車重減輕了200kg,動力系統除了六缸機外,還會提供4.0L V8雙渦輪增壓發動機。

    G-class硬派的風格着實讓不少車迷痴迷,要是能買上一輛,基本就是人生贏家的存在了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    ※回頭車貨運收費標準

  • 1.5T+CVT的SUV只賣9.78萬 它能比哈弗更值得買嗎?

    1.5T+CVT的SUV只賣9.78萬 它能比哈弗更值得買嗎?

    加上方向盤的助力輕盈,所以瑞虎5是一輛舒適好開的SUV車型,雖然它搭載功率較高的1。5T發動機,但是開起來不會讓人有太多激烈駕駛的慾望。瑞虎5油耗高嗎。1。5T手動擋車型的車主口碑油耗:8。5L/100km1。5T CVT車型的車主口碑油耗:9。

    說起奇瑞瑞虎5,大家都不會太過陌生。它外觀敦實大氣、空間夠大、定位為緊湊型SUV,而且8.88-15.19萬的售價也比較親民。

    2017款的瑞虎5全系採用了一款代號為SQRE4T15的1.5T渦輪增壓發動機,最大功率152馬力,最大扭矩205牛米/2000-4000轉。

    這樣的發動機功率表現比較不錯,而採用這款1.5T發動機配合CVT變速箱實際開起來的表現怎樣?

    駕駛體驗如何?

    進入到車內,較高的坐姿,讓它的視野廣闊,方便察看前方的車流動向。

    瑞虎5搭載的CVT變速箱帶有模擬七速的功能,並且提供ECO/SpORT兩種駕駛模式。這兩種駕駛模式差異明顯,在SpORT模式下發動機轉速會明顯提高來提升動力輸出。

    在實際開起來的時候,瑞虎5的油門響應比較沉穩,踩下油門時,發動機的轉速會平穩地攀升,加速過程缺少一些激情,開起來有條不紊,但動力還是屬於“夠用”的類型。

    底盤採用前麥弗遜式獨立懸架、后雙連桿獨立懸架的結構,它的調校以舒適性為主,所以路面上的大小震動都被過濾得比較徹底。

    加上方向盤的助力輕盈,所以瑞虎5是一輛舒適好開的SUV車型,雖然它搭載功率較高的1.5T發動機,但是開起來不會讓人有太多激烈駕駛的慾望。

    瑞虎5油耗高嗎?

    1.5T手動擋車型的車主口碑油耗:8.5L/100km

    1.5T CVT車型的車主口碑油耗:9.3L/100km

    瑞虎5的車重在1.5噸左右,並且是一輛緊湊型SUV,所以這樣的油耗表現屬於比較合理的。

    競爭對手

    長城汽車-哈弗H2s

    指導價:8.38-10.28萬

    哈弗H2S和瑞虎5的售價區間部分重疊,雖然它們定位有些差別,但是仍然形成了激烈的競爭關係。而哈弗H2S採用的是7擋濕式雙離合變速箱,升擋、降擋都比較利索,所以動力響應性更好。

    總結:

    瑞虎5是一輛平順舒適的SUV車型,它的空間和舒適性都比較到位,如果你正考慮買一輛10萬左右的SUV,那麼它應該是一輛表現均衡的選擇之一。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    ※教你寫出一流的銷售文案?

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

  • 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(五)

    基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(五)

    系列文章

    1. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用 abp cli 搭建項目
    2. 基於 abp vNext 和 .NET Core 開發博客項目 – 給項目瘦身,讓它跑起來
    3. 基於 abp vNext 和 .NET Core 開發博客項目 – 完善與美化,Swagger登場
    4. 基於 abp vNext 和 .NET Core 開發博客項目 – 數據訪問和代碼優先
    5. 基於 abp vNext 和 .NET Core 開發博客項目 – 自定義倉儲之增刪改查
    6. 基於 abp vNext 和 .NET Core 開發博客項目 – 統一規範API,包裝返回模型
    7. 基於 abp vNext 和 .NET Core 開發博客項目 – 再說Swagger,分組、描述、小綠鎖
    8. 基於 abp vNext 和 .NET Core 開發博客項目 – 接入GitHub,用JWT保護你的API
    9. 基於 abp vNext 和 .NET Core 開發博客項目 – 異常處理和日誌記錄
    10. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用Redis緩存數據
    11. 基於 abp vNext 和 .NET Core 開發博客項目 – 集成Hangfire實現定時任務處理
    12. 基於 abp vNext 和 .NET Core 開發博客項目 – 用AutoMapper搞定對象映射
    13. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(一)
    14. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(二)
    15. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(三)
    16. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(一)
    17. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(二)
    18. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(三)
    19. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(四)

    上篇文章完成了文章詳情頁數據查詢和清除緩存的功能。

    本篇繼續完成分類、標籤、友情鏈接的後台操作接口,還是那句話,這些純CRUD的內容,建議還是自己動手完成比較好,本篇將不再啰嗦,直接貼代碼,以供參考。

    分類

    添加接口:查詢分類列表QueryCategoriesForAdminAsync()、新增分類InsertCategoryAsync(...)、更新分類UpdateCategoryAsync(...)、刪除分類DeleteCategoryAsync(...)

    #region Categories
    
    /// <summary>
    /// 查詢分類列表
    /// </summary>
    /// <returns></returns>
    Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync();
    
    /// <summary>
    /// 新增分類
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> InsertCategoryAsync(EditCategoryInput input);
    
    /// <summary>
    /// 更新分類
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> UpdateCategoryAsync(int id, EditCategoryInput input);
    
    /// <summary>
    /// 刪除分類
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<ServiceResult> DeleteCategoryAsync(int id);
    
    #endregion Categories
    

    查詢分類列表需要返回的模型類QueryCategoryForAdminDto.cs

    //QueryCategoryForAdminDto.cs
    namespace Meowv.Blog.Application.Contracts.Blog
    {
        public class QueryCategoryForAdminDto : QueryCategoryDto
        {
            /// <summary>
            /// 主鍵
            /// </summary>
            public int Id { get; set; }
        }
    }
    

    新增分類和更新分類需要的輸入參數EditCategoryInput.cs,直接繼承CategoryDto即可。

    //EditCategoryInput.cs
    namespace Meowv.Blog.Application.Contracts.Blog.Params
    {
        public class EditCategoryInput : CategoryDto
        {
        }
    }
    

    分別實現這幾個接口。

    /// <summary>
    /// 查詢分類列表
    /// </summary>
    /// <returns></returns>
    public async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync()
    {
        var result = new ServiceResult<IEnumerable<QueryCategoryForAdminDto>>();
    
        var posts = await _postRepository.GetListAsync();
    
        var categories = _categoryRepository.GetListAsync().Result.Select(x => new QueryCategoryForAdminDto
        {
            Id = x.Id,
            CategoryName = x.CategoryName,
            DisplayName = x.DisplayName,
            Count = posts.Count(p => p.CategoryId == x.Id)
        });
    
        result.IsSuccess(categories);
        return result;
    }
    
    /// <summary>
    /// 新增分類
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<ServiceResult> InsertCategoryAsync(EditCategoryInput input)
    {
        var result = new ServiceResult();
    
        var category = ObjectMapper.Map<EditCategoryInput, Category>(input);
        await _categoryRepository.InsertAsync(category);
    
        result.IsSuccess(ResponseText.INSERT_SUCCESS);
        return result;
    }
    

    這裏需要一條AutoMapper配置,將EditCategoryInput轉換為Category,忽略Id字段。

    CreateMap<EditCategoryInput, Category>().ForMember(x => x.Id, opt => opt.Ignore());
    
    /// <summary>
    /// 更新分類
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<ServiceResult> UpdateCategoryAsync(int id, EditCategoryInput input)
    {
        var result = new ServiceResult();
    
        var category = await _categoryRepository.GetAsync(id);
        category.CategoryName = input.CategoryName;
        category.DisplayName = input.DisplayName;
    
        await _categoryRepository.UpdateAsync(category);
    
        result.IsSuccess(ResponseText.UPDATE_SUCCESS);
        return result;
    }
    
    /// <summary>
    /// 刪除分類
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<ServiceResult> DeleteCategoryAsync(int id)
    {
        var result = new ServiceResult();
    
        var category = await _categoryRepository.FindAsync(id);
        if (null == category)
        {
            result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
            return result;
        }
    
        await _categoryRepository.DeleteAsync(id);
    
        result.IsSuccess(ResponseText.DELETE_SUCCESS);
        return result;
    }
    

    BlogController.Admin.cs中添加接口。

    #region Categories
    
    /// <summary>
    /// 查詢分類列表
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [Authorize]
    [Route("admin/categories")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync()
    {
        return await _blogService.QueryCategoriesForAdminAsync();
    }
    
    /// <summary>
    /// 新增分類
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [Authorize]
    [Route("category")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> InsertCategoryAsync([FromBody] EditCategoryInput input)
    {
        return await _blogService.InsertCategoryAsync(input);
    }
    
    /// <summary>
    /// 更新分類
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPut]
    [Authorize]
    [Route("category")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> UpdateCategoryAsync([Required] int id, [FromBody] EditCategoryInput input)
    {
        return await _blogService.UpdateCategoryAsync(id, input);
    }
    
    /// <summary>
    /// 刪除分類
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete]
    [Authorize]
    [Route("category")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> DeleteCategoryAsync([Required] int id)
    {
        return await _blogService.DeleteCategoryAsync(id);
    }
    
    #endregion Categories
    

    標籤

    添加接口:查詢標籤列表QueryTagsForAdminAsync()、新增標籤InsertTagAsync(...)、更新標籤UpdateTagAsync(...)、刪除標籤DeleteTagAsync(...)

    #region Tags
    
    /// <summary>
    /// 查詢標籤列表
    /// </summary>
    /// <returns></returns>
    Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync();
    
    /// <summary>
    /// 新增標籤
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> InsertTagAsync(EditTagInput input);
    
    /// <summary>
    /// 更新標籤
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> UpdateTagAsync(int id, EditTagInput input);
    
    /// <summary>
    /// 刪除標籤
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<ServiceResult> DeleteTagAsync(int id);
    
    #endregion Tags
    

    查詢標籤列表需要返回的模型類QueryTagForAdminDto.cs

    //QueryTagForAdminDto.cs
    namespace Meowv.Blog.Application.Contracts.Blog
    {
        public class QueryTagForAdminDto : QueryTagDto
        {
            /// <summary>
            /// 主鍵
            /// </summary>
            public int Id { get; set; }
        }
    }
    

    新增標籤和更新標籤需要的輸入參數EditTagInput.cs,直接繼承TagDto即可。

    //EditTagInput.cs
    namespace Meowv.Blog.Application.Contracts.Blog.Params
    {
        public class EditTagInput : TagDto
        {
        }
    }
    

    分別實現這幾個接口。

    /// <summary>
    /// 查詢標籤列表
    /// </summary>
    /// <returns></returns>
    public async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync()
    {
        var result = new ServiceResult<IEnumerable<QueryTagForAdminDto>>();
    
        var post_tags = await _postTagRepository.GetListAsync();
    
        var tags = _tagRepository.GetListAsync().Result.Select(x => new QueryTagForAdminDto
        {
            Id = x.Id,
            TagName = x.TagName,
            DisplayName = x.DisplayName,
            Count = post_tags.Count(p => p.TagId == x.Id)
        });
    
        result.IsSuccess(tags);
        return result;
    }
    
    /// <summary>
    /// 新增標籤
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    public async Task<ServiceResult> InsertTagAsync(EditTagInput input)
    {
        var result = new ServiceResult();
    
        var tag = ObjectMapper.Map<EditTagInput, Tag>(input);
        await _tagRepository.InsertAsync(tag);
    
        result.IsSuccess(ResponseText.INSERT_SUCCESS);
        return result;
    }
    

    這裏需要一條AutoMapper配置,將EditCategoryInput轉換為Tag,忽略Id字段。

    CreateMap<EditTagInput, Tag>().ForMember(x => x.Id, opt => opt.Ignore());
    
    /// <summary>
    /// 更新標籤
    /// </summary>
    /// <param name="id"></param>
    /// <param name="dto"></param>
    /// <returns></returns>
    public async Task<ServiceResult> UpdateTagAsync(int id, EditTagInput input)
    {
        var result = new ServiceResult();
    
        var tag = await _tagRepository.GetAsync(id);
        tag.TagName = input.TagName;
        tag.DisplayName = input.DisplayName;
    
        await _tagRepository.UpdateAsync(tag);
    
        result.IsSuccess(ResponseText.UPDATE_SUCCESS);
        return result;
    }
    
    /// <summary>
    /// 刪除標籤
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<ServiceResult> DeleteTagAsync(int id)
    {
        var result = new ServiceResult();
    
        var tag = await _tagRepository.FindAsync(id);
        if (null == tag)
        {
            result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
            return result;
        }
    
        await _tagRepository.DeleteAsync(id);
        await _postTagRepository.DeleteAsync(x => x.TagId == id);
    
        result.IsSuccess(ResponseText.DELETE_SUCCESS);
        return result;
    }
    

    BlogController.Admin.cs中添加接口。

    #region Tags
    
    /// <summary>
    /// 查詢標籤列表
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [Authorize]
    [Route("admin/tags")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync()
    {
        return await _blogService.QueryTagsForAdminAsync();
    }
    
    /// <summary>
    /// 新增標籤
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [Authorize]
    [Route("tag")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> InsertTagAsync([FromBody] EditTagInput input)
    {
        return await _blogService.InsertTagAsync(input);
    }
    
    /// <summary>
    /// 更新標籤
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPut]
    [Authorize]
    [Route("tag")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> UpdateTagAsync([Required] int id, [FromBody] EditTagInput input)
    {
        return await _blogService.UpdateTagAsync(id, input);
    }
    
    /// <summary>
    /// 刪除標籤
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete]
    [Authorize]
    [Route("tag")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> DeleteTagAsync([Required] int id)
    {
        return await _blogService.DeleteTagAsync(id);
    }
    
    #endregion Tags
    

    友鏈

    添加接口:查詢友鏈列表QueryFriendLinksForAdminAsync()、新增友鏈InsertFriendLinkAsync(...)、更新友鏈UpdateFriendLinkAsync(...)、刪除友鏈DeleteFriendLinkAsync(...)

    #region FriendLinks
    
    /// <summary>
    /// 查詢友鏈列表
    /// </summary>
    /// <returns></returns>
    Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync();
    
    /// <summary>
    /// 新增友鏈
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> InsertFriendLinkAsync(EditFriendLinkInput input);
    
    /// <summary>
    /// 更新友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    Task<ServiceResult> UpdateFriendLinkAsync(int id, EditFriendLinkInput input);
    
    /// <summary>
    /// 刪除友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<ServiceResult> DeleteFriendLinkAsync(int id);
    
    #endregion FriendLinks
    

    查詢友鏈列表需要返回的模型類QueryFriendLinkForAdminDto.cs

    //QueryFriendLinkForAdminDto.cs
    namespace Meowv.Blog.Application.Contracts.Blog
    {
        public class QueryFriendLinkForAdminDto : FriendLinkDto
        {
            /// <summary>
            /// 主鍵
            /// </summary>
            public int Id { get; set; }
        }
    }
    

    新增友鏈和更新友鏈需要的輸入參數EditFriendLinkInput.cs,直接繼承FriendLinkDto即可。

    //EditFriendLinkInput .cs
    namespace Meowv.Blog.Application.Contracts.Blog.Params
    {
        public class EditFriendLinkInput : FriendLinkDto
        {
        }
    }
    

    分別實現這幾個接口。

    /// <summary>
    /// 查詢友鏈列表
    /// </summary>
    /// <returns></returns>
    public async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync()
    {
        var result = new ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>();
    
        var friendLinks = await _friendLinksRepository.GetListAsync();
    
        var dto = ObjectMapper.Map<List<FriendLink>, IEnumerable<QueryFriendLinkForAdminDto>>(friendLinks);
    
        result.IsSuccess(dto);
        return result;
    }
    
    /// <summary>
    /// 新增友鏈
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<ServiceResult> InsertFriendLinkAsync(EditFriendLinkInput input)
    {
        var result = new ServiceResult();
    
        var friendLink = ObjectMapper.Map<EditFriendLinkInput, FriendLink>(input);
        await _friendLinksRepository.InsertAsync(friendLink);
    
        // 執行清除緩存操作
        await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);
    
        result.IsSuccess(ResponseText.INSERT_SUCCESS);
        return result;
    }
    
    /// <summary>
    /// 更新友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<ServiceResult> UpdateFriendLinkAsync(int id, EditFriendLinkInput input)
    {
        var result = new ServiceResult();
    
        var friendLink = await _friendLinksRepository.GetAsync(id);
        friendLink.Title = input.Title;
        friendLink.LinkUrl = input.LinkUrl;
    
        await _friendLinksRepository.UpdateAsync(friendLink);
    
        // 執行清除緩存操作
        await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);
    
        result.IsSuccess(ResponseText.UPDATE_SUCCESS);
        return result;
    }
    
    /// <summary>
    /// 刪除友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<ServiceResult> DeleteFriendLinkAsync(int id)
    {
        var result = new ServiceResult();
    
        var friendLink = await _friendLinksRepository.FindAsync(id);
        if (null == friendLink)
        {
            result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
            return result;
        }
    
        await _friendLinksRepository.DeleteAsync(id);
    
        // 執行清除緩存操作
        await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);
    
        result.IsSuccess(ResponseText.DELETE_SUCCESS);
        return result;
    }
    

    其中查詢友鏈列表和新增友鏈中有兩條AutoMapper配置。

    CreateMap<FriendLink, QueryFriendLinkForAdminDto>();
    
    CreateMap<EditFriendLinkInput, FriendLink>().ForMember(x => x.Id, opt => opt.Ignore());
    

    BlogController.Admin.cs中添加接口。

    #region FriendLinks
    
    /// <summary>
    /// 查詢友鏈列表
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [Authorize]
    [Route("admin/friendlinks")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync()
    {
        return await _blogService.QueryFriendLinksForAdminAsync();
    }
    
    /// <summary>
    /// 新增友鏈
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [Authorize]
    [Route("friendlink")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> InsertFriendLinkAsync([FromBody] EditFriendLinkInput input)
    {
        return await _blogService.InsertFriendLinkAsync(input);
    }
    
    /// <summary>
    /// 更新友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPut]
    [Authorize]
    [Route("friendlink")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> UpdateFriendLinkAsync([Required] int id, [FromBody] EditFriendLinkInput input)
    {
        return await _blogService.UpdateFriendLinkAsync(id, input);
    }
    
    /// <summary>
    /// 刪除友鏈
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete]
    [Authorize]
    [Route("friendlink")]
    [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
    public async Task<ServiceResult> DeleteFriendLinkAsync([Required] int id)
    {
        return await _blogService.DeleteFriendLinkAsync(id);
    }
    
    #endregion
    

    Next

    截止本篇,基於 abp vNext 和 .NET Core 開發博客項目 系列的後台API部分便全部開發完成了。

    本博客項目系列是我一邊寫代碼一邊記錄后的成果,並不是開發完成后再拿出來寫的,涉及到東西也不是很多,對於新手入門來說應該是夠了的,如果你從中有所收穫請多多轉發分享。

    在此,希望大家可以關注一下我的微信公眾號:『阿星Plus』,文章將會首發在公眾號中。

    現在有了API,大家可以選擇自己熟悉的方式去開發前端界面,比如目前我博客的線上版本就是用的 ASP.NET Core Web ,感興趣的可以去 release 分支查看。

    關於前端部分,看到有人呼籲vue,說實話前端技術不是很厲害,本職主要是後端開發,可能達不到預期效果。

    所以我準備入坑 Blazor ,接下來就現學現賣吧,一起學習一起做項目一起進步,加油

    開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

    搭配下方課程學習更佳 ↓ ↓ ↓

    http://gk.link/a/10iQ7

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計最專業,超強功能平台可客製化

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※回頭車貨運收費標準

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家公司費用怎麼算?

  • WinUI 3 試玩報告

    WinUI 3 試玩報告

    1. 什麼是 WinUI 3

    在微軟 Build 2020 開發者大會上,WinUI 團隊宣布可公開預覽的 WinUI 3 Preview 1,它讓開發人員可以在 Win32 中使用 WinUI。WinUI 3 Preview 1 包含新的 VisualStudio 項目模板,可以創建面向 .NET 5 的 C# 和 C++/Win32 項目。從技術上講,WinUI 3 將 UWP 的 XAML、Composition 和 Input 層分離,並通過NuGet將它們獨立分發給針對Windows 10 版本 1803 及更高版本的 Win32 應用。

    WinUI 3 適用於 Win32 和 UWP,這篇文章主要討論 Win32 的情況。

    2. 理解 WinUI 3

    以前我們總是抱怨 WPF 多年都不提供新的主題,不提供新的控件,性能又沒提升。現在微軟索性把什麼都是新的 WinUI 3 提供給桌面開發,沒 WPF 什麼事了。

    簡單來說,UWP 的開發體驗不好(關於這個話題真是一言難盡),而且出了 Bug 還必須等待下半年的 Windows 更新進行修復,但微軟的開發人員專心給 UWP 的 UI 層加各種功能;.NET Core 更新很快,但很少人有興趣有動力給陳舊的 WPF 的 UI 層進行大幅度的改進。於是 WinUI 將 UWP 的 UI 層從 Windows SDK 的其它部分分離,並將從 Windows 轉移到 Nuget。現在建一個 C++ 或 C#(.NET 5) 程序,再從 Nuget 上裝個 WinUI 3 的包套個 UI 層,一個基於 Fluent Design,觸摸友好,性能無與倫比的應用程序就誕生了。

    上圖列舉了 WinUI 3 和其他平台對比的部分特性,除此之外 WinUI 3 還有很多好處,例如開源、更新更快、更新不與系統版本綁定等,更詳細的內容還是看微軟自己怎麼宣傳吧:

    WinUI – The modern native UI platform of Windows.

    不過要用上 WinUI 3 還要等一年半載。下面是微軟給出的發布路線圖,目前我們也只能用 Preview 版嘗嘗鮮。

    3. 試玩WinUI 3

    要試玩 WinUI 3 首先要有 Windows 10 1803 以上版本的電腦(WinUI 3 最低支持1803),然後還需要使用 Visual Studio 2019 16.7 以上版本(目前只能安裝預覽版)。安裝 Visual Studio 時要把以下工作負載全都選上:

    • .NET 桌面開發
    • 通用 Windows 平台開發
    • 使用 C++ 的桌面開發
    • 適用於通用 Windows 平台負載的 C++(V142) 通用 Windows 平台工具可選組件

    當然 .NET 5.0 也要裝上。

    然後在 https://aka.ms/winui3/previewdownload 下載並安裝 WinUI 3 Project Templates 擴展,這樣才可以在 Visual Studio 創建 WinUI 的項目。

    可選 C++ 或 C# ,這裏我選擇了 C# 的“Blank App, Packaged
    (WinUI in Desktop)”項目,並選擇了對應的 Windows 平台:

    項目創建后 Visual Studio 生成了兩個項目。第一個包含應用的代碼,代碼結構基本和 UWP 一樣,只是少了用於打包應用的 Package.appxmanifest 和一些圖片。從依賴項里可以看到項目已經安裝了 Microsoft.WinUI 3 的包。從項目屬性里可以看到這就是個 .NET 5 的項目。

    Visual Studio 生成的第二個項目是一個 Windows 應用程序打包項目,該項目經配置后可將應用生成為適合部署的 MSIX 程序包。 也就是說 UWP 項目中用於打包的部分被獨立出來了。這個項目還應該是解決方案的啟動項目。運行這個項目后創建的應用會添加到開始菜單中,這點也和UWP一樣。

    到這裏為止都和預期的一樣,我之後還嘗試了將 UWP 應用移植到 WinUI ,基本上只需要將 Windows.UI 命名空間改為 Microsoft.UI就可以了,XAML 和 C# 代碼完全不用變。只可惜目前 WinUI 還很簡陋,Win2D、Community Toolkit 等微軟自己發布的 UWP 包都還沒有 WinUI 版本。而且沒有設計視圖,XAML 視圖也沒有智能感知,現在想要用 WinUI做些什麼有趣的項目會很困難。不過從目前的移植難度上來看,將來正式發布后應該可以完整地將 UWP 的 UI 的開發經驗運用在 WinUI 上。

    4. 和 WPF 及 UWP 進行對比

    既然 WinUI 3 開發模式和 WPF 及 UWP 都很像,我當然對它們之間的對比很感興趣。

    命名

    首先說說命名,“WinUI” 光這個名字就 Win 了。 “UWP” 太高雅,我敢打賭國內有些 UWP 的開發(例如我)都不能好好地把 UWP 的全稱拼出來;“WPF” 好些,但 WPF 的含義也讓人很疑惑。而 Windows UI 簡稱 WinUI ,意義和發音都很清晰明確。不過這三個都比很多人都不會讀的 “Xamarin” 強多了。

    可是有了 WinUI 3 ,就會有人問“那 WinUI 2 呢?”WinUI 2是一個 UWP 的控件庫,當然的只能用在 UWP 上。這就很尷尬了,WinUI 的 3 和 2 根本不是同一個概念,實在很容易讓人混淆,說不定以後會把後綴的 3 去掉(我這篇文章就常常懶得理寫這個3)。而且 UWP 中代碼的命名空間以 Windows.UI 開頭,在 WinUI 3 中則 Microsoft.UI ,按着 Office 365 改名為 Microsoft 365、Bind Ads 改名為 Microsoft Advertising 這些經驗,該不會以後 WinUI 可能改名為 Microsoft UI ,簡稱 MiUI 吧?

    權限

    權限方面是 WinUI 的一個亮點,因為它本質上就是個 Win32 程序,可以放開手腳隨便來。相對的 UWP 有很嚴格的權限限制,開發 UWP 時常常會感到綁手綁腳。例如下面這段代碼,大部分 WPF 開發者都難以想象只是最小化 UWP 程序而已,它就不能好好運行了:

    int count = 0;
    DispatcherTimer timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromSeconds(1);
    timer.Tick += (s, e) =>
      {
          myButton.Content = count++;
      };
    timer.Start();
    

    UWP 的生命周期如上圖,當 UWP 處於 background 運行或 suspended 狀態時應用基本處於暫停狀態,也也不會處理UI功能。我明白這是 UWP 為了省電、安全等原因才這樣設計,但對開發人員來說真的太不方便。而 WinUI 應用基本上就是個 Win32 應用,目前看來不會有這些坑。

    開發體驗

    說起開發體驗,WPF 好歹還算正常,Visual Studio 的設計視圖運行正常,編譯起來也快。UWP 編譯很慢,設計視圖經常出問題,Blend 也時好時壞把設計師都氣跑了。就算完全按着官方的文檔完成一個 UWP App,甚至一行代碼都不改,發布到商店后還是有可能崩潰。而對於應用商店,真是千言萬語彙聚成一個草花頭。

    現在 WinUI 的 XAML 視圖連智能感知都沒有,也沒有設計視圖,實在沒法談開發體驗。很難猜測正式發布的時候會怎麼樣,希望至少和WPF保持一致吧。

    性能

    WPF 總是給人“慢”的印象,除了因為在它剛出來的時候(10年前)電腦性能不夠導致留下了刻板印象,還有一個主要原因是:它真的很慢。

    UWP 的 XAML 有很優秀的性能表現,除此之外為了照顧已經不存在的 Windows Phone 的貧弱性能,很多控件模版都經過精心設計並大幅簡化。

    為了驗證 WinUI 的性能我寫了下面這些代碼,然後分別移植到 WPF .Net Framework 4.8、WPF .NET 5、UWP、WinUI(WPF 和 UWP/WinUI 的代碼稍微有一點不同):

    for (int i = 0; i < 50; i++)
    {
        var rectangle = new Rectangle
        {
            Height = 500,
            Width = 500,
            Opacity = ((double)i + 40) / 100d,
            RadiusX = 108,
            RadiusY = 98,
            StrokeThickness = 3,
            Stroke = new SolidColorBrush(Color.FromArgb(255, 75, 75, (byte)(i * 250d / 50d))),
            RenderTransformOrigin = new Point(0.5, 0.5)
        };
        Root.Children.Add(rectangle);
        var angle = i * 360d / 50d;
        var transform = new RotateTransform
        {
            Angle = angle
        };
    
        rectangle.RenderTransform = transform;
    
        var storyboard = new Storyboard();
        storyboard.Children.Add(new DoubleAnimation { Duration = TimeSpan.FromSeconds(1), From = angle, To = angle + 360 });
        Storyboard.SetTarget(storyboard, transform);
        Storyboard.SetTargetProperty(storyboard, "Angle");
        storyboard.RepeatBehavior = RepeatBehavior.Forever;
        storyboard.Begin();
    }
    

    上面這段代碼是讓50個矩形旋轉,十分考驗 WPF 的性能。結果可以說出乎意料。

    CPU 內存 GPU
    WPF .NET Framework 4.8 12 60 76
    WPF .NET 5.0 12 85 72
    UWP 3 28 36
    WinUI 5 65 95

    我的環境是 i7-6820HQ 及集成顯卡。WPF 平台佔用 70 多%的 GPU,這我大致能猜到。UWP 十分流暢,GPU 只佔用 WPF 的一半,CPU 和 內存都有出色表現,不過我還以為會更低的。

    WinUI 這個濃眉大眼的我真的萬萬沒想到,不僅掉幀明顯,還佔用了幾乎 100% GPU,也就是說它連這麼簡單的代碼都跑不起來。()順便一提,將測試代碼中旋轉的矩形減少為10個,WPF 的程序佔用 32% GPU,而 WinUI 佔用 70 多%。)

    從上面的數據基本可以說明,WinUI 離設計目標還十分遙遠,畢竟是預覽版,還有一年半載可以慢慢優化。

    5. 結語

    總的來說微軟雄心勃勃,可是現在拿出來的 WinUI 預覽版還差得太遠,功能未完善,性能不及預期。我覺得大致方向沒錯,WinUI 對 C++、WPF、UWP 開發者都是個新的工具新的機遇,可以關注一下。

    6. Q & A

    Windows 7 怎麼辦?

    按微軟公布的路線圖,再包括跳票等因素,等 WinUI 真正可用時 Windows 7 已停止更新很久,到時 Windows 7 的佔有率可能已經下降到開發者不會關心的程度。

    基於 .NET Core 的 Wpf 還是 WinUI?

    假使不想花精力將現有項目遷移到 WinUI,或者對來自 UWP 的 WinUI 沒信心,又或者舍不得 Windows 7 的用戶,並且對觸摸沒需求,當然可以繼續選用 WPF,基於 .NET Core 的 WPF 會是個很好的選擇。

    MAUI 還是 WinUI ?

    MAUI 還在很遙遠的將來(2021年11月),我沒試玩過,所以不好評價。如果有跨平台需求當然只能選 MAUI,如果 WinUI 團隊技高一籌實現了 MAUI 難以企及的超高性能,那就選 WinUI。不過 MAUI 這個名字太過普通/普遍,可能會被逼着改名吧。

    那 UWP 呢?

    權限受限的 UWP 可以說是人畜無害,對用戶來說可能也是個不錯的選擇。而且 UWP 還支持 Xbox 和 Hololens 等平台,目前看來還是有它的市場。

    Winforms 呢?

    人只有忘卻了過去,才能好好活着。

    WinUI 有未來嗎?

    我做了好多年 Silverlight 開發,買了5、6部 Windows Phone 手機,寫了幾十篇 UWP 文章,根據我豐富的經驗,我可以肯定 WinUI 是有未來的。

    8. 參考

    WinUI – The modern native UI platform of Windows.

    Introducing WinUI 3 Preview 1 – Windows Developer Blog

    Get started with WinUI 3 for desktop apps Microsoft Docs

    GitHub – microsoft_microsoft-ui-xaml

    Windows UI Library Roadmap

    WinUI 3.0_ The future of Windows controls

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※推薦評價好的iphone維修中心

    ※教你寫出一流的銷售文案?

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

  • 發動機不磨合?機油燒死你!

    還說買了性能車,直接來一發暴力磨合。然而事實是,出廠前就磨合好的車只是佔少數,同時暴力磨合也是一種不合理的磨合方式。所以,作為普通車主,還是不要搞什麼幺蛾子了。另外,磨合的時候多跑高速也是有特定條件的。

    大家常聽老司機說新車要磨合,有所謂的磨合期,那麼磨合到底是什麼鬼?

    發動機是磨合過程中最核心的部分,為了讓內部的活塞和發動機缸體達到完美的活塞運動,這時候就要在它們之間放置活塞環和缸套。

    而磨合的作用,是要讓活塞環與缸套的內壁完美地磨合密封。

    但是因為活塞環有彈性,如果金屬內壁不夠平滑,那就很容易出現各種磨損,最明顯的一個傷害就是燒機油。所以,所謂磨合期,就是要慢慢的讓發動機進行活塞運動,然後讓零件間的接合面打磨得更加貼合,達到最佳狀態。

    不過長期以來,各種鍵盤車神和老司機間都流傳着一些以訛傳訛的說法,比如說新車早就在廠里磨合好,買回來就儘管飆。還說買了性能車,直接來一發暴力磨合。

    然而事實是,出廠前就磨合好的車只是佔少數,同時暴力磨合也是一種不合理的磨合方式。所以,作為普通車主,還是不要搞什麼幺蛾子了。

    另外,磨合的時候多跑高速也是有特定條件的。在跑的過程中多切換不同的擋位和油門深淺,同是不要貪新鮮開個幾百米就算,這樣各類油溫沒起來,會對車輛造成一定損害。

    不過說了那麼多,千言萬語合萬一句,就是要多看看車主手冊。看完這期節目,大家記得要等着看下一期,什麼是燒機油!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    網頁設計最專業,超強功能平台可客製化

    ※別再煩惱如何寫文案,掌握八大原則!

  • 12萬就能買德國旅行車 試駕大眾蔚領

    12萬就能買德國旅行車 試駕大眾蔚領

    這時你可以選擇切到S擋,這時你就可以激活一下蔚領的潛在運動基因,讓它活潑一些,也讓自己小小放肆一回。而蔚領的懸挂行程得到了增加,正常行駛沒有什麼問題,遇上爛路的話你只能包容一下了。作為一輛跨界屬性的車型,沒有一個體面的後備廂,是沒有說服力的。

    六年時間,無論是對於人還是事,都是一段不短的時間了。如果說一輛車花了六年時間來研發,那它推出市場后的反響,直接就能定義它是否成功了。這對於一汽大眾蔚領來說,它面對的處境是很相似的。

    近期一汽大眾的大新聞莫過於和奧迪間的瓜葛,但回到產品上來說,新上市的蔚領也是吸引了不少眼球。蔚領的定位其實有點模糊,長着旅行車的身版,標配沒卵用的行李架,又有摻了點跨界風。而它的外觀最大的亮點,我覺得應該是在於它的尾燈造型了。但是相對於現在套娃風來說,還算有點誠意了。

    EA211 1.4T發動機+DQ2007速乾式雙離合變速箱,這套動力單元的搭載給人的感覺就和豆漿油條一樣,基於pQ34平台,一切的配方都是那麼熟悉。

    所以蔚領一上手,你還是會不由自主的覺得,這是一輛大眾。D擋模式下,你要車子很快的走起來,那就得來多點油,雖然扭矩有小幅提升,但低扭輸出還是差不多。這時你可以選擇切到S擋,這時你就可以激活一下蔚領的潛在運動基因,讓它活潑一些,也讓自己小小放肆一回。

    蔚領的內飾個人而言還是可以接受的,雖然都是一片硬塑料,拋開質感而言,說得過去。

    蔚領提供了1.4T和1.6L兩款發動機型號可選,目前的售價區間為12.59-16.29萬元。能接受兩廂車造型,憧憬一家大小在周末出外休閑的朋友,現在就多了一個選擇了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    ※教你寫出一流的銷售文案?

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

  • 12萬左右到底是買SUV還是轎車比較好?

    12萬左右到底是買SUV還是轎車比較好?

    誠然,1。6L發動機在扭矩輸出方面弱於1。2T,但是居家過日子,還是1。6L會更讓人省心。1。6L+CVT的這套動力組合,雖然給不了你太多激情,但是輸出平順線性,易於操控,質量可靠,就像春雨一樣,潤物細無聲。得益於較高的車身,即便精英版配有天窗,頭部空間依然很充裕。

    前言

    12萬左右買SUV還是轎車,估計這是許多人糾結的問題。但實際上問題其實很簡單,為何這麼說?下面聽我娓娓道來。

    首先這個價位,能挑選的主流合資小型SUV是幾乎沒有的。剩下一些非主流的貨,例如創酷與2008,這兩車是比小型SUV還要小一點點的車,因此我也不作推薦。

    在12萬內想買到SUV就唯有投身自主品牌的懷抱,但是買轎車卻有許多花樣選擇。所以我就推薦幾款自主品牌的SUV與合資家轎來給各位參考一下。

    廣汽乘用車-傳祺GS4

    2017款 200T G-DCT兩驅舒適版

    指導價:11.58萬

    編者點評:

    GS4的雙離合變速箱算是自主品牌當中匹配較為完善的一個,無論是高速巡航,還是低速跟車都表現得相當出色,不會展露出明顯的頓挫。同時,由於雙離合變速箱的傳動效率較高,其百公里綜合油耗在7.8L左右,在同級別中難遇敵手。

    底盤也是GS4一個最大的亮點,當車子碾過不平的路面時,懸架總能很好地吸收震動,給人滿滿的厚實感。更難能可貴的是,遇到大坑窪時,GS4依舊可以不露餡,不會讓乘客有種懸架極限已到的感覺。

    廣汽豐田-雷凌

    2016款 1.6G CVT精英版

    指導價:12.48萬

    編者點評:

    自從雷凌出了1.2T發動機以後,1.6L的日子恐怕即將到頭,所以現在終端也會有1萬塊以上的優惠,絕對是抄底的好時機。停產不代表1.6L這款發動機不行,相反地,這款發動機是日本原裝進口,各種配合精度都會優於國產。誠然,1.6L發動機在扭矩輸出方面弱於1.2T,但是居家過日子,還是1.6L會更讓人省心。

    1.6L+CVT的這套動力組合,雖然給不了你太多激情,但是輸出平順線性,易於操控,質量可靠,就像春雨一樣,潤物細無聲。得益於較高的車身,即便精英版配有天窗,頭部空間依然很充裕。同時,後排地台全平還配有中央頭枕,坐在中間的乘客也不會太難受,這點想得很周到。

    一汽-大眾-寶來

    2016款 1.6L 自動時尚型

    指導價:11.98萬

    編者點評:

    寶來剛出的時候,許多人都針對其拉皮車的身份責罵一番。但是,是否是舊平台其實並不十分重要,關鍵還是要看實際的產品力。從這方面來說,寶來的表現還是不錯的。1.6L+6擋手自一體的動力組合,動力還算輕快,變速箱的邏輯清晰,換擋平順,高速巡航時也有很好的油耗表現。

    懸挂濾震有韌性,行駛質感有所提升,座椅也變寬大,坐到裏面感覺更舒適。缺點也是有一點點,中控台並沒有重新設計,顯得有點老氣,整車的做工精細度不足,一些接縫處的間隙較大。

    編者總結:

    雖然SUV現在銷售火爆,但是同品牌下的SUV定價多數都高轎車那麼一些。12萬這個價錢左右想買車,如果不是對SUV有剛性需求的, 還是建議買合資家轎。現在的國產SUV儘管發展得紅紅火火,但是在可靠性上與合資車型依舊有差距,為了日後省心地過日子,合資家轎會是更好的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※回頭車貨運收費標準