減少碳排放 全球7大船東協會提燃油課稅案

摘錄自2019年12月18日中央社倫敦報導

重視巴黎氣候協定(Paris Agreement)所設定目標的全球7大船東協會,今天(18日)提出燃油課稅新方案,以減少造成全球汙染最嚴重行業之一的碳排放量。

包括波羅的海國際航運公會(BIMCO)、國際獨立油輪船東協會(Intertanko)與世界航運評議會(World Shipping Council)在內的7大船東協會發表聲明宣布,他們將向商船公司課徵每公噸燃油2美元的稅金,籌措50億美元(約新台幣1508億元)經費以成立研究航運業減碳的團體。

這個國際海洋研究發展理事會(The International Maritime Research and Development Board, IMRDB)將致力於減少航運業的二氧化碳排放量,並計畫於2030年初,研發商業上行得通的零排碳船。

7大船東協會另包括國際郵輪協會(Cruise Lines International Association)、國際乾貨船東協會(Intercargo)、國際航運商會(International Chamber of Shipping)與國際郵件油輪協會(International Parcel Tankers Association)。

 

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

※超省錢租車方案

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

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

中國高新汽車國際峰會 – 汽車行業思想領袖的國際峰會

第二屆中國高新汽車國際峰會由法蘭克福展覽(上海)公司和中國國家發展改革委員會國際合作中心聯合主辦,是真正國際化的高端會議,匯聚來自汽車行業的企業高管、中國政府高層官員和海外專家。    高峰會議程中將探討的關鍵主題包括:   ‧
促進中國國有和民營汽車企業的競爭力轉型:朝著真正合作、技術轉讓和全球市場渠道邁進   ‧
合併路線圖:如何盡可能擴大未來汽車行業的商業優勢
  ‧
混合動力機會評估:與電動汽車和輕量化ICE相比較
  ‧
通往電氣化的關鍵步驟:實現電動汽車和電動交通的真正發展
  ‧
汽車智聯技術創新實現新機會

  ‧
具備高增長潛力的未來配件市場機會:盈利發展   ‧
新型汽車市場定位:中西部地區與新興城市
 

數字營銷與社交媒體:OEM廠商和經銷商的最佳實踐
  ‧
有效減排:實現更加高效的商業車隊
  ‧
促進節能以及輕量化ICE的影響
  ‧
通過主動和被動方式,實現前所未有的駕駛者和車輛安全目標   ‧
輕量化實現更加節能、高性能的車型
  ‧
經銷商和分銷商在新維修市場中的新興機會   ‧
汽車融資和保險選項的演化和潛在新方向   ‧
展望未來:個人交通戰略與中國的未來生態城市相匹配   峰會誠邀候選演講嘉賓與主講嘉賓在2013年10月18日星期五之前提交演講提案。 峰會可提供的機會包括:思想領導力演講、企業高管訪談、互動小組討論、科技前沿和贊助活動。歡迎高層企業家參與主題演講,主題與演講機會有限,敬請報名。(請注意,演講提案並非僅限於上述主題,也歡迎您提供更多主題思路。)   提交程序   請在截止日期之前通過電子方式提交。請盡快從峰會官網下載「徵文申請表」,填寫並發回。所提交的所有申請表將由峰會議程組委會加以確認,並在評估階段提供反饋。 網址: http://www.nextgenautosummit-china.com/sc/Call_for_papers.html   Automechanika Shanghai上海汽配展毗鄰舉行 發揮絕佳的協同效應 Automechanika Shanghai上海國際汽車零部件、維修檢測診斷設備及汽車用品展覽會,將於2013年12月10至13日在毗連嘉裡大酒店的上海新國際博覽中心舉行。Automechanika Shanghai由法蘭克福展覽(上海)有限公司及中國汽車工業國際合作總公司聯合主辦,是規模僅次於德國法蘭克福母展的全球第二大Automechanika品牌展覽會,涵蓋汽車零部件、維修與保養、用 品及改裝三大行業板塊,涉及汽車原廠製造及售後市場的各 項產品及服務,整合汽車全產業鏈。預計展商數量超過4,400家,並將迎來8萬名海內外優質買家蒞臨,創歷屆展會之最。   垂詢與聯繫方式:    登記和綜合垂詢: Anna Gu 小姐 電話: +86 21 6160 8569 anna.gu@china.messefrankfurt.com   戰略合作機會: Keiann Yip 小姐 電話: +852 2230 9202 keiann.yip@hongkong.messefrankfurt.com   演講機會、贊助與檯面展示: Charlotte Chan 小姐 電話: +852 2111 3977 charlotte.chan@inspira-events.com    Michael Cherrington 先生 電話: +852 2111 3972 michael.cherrington@inspira-events.com   媒體垂詢: Rachelle Kong 電話: +852 2230 9226 rachelle.kong@hongkong.messefrankfurt.com  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

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

車型電氣化成趨勢 通用擬在華推多款電動車

車型電氣化已成為目前各大車企在新能源方面的發展方向,如大眾、BMW、奔馳等品牌皆是如此。作為美國車企巨頭的通用汽車,也開始沿襲電動化產品的發展趨勢。

通用汽車CEO丹‧艾克森表示,將會加強在電動汽車領域的推廣,其中中國市場是通用全球市場中的重要一環。此外,通用汽車還將通過推出豪華電動車跑車,抗衡目前風頭正勁的豪華電動車品牌特斯拉。

據通用汽車高管透露,未來通用汽車將在華推出凱迪拉克ELR和賽歐EV等多款電動車和插電混動車,加之已經引入的沃藍達,通用汽車在華將形成豐富電動車產品陣容。

通用汽車曾官方表示:“至2017年,通用汽車在全球范圍內每年將生產50萬輛採用電氣化技術的車輛。”對于目前通用汽車採用插電式混動車型僅有雪佛蘭沃藍達一款車型。今後還將陸續投放凱迪拉克ELR插電混動跑車和一款價格相比沃藍達更低的入門級插電混動車。此外,通用汽車還有多款搭載eAssist微混動力係統的車型,如別克君越、別克君威、雪佛蘭邁銳寶以及雪佛蘭Impala。

除通用汽車在華全面發展電氣化車型外,如BMW品牌和大眾集團也計劃在華開展電動車項目。BMW在發布全新子品牌BMW i,該品牌將致力于發展新能源車型,並于2014年將旗下首款電動車i3及超跑電動車i8發布上市。大眾集團也計劃先期在華投放e-up!,同時在2014年陸續將有多款車型匹配插電混合動力,從而使大眾汽車實現全面電氣化。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

挪威大力提倡電動汽車 9月Tesla銷量奪冠

美國電動汽車廠商特斯拉(Tesla),在挪威推出電動轎車Model S才2個月,需求即湧現,甚至9月榮登挪威最熱賣車款,市場認為這跟挪威政府大力補貼電動車有莫大關係。

特斯拉9月在挪威賣出616輛Model S,在挪威9月汽車總銷售裡占5.1%。至於日產電動車Leaf,1 ~9月為挪威包括汽油等各車種裡,暢銷排名第5的車款,在挪威整體車市裡占約3%。外界估計目前在挪威路上行駛的電動車達14,500輛。根據美國電力驅動運輸協會(EDTA)資料,美國去年僅賣出13,427輛電動車。

挪威大力提倡電動車,成為其他推廣電動車國家的一個典範。挪威首都奧斯陸市長史坦(Fabian Stang),最近宣布奧斯陸為「電動車之都」。他說,挪威對電動車立場,是對環保所做更大承諾的一部份。挪威政府估計,該國為全球人均電動車比例最高的國家。挪威全國人口約500萬人。

外界認為挪威有規模7,500億美元的主權財富基金做後盾,才能提供許多補貼來大力提倡電動車,使許多國家望塵莫及。其主權基金是以石油收益來建立,外界估計是全球規模最大的主權基金。

挪威給電動車豁免領牌費和25%增值稅,如此每輛電動車的車主立即省下數萬美元;豁免通行費,並可在奧斯陸的高速公路上使用公車專用道,能在最繁忙時段避開塞車之苦。挪威汽油價格為歐洲最高,每公升汽油15.16挪威克朗(約74.6台幣),讓電動車有很大吸引力。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

截至9月底 日產Leaf全球累計銷量83,000台

日本汽車大廠日產汽車(Nissan)昨(8)日發布新聞稿稱,旗下全球首款量產型電動車「Leaf」於全球各個市場的銷售氣勢持續高漲,截至2013年9月底為止,Leaf全球累計銷售量達83,000台,9月Leaf全球銷售量更高達4,700台、創史上最高單月銷售紀錄。另外,截至9月底為止,Leaf於日本市場的累計銷售量達30,000台。

日產表示,Leaf於2010年10月搶先於日本/北美市場開賣,且銷售量逐年增長,其中2012年度Leaf於日本市場的銷售量就達11,600台、且預估今年度(2013年度)日本市場銷售量可望更優於2012年度水準。

為了加快電動車的普及速度,日產還計劃於2013年度下半年內(2013年10月-2014年3月)追加於日本國內的日產銷售通路增設700座快速充電器。目前日本全國的快速充電器數量總計約1,900座,其中約800座設置在日產的銷售通路。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

預購突破8000臺 BMW考慮擴大電動i3生產規模

據彭博今(16)日報導,德國豪華車廠寶馬汽車公司(BMW)CFO Friedrich Eichiner表示,因初期需求超乎該公司原先預期,故考慮擴大電動車的生產規模。Friedrich Eichiner表示,i3雖尚未在歐洲市場開賣,但目前接獲的預購訂單已突破8,000台,且預估此種需求可望持續下去,故有必要立即進行進一步的投資。

據悉,i3將在11月16日於德國開賣,售價為3萬4,950歐元(約460萬日圓),之後並將在2014年1-6月期間於美國、日本、中國大陸進行販售。BMW預估2014年i3銷售量可達1萬台以上。

近來電動汽車需求出現明顯增長,除BMW之外,日產(Nissan)電動車「Leaf」也交出亮眼成績單。

據日經新聞報導,日產汽車首席運營官(COO)志賀俊之10月11日於東京都內舉行的日本證券分析師大會上表示,日產電動車「Leaf」今年度(2013年度)全球銷售量預估將能達到5萬台以上的水準,將較2012年度的3萬台呈現大幅度的增長(將大增7成)。

根據日產汽車10月8日發布的資料顯示,截至2013年9月底為止,Leaf全球累計銷售量達83,000台,而2013年9月份Leaf全球銷售量更高達4,700台、創史上最高單月銷售紀錄。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

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

萬向電動汽車獲得中國工信部資質認證

中國工信部今(22)日發布《車輛生產企業及產品公告》(第254批),公示了三家新增車輛生產企業的名單,公示時間為22日-28日。其中萬向電動汽車有限公司名列其中。

此前,萬向在電動汽車相關領域投入重資,雖已經初步形成車載電子系統、動力電池和電動汽車領等較完整的產業鏈,但在國內市場上,作為從事電動汽車相關業務的公司,仍缺少工信部認證的資質。

2013年1月,萬向成功收購美國A123,完善了電池產業鏈條,借全球知名企業品牌優勢,擴大了其在美國市場的銷售份額。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

※超省錢租車方案

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

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

一個線上問題的思考:Eureka註冊中心集群如何實現客戶端請求負載及故障轉移?

前言

先拋一個問題給我聰明的讀者,如果你們使用微服務SpringCloud-Netflix進行業務開發,那麼線上註冊中心肯定也是用了集群部署,問題來了:

你了解Eureka註冊中心集群如何實現客戶端請求負載及故障轉移嗎?

可以先思考一分鐘,我希望你能夠帶着問題來閱讀此篇文章,也希望你看完文章後會有所收穫!

背景

前段時間線上Sentry平台報警,多個業務服務在和註冊中心交互時,例如續約註冊表增量拉取等都報了Request execution failed with message : Connection refused 的警告:

緊接着又看到 Request execution succeeded on retry #2 的日誌。

看到這裏,表明我們的服務在嘗試兩次重連后和註冊中心交互正常了。

一切都顯得那麼有驚無險,這裏報Connection refused 是註冊中心網絡抖動導致的,接着觸發了我們服務的重連,重連成功后一切又恢復正常。

這次的報警雖然沒有對我們線上業務造成影響,並且也在第一時間恢復了正常,但作為一個愛思考的小火雞,我很好奇這背後的一系列邏輯:Eureka註冊中心集群如何實現客戶端請求負載及故障轉移?

註冊中心集群負載測試

線上註冊中心是由三台機器組成的集群,都是4c8g的配置,業務端配置註冊中心地址如下(這裏的peer來代替具體的ip地址):

eureka.client.serviceUrl.defaultZone=http://peer1:8080/eureka/,http://peer2:8080/eureka/,http://peer3:8080/eureka/

我們可以寫了一個Demo進行測試:

註冊中心集群負載測試

1、本地通過修改EurekaServer服務的端口號來模擬註冊中心集群部署,分別以87618762兩個端口進行啟動
2、啟動客戶端SeviceA,配置註冊中心地址為:http://localhost:8761/eureka,http://localhost:8762/eureka

3、啟動SeviceA時在發送註冊請求的地方打斷點:AbstractJerseyEurekaHttpClient.register(),如下圖所示:

這裏看到請求註冊中心時,連接的是8761這個端口的服務。

4、更改ServiceA中註冊中心的配置:http://localhost:8762/eureka,http://localhost:8761/eureka
5、重新啟動SeviceA然後查看端口,如下圖所示:

此時看到請求註冊中心是,連接的是8762這個端口的服務。

註冊中心故障轉移測試

以兩個端口分別啟動EurekaServer服務,再啟動一個客戶端ServiceA。啟動成功后,關閉一個8761端口對應的服務,查看此時客戶端是否會自動遷移請求到8762端口對應的服務:

1、以87618762兩個端口號啟動EurekaServer
2、啟動ServiceA,配置註冊中心地址為:http://localhost:8761/eureka,http://localhost:8762/eureka
3、啟動成功后,關閉8761端口的EurekaServer
4、在EurekaClient發送心跳請求的地方打上斷點:AbstractJerseyEurekaHttpClient.sendHeartBeat()
5、查看斷點處數據,第一次請求的EurekaServer8761端口的服務,因為該服務已經關閉,所以返回的responsenull

6、第二次會重新請求8762端口的服務,返回的response為狀態為200,故障轉移成功,如下圖:

思考

通過這兩個測試Demo,我以為EurekaClient每次都會取defaultZone配置的第一個host作為請求EurekaServer的請求的地址,如果該節點故障時,會自動切換配置中的下一個EurekaServer進行重新請求。

那麼疑問來了,EurekaClient每次請求真的是以配置的defaultZone配置的第一個服務節點作為請求的嗎?這似乎也太弱了!!?

EurekaServer集群不就成了偽集群!!?除了客戶端配置的第一個節點,其它註冊中心的節點都只能作為備份和故障轉移來使用!!?

真相是這樣嗎?NO!我們眼見也不一定為實,源碼面前毫無秘密!

翠花,上乾貨!

客戶端請求負載原理

原理圖解

還是先上結論,負載原理如圖所示:

這裡會以EurekaClient端的IP作為隨機的種子,然後隨機打亂serverList,例如我們在商品服務(192.168.10.56)中配置的註冊中心集群地址為:peer1,peer2,peer3,打亂后的地址可能變成peer3,peer2,peer1

用戶服務(192.168.22.31)中配置的註冊中心集群地址為:peer1,peer2,peer3,打亂后的地址可能變成peer2,peer1,peer3

EurekaClient每次請求serverList中的第一個服務,從而達到負載的目的。

代碼實現

我們直接看最底層負載代碼的實現,具體代碼在
com.netflix.discovery.shared.resolver.ResolverUtils.randomize() 中:

這裏面random 是通過我們EurekaClient端的ipv4做為隨機的種子,生成一個重新排序的serverList,也就是對應代碼中的randomList,所以每個EurekaClient獲取到的serverList順序可能不同,在使用過程中,取列表的第一個元素作為serverhost,從而達到負載的目的。

思考

原來代碼是通過EurekaClientIP進行負載的,所以剛才通過DEMO程序結果就能解釋的通了,因為我們做實驗都是用的同一個IP,所以每次都是會訪問同一個Server節點。

既然說到了負載,這裏肯定會有另一個疑問:

通過IP進行的負載均衡,每次請求都會均勻分散到每一個Server節點嗎?

比如第一次訪問Peer1,第二次訪問Peer2,第三次訪問Peer3,第四次繼續訪問Peer1等,循環往複……

我們可以繼續做個試驗,假如我們有10000個EurekaClient節點,3個EurekaServer節點。

Client節點的IP區間為:192.168.0.0 ~ 192.168.255.255,這裏面共覆蓋6w多個ip段,測試代碼如下:

/**
 * 模擬註冊中心集群負載,驗證負載散列算法
 *
 *  @author 一枝花算不算浪漫
 *  @date 2020/6/21 23:36
 */
public class EurekaClusterLoadBalanceTest {

    public static void main(String[] args) {
        testEurekaClusterBalance();
    }

    /**
     * 模擬ip段測試註冊中心負載集群
     */
    private static void testEurekaClusterBalance() {
        int ipLoopSize = 65000;
        String ipFormat = "192.168.%s.%s";
        TreeMap<String, Integer> ipMap = Maps.newTreeMap();
        int netIndex = 0;
        int lastIndex = 0;
        for (int i = 0; i < ipLoopSize; i++) {
            if (lastIndex == 256) {
                netIndex += 1;
                lastIndex = 0;
            }

            String ip = String.format(ipFormat, netIndex, lastIndex);
            randomize(ip, ipMap);
            System.out.println("IP: " + ip);
            lastIndex += 1;
        }

        printIpResult(ipMap, ipLoopSize);
    }

    /**
     * 模擬指定ip地址獲取對應註冊中心負載
     */
    private static void randomize(String eurekaClientIp, TreeMap<String, Integer> ipMap) {
        List<String> eurekaServerUrlList = Lists.newArrayList();
        eurekaServerUrlList.add("http://peer1:8080/eureka/");
        eurekaServerUrlList.add("http://peer2:8080/eureka/");
        eurekaServerUrlList.add("http://peer3:8080/eureka/");

        List<String> randomList = new ArrayList<>(eurekaServerUrlList);
        Random random = new Random(eurekaClientIp.hashCode());
        int last = randomList.size() - 1;
        for (int i = 0; i < last; i++) {
            int pos = random.nextInt(randomList.size() - i);
            if (pos != i) {
                Collections.swap(randomList, i, pos);
            }
        }

        for (String eurekaHost : randomList) {
            int ipCount = ipMap.get(eurekaHost) == null ? 0 : ipMap.get(eurekaHost);
            ipMap.put(eurekaHost, ipCount + 1);
            break;
        }
    }

    private static void printIpResult(TreeMap<String, Integer> ipMap, int totalCount) {
        for (Map.Entry<String, Integer> entry : ipMap.entrySet()) {
            Integer count = entry.getValue();
            BigDecimal rate = new BigDecimal(count).divide(new BigDecimal(totalCount), 2, BigDecimal.ROUND_HALF_UP);
            System.out.println(entry.getKey() + ":" + count + ":" + rate.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP) + "%");
        }
    }
}

負載測試結果如下:

可以看到第二個機器會有50%的請求,最後一台機器只有17%的請求,負載的情況並不是很均勻,我認為通過IP負載並不是一個好的方案。

還記得我們之前講過Ribbon默認的輪詢算法RoundRobinRule,【一起學源碼-微服務】Ribbon 源碼四:進一步探究Ribbon的IRule和IPing 。

這種算法就是一個很好的散列算法,可以保證每次請求都很均勻,原理如下圖:

故障轉移原理

原理圖解

還是先上結論,如下圖:

我們的serverList按照client端的ip進行重排序后,每次都會請求第一個元素作為和Server端交互的host,如果請求失敗,會嘗試請求serverList列表中的第二個元素繼續請求,這次請求成功后,會將此次請求的host放到全局的一個變量中保存起來,下次client端再次請求 就會直接使用這個host

這裏最多會重試請求兩次。

代碼實現

直接看底層交互的代碼,位置在
com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute() 中:

我們來分析下這個代碼:

  1. 第101行,獲取client上次成功server端的host,如果有值則直接使用這個host
  2. 第105行,getHostCandidates()是獲取client端配置的serverList數據,且通過ip進行重排序的列表
  3. 第114行,candidateHosts.get(endpointIdx++),初始endpointIdx=0,獲取列表中第1個元素作為host請求
  4. 第120行,獲取返回的response結果,如果返回的狀態碼是200,則將此次請求的host設置到全局的delegate變量中
  5. 第133行,執行到這裏說明第120行執行的response返回的狀態碼不是200,也就是執行失敗,將全局變量delegate中的數據清空
  6. 再次循環第一步,此時endpointIdx=1,獲取列表中的第二個元素作為host請求
  7. 依次執行,第100行的循環條件numberOfRetries=3,最多重試2次就會跳出循環

我們還可以第123和129行,這也正是我們業務拋出來的日誌信息,所有的一切都對應上了。

總結

感謝你看到這裏,相信你已經清楚了開頭提問的問題。

上面已經分析完了Eureka集群下Client端請求時負載均衡的選擇以及集群故障時自動重試請求的實現原理。

如果還有不懂的問題,可以添加我的微信或者給我公眾號留言,我會單獨和你討論交流。

本文首發自:一枝花算不算浪漫 公眾號,如若轉載請在文章開頭標明出處,如需開白可直接公眾號回復即可。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

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

Jmeter(十二) – 從入門到精通 – JMeter邏輯控制器 – 終篇(詳解教程)

1.簡介

Jmeter官網對邏輯控制器的解釋是:“Logic Controllers determine the order in which Samplers are processed.”。

意思是說,邏輯控制器可以控制採樣器(samplers)的執行順序。由此可知,控制器需要和採樣器一起使用,否則控制器就沒有什麼意義了。放在控制器下面的所有的採樣器都會當做一個整體,執行時也會一起被執行。

JMeter邏輯控制器可以對元件的執行邏輯進行控制,除僅一次控制器外,其他可以嵌套別的種類的邏輯控制器。

2.邏輯控制器分類

JMeter中的Logic Controller分為兩類:
(1)控制測試計劃執行過程中節點的邏輯執行順序,如:Loop Controller、If Controller等;
(2)對測試計劃中的腳本進行分組、方便JMeter統計執行結果以及進行腳本的運行時控制等,如:Throughput Controller、Transaction Controller。

3.預覽邏輯控制器 

首先我們來看一下JMeter的邏輯控制器,路徑:線程組(用戶)->添加->邏輯控制器(Logic Controller);我們可以清楚地看到JMeter5中共有17個邏輯控制器,如下圖所示:

如果上圖您看得不是很清楚的話,宏哥總結了一個思維導圖,關於JMeter5的邏輯控制器類型,如下圖所示: 

 通過以上的了解,我們對邏輯控制器有了一個大致的了解和認識。下面宏哥就給小夥伴或則童鞋們分享講解一些通常在工作中會用到的邏輯控制器。 

4.常用邏輯控制器詳解

  這一小節,宏哥就由上而下地詳細地講解一下常用的邏輯控制器。

4.1Runtime Controller

運行控制器用來控制其子元件的執行時長。市場單位是秒。

 1、我們先來看看這個Runtime Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 運行控制器,如下圖所示:

2、關鍵參數說明如下:

Name:名稱,可以隨意設置,甚至為空;

Comments:註釋,可隨意設置,可以為空;

Runtime:默認為1,去掉1則默認為0,此時不執行其節點下的元件。 與線程組中的調度器的持續時間 效果一致。不填 或 0,不會執行樣例

4.1.1Runtime控制器控制其下取樣器執行2s

1、創建測試計劃,設置 Runtime 控制器的運行時間 為 2,線程組設置默認不變,如下圖所示:

Runtime 控制器設置

線程組設置

2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

4.1.2使用線程組中的調度器控制樣例運行3s

1、創建測試計劃,設置 Runtime 控制器的運行時間 為 2,線程組設置運行時間3,如下圖所示:

線程組設置

Runtime 控制器設置

2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

線程組設置3,Runtime控制器設置2,但是運行時間是2s。所以從上邊的運行時間得出結論:如果線程組中設置了持續時間,Runtime 控制器也設置了 運行時間,那麼會優先於線程組中的設置。

4.2Simple Controller

Simple Controller用來指定了一個執行單元,它不改變元件的執行順序。在它下邊還可以嵌套其他控制器。簡單控制器可以編輯只有名稱和註釋。就像他的名字一樣,簡單,可以理解為一個文件夾,就是分組用的,沒有其他特殊功能,但相比不添加簡單控制器,區別在於簡單控制器可以被模塊控制器所引用。其作用就是分組,比如QQ好友列表,可分為家人、同學、等。一般是請求較多,需要分組時採用。

 1、我們先來看看這個Simple Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 簡單控制器,如下圖所示:

2、關鍵參數說明如下:

Name:名稱,可以隨意設置,甚至為空;

Comments:註釋,可隨意設置,可以為空。

4.2.1簡單實例

1、創建測試計劃,線程組設置循環10,如下圖所示:

2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

4.3Throughput Controller

用來控制其下元件的執行次數,並無控制吞吐量的功能,想要控制吞吐量可以使用Constant Throughput Timer,後邊會講解到。吞吐量控制器有兩種模式:Total Executions:設置運行次數與Percent Executions:設置運行比例(1~100之間)。

1、我們先來看看這個Throughput Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 吞吐量控制器,如下圖所示: 

2、關鍵參數說明如下:

Name:名稱,可以隨意設置,甚至為空;

Comments:註釋,可隨意設置,可以為空;

Total Executions:執行百分比(1-100);

percent Executions:執行數量;

Throughput:根據上邊選擇的方式填寫,百分比為0~100;

Per User:線程數,當選Total Executions時,是線程數;當選percent Executions時,是線程數*循環次數。

4.3.1不勾選Per User

1、線程組中設置 線程數量 2,循環次數 10,吞吐量控制器 設置 Total Executions,吞吐量設置為 2,其下添加一個取樣器,如下圖所示:

2、配置好以後,運行JMeter,然後查看結果樹(執行了2次),如下圖所示:

3、現在將 吞吐量控制器 設置為百分比的控制方式,吞吐量設置為:50%,如下圖所示:

4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了10次,計算方式:10=吞吐量50% * 循環次數10 * 線程數 2),如下圖所示:

4.3.2勾選Per User

1、線程組中設置 線程數量 2,循環次數 10,吞吐量控制器 設置 Total Executions,吞吐量設置為 2,其下添加一個取樣器,勾選Per User,如下圖所示:

線程組設置

吞吐量控制器

2、配置好以後,點擊“保存”,運行JMeter,然後查看結果樹(總共執行了4次,其中吞吐量設置為2,執行2次,線程設置為2,執行2次,總共4次),函數 __threadNum 只是簡單地返回當前線程的編號,如下圖所示:

3、現在將 吞吐量控制器 設置為百分比的控制方式,吞吐量設置為:50,如下圖所示:

4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了10次,計算方式:10=吞吐量50% * 循環次數10 * 線程數 2),如下圖所示:

綜上所述:

勾選Per User:

1.線程數*循環次數>=線程數*吞吐量時,Total Executions模式的執行次數=線程數*吞吐量。

2.線程數*循環次數<線程數*吞吐量時,Total Executions模式的執行次數=當線程數*循環次數。

不勾選Per User:

1.線程數*循環次數<=吞吐量時,Total Executions模式的執行次數=線程數*循環次數。

2.線程數*循環次數>吞吐量時,Total Executions模式的執行次數=吞吐量。

l Percent Executions:設置運行比例(1~100之間),單位為%

不管Per User是否勾選,按Percent Executions模式的執行次數都不受Per User影響,Percent Executions模式的執行次數=線程數*循環次數*吞吐量%。(循環次數=線程組循環次數*循環控制器循環次數)

l Per User:勾選該項的話則按虛擬用戶數(線程數)來計算執行次數,不勾選則按所有虛擬用戶數來計算執行次數

測試計劃

序號 線程數 循環次數 模式 Throughput Per User 執行次數
1 2 10 Percent 50 Y 10
2 2 10 Percent 50 N 10
3 2 10 Total 7 Y 14
4 2 10 Total 7 N 7
5 2 2 Total 7 Y 4
6 2 2 Total 7 N 4

下面說明一下這6個場景:
(1)序號1和2場景,Per User 對總執行次數沒有影響。
(2)序號3場景,Per User勾選,每個虛擬用戶(線程)執行7次,共執行14次。
(3)序號4場景,Per User不勾選,則所有虛擬用戶執行7次。
(4)序號5場景,Per User勾選,每個虛擬用戶(線程)執行7次,共執行14次,由於Thread Group計劃循環次數是4(2線程*2循環)次,所以最多只能執行4次。
(5)序號6場景,Per User不勾選,所有虛擬用戶執行7次,由於Thread Group計劃循環次數是4(2線程*2循環)次,所以最多只能執行4次。

4.4Module Controller

模塊控制器可以快速的切換腳本,不用來回的新建,方便腳本調試。 

可以理解為引用、調用的意思,執行內容為Module To Run種所選的內容,引用範圍為當前測試計劃內的測試片段、邏輯控制器<模塊控制器除外>
被引用的邏輯控制器、測試片段可以為禁用狀態,被引用后仍然會被執行。
可以將模塊控制器與包括控制器一起學習比較,模塊控制器是從內部文件中引用,引用上相對比較靈活,可以只引用部分測試片段或模塊內容,包括控制器是從外部文件引用,只能引用整個測試片段的內容。
注意:被應用的模塊位置不可隨意變更,變更後會執行時出現提示引用失敗
找到目標元素:快速查找與跳轉的作用,點擊後會立即跳轉到所選的邏輯控制器的內容詳情

1、我們先來看看這個Module Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 >  模塊控制器,如下圖所示: 

2、關鍵參數說明如下:

Name:名稱,可以隨意設置,甚至為空;

Comments:註釋,可隨意設置,可以為空;

Forever:勾選上這一項表示一直循環下去。

4.4.1實例

1、創建測試計劃,添加兩個測試片段,並且在每個測試片段下添加一個取樣器,然後,添加線程組,再添加模塊控制器,最後添加查看結果樹,如下圖所示:

2、配置模塊控制器,選擇第一個測試片段,如下圖所示:

3、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第1個測試片段的取樣器),如下圖所示:

4、配置模塊控制器,選擇第二個測試片段,如下圖所示: 

5、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第2個測試片段的取樣器),如下圖所示:

4.5Switch Controller

Switch Controller:開關控制器,通過其下樣例順序數值或名稱 控制執行某一個樣例。

 1、我們先來看看這個if Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 如果 (if) 控制器,如下圖所示:

2、關鍵參數說明如下:

Name:名稱,可以隨意設置,甚至為空;

Comments:註釋,可隨意設置,可以為空;

Switch Value:指定請求的索引或者名稱,索引從0開始,如果沒有賦值,或者索引超過請求個數的話就執行第0個請求。可以是数字,也可以是字符,為字符時匹配取樣器名稱,如果匹配不上就會默認並找取樣器名稱為default的取樣器,如果沒有則不運行。

4.5.1數值

數值:表示將執行其下第 數值+1個取樣器,例如:填1,將執行第2個取樣器;填0或者不填,將執行第1個取樣器;數值超出其下取樣器數目時,執行第1個取樣器。

1、創建一個測試計劃,設置線程組和Switch控制器,如下圖所示:

線程組

Switch控制器

2、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第3<數值+1>個取樣器),如下圖所示:

3、修改Switch控制器的數值為0或者不填,如下圖所示:

4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第1<數值為0或者不填,執行第1個取樣器>個取樣器),如下圖所示:

4.5.2字符

1、創建一個測試計劃,設置線程組和Switch控制器(直接使用取樣器名字),如下圖所示:

線程組

Switch控制器

2、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了使用名字的取樣器),如下圖所示:

5.小結

好了,今天關於邏輯控制器的上篇就講解到這裏,這一篇主要介紹了 Runtime Controller 、 Simple Controller 、Throughput ControllerModule Controller 和  Switch Controller

 

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

別忘了點 推薦 留下您來過的痕迹

 

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

基於領域驅動設計(DDD)超輕量級快速開發架構(二)動態linq查詢的實現方式

-之動態查詢,查詢邏輯封裝復用

基於領域驅動設計(DDD)超輕量級快速開發架構詳細介紹請看

https://www.cnblogs.com/neozhu/p/13174234.html

需求

  1. 配合EasyUI datagird filter實現多字段(任意字段)的篩選
  2. 根據業務需求篩選特定的狀態或條件,如:查看結案的訂單,最近30天的訂單,查看屬於我的訂單.等等,這些邏輯是固定也是可以被重用,但又不想每次寫相同的條件,那麼下面我會給我的解決方案.

需求1隻是一個偷懶的實現方式,因為datagrid自帶這個功能,但又不想根據具體的需求來畫查詢條件,如果需求必須要再datagrid上面做一塊查詢條件的輸入那目前只能在前端自己手工添加,在組織後傳入後台,暫時不在這裏討論

需求2可能不太好解釋,看完代碼就自然理解為什麼要這麼做了,這麼做的好處有哪些

具體實現的方式

 

 默認情況下 datagrid 有幾列就可以對這幾列進行篩選,對於日期型的字段會採用between,選擇2個時間之間進行篩選,数字類型會提供大於小於等符號選擇,可以自行嘗試,其原理是datagrid 會根據datagrid 頭部輸入的值生成一個Json字符串發送後台請求數據

JSON:格式

filterRules: [
{field:field,op:op,value:value},
{field:field,op:op,value:value},
] 
  • 通常的做法是一個一個判斷加條件
  1 var filters = JsonConvert.DeserializeObject<IEnumerable<filterRule>>(filterRules); 
  2 foreach (var rule in filters)
  3         {
  4           if (rule.field == "Id" && !string.IsNullOrEmpty(rule.value) && rule.value.IsInt())
  5           {
  6             var val = Convert.ToInt32(rule.value);
  7             switch (rule.op)
  8             {
  9               case "equal":
 10                 this.And(x => x.Id == val);
 11                 break;
 12               case "notequal":
 13                 this.And(x => x.Id != val);
 14                 break;
 15               case "less":
 16                 this.And(x => x.Id < val);
 17                 break;
 18               case "lessorequal":
 19                 this.And(x => x.Id <= val);
 20                 break;
 21               case "greater":
 22                 this.And(x => x.Id > val);
 23                 break;
 24               case "greaterorequal":
 25                 this.And(x => x.Id >= val);
 26                 break;
 27               default:
 28                 this.And(x => x.Id == val);
 29                 break;
 30             }
 31           }
 32           if (rule.field == "Name" && !string.IsNullOrEmpty(rule.value))
 33           {
 34             this.And(x => x.Name.Contains(rule.value));
 35           }
 36           if (rule.field == "Code" && !string.IsNullOrEmpty(rule.value))
 37           {
 38             this.And(x => x.Code.Contains(rule.value));
 39           }
 40 
 41           if (rule.field == "Address" && !string.IsNullOrEmpty(rule.value))
 42           {
 43             this.And(x => x.Address.Contains(rule.value));
 44           }
 45 
 46           if (rule.field == "Contect" && !string.IsNullOrEmpty(rule.value))
 47           {
 48             this.And(x => x.Contect.Contains(rule.value));
 49           }
 50 
 51           if (rule.field == "PhoneNumber" && !string.IsNullOrEmpty(rule.value))
 52           {
 53             this.And(x => x.PhoneNumber.Contains(rule.value));
 54           }
 55 
 56           if (rule.field == "RegisterDate" && !string.IsNullOrEmpty(rule.value))
 57           {
 58             if (rule.op == "between")
 59             {
 60               var datearray = rule.value.Split(new char[] { '-' });
 61               var start = Convert.ToDateTime(datearray[0]);
 62               var end = Convert.ToDateTime(datearray[1]);
 63 
 64               this.And(x => SqlFunctions.DateDiff("d", start, x.RegisterDate) >= 0);
 65               this.And(x => SqlFunctions.DateDiff("d", end, x.RegisterDate) <= 0);
 66             }
 67           }
 68           if (rule.field == "CreatedDate" && !string.IsNullOrEmpty(rule.value))
 69           {
 70             if (rule.op == "between")
 71             {
 72               var datearray = rule.value.Split(new char[] { '-' });
 73               var start = Convert.ToDateTime(datearray[0]);
 74               var end = Convert.ToDateTime(datearray[1]);
 75 
 76               this.And(x => SqlFunctions.DateDiff("d", start, x.CreatedDate) >= 0);
 77               this.And(x => SqlFunctions.DateDiff("d", end, x.CreatedDate) <= 0);
 78             }
 79           }
 80 
 81 
 82           if (rule.field == "CreatedBy" && !string.IsNullOrEmpty(rule.value))
 83           {
 84             this.And(x => x.CreatedBy.Contains(rule.value));
 85           }
 86 
 87          if (rule.field == "LastModifiedDate" && !string.IsNullOrEmpty(rule.value))
 88           {
 89             if (rule.op == "between")
 90             {
 91               var datearray = rule.value.Split(new char[] { '-' });
 92               var start = Convert.ToDateTime(datearray[0]);
 93               var end = Convert.ToDateTime(datearray[1]);
 94 
 95               this.And(x => SqlFunctions.DateDiff("d", start, x.LastModifiedDate) >= 0);
 96               this.And(x => SqlFunctions.DateDiff("d", end, x.LastModifiedDate) <= 0);
 97             }
 98           }
 99 
100           if (rule.field == "LastModifiedBy" && !string.IsNullOrEmpty(rule.value))
101           {
102             this.And(x => x.LastModifiedBy.Contains(rule.value));
103           }
104 
105         }

View Code

  • 新的做法是動態根據field,op,value生成一個linq 表達式,不用再做繁瑣的判斷,這塊代碼也可以被其它項目使用,非常好用
namespace SmartAdmin
{
 
  public static class PredicateBuilder
  {

    public static Expression<Func<T, bool>> FromFilter<T>(string filtergroup) {
      Expression<Func<T, bool>> any = x => true;
      if (!string.IsNullOrEmpty(filtergroup))
      {
        var filters = JsonSerializer.Deserialize<filter[]>(filtergroup);

          foreach (var filter in filters)
          {
            if (Enum.TryParse(filter.op, out OperationExpression op) && !string.IsNullOrEmpty(filter.value))
            {
              var expression = GetCriteriaWhere<T>(filter.field, op, filter.value);
              any = any.And(expression);
            }
          }
      }

      return any;
    }

    #region -- Public methods --
    public static Expression<Func<T, bool>> GetCriteriaWhere<T>(Expression<Func<T, object>> e, OperationExpression selectedOperator, object fieldValue)
    {
      var name = GetOperand<T>(e);
      return GetCriteriaWhere<T>(name, selectedOperator, fieldValue);
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T, T2>(Expression<Func<T, object>> e, OperationExpression selectedOperator, object fieldValue)
    {
      var name = GetOperand<T>(e);
      return GetCriteriaWhere<T, T2>(name, selectedOperator, fieldValue);
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T>(string fieldName, OperationExpression selectedOperator, object fieldValue)
    {
      var props = TypeDescriptor.GetProperties(typeof(T));
      var prop = GetProperty(props, fieldName, true);
      var parameter = Expression.Parameter(typeof(T));
      var expressionParameter = GetMemberExpression<T>(parameter, fieldName);
      if (prop != null && fieldValue != null)
      {
       
        BinaryExpression body = null;
        switch (selectedOperator)
        {
          case OperationExpression.equal:
            body = Expression.Equal(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType)?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.notequal:
            body = Expression.NotEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.less:
            body = Expression.LessThan(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.lessorequal:
            body = Expression.LessThanOrEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.greater:
            body = Expression.GreaterThan(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.greaterorequal:
            body = Expression.GreaterThanOrEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.contains:
            var contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            var bodyLike = Expression.Call(expressionParameter, contains, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodyLike, parameter);
          case OperationExpression.endwith:
            var endswith = typeof(string).GetMethod("EndsWith",new[] { typeof(string) });
            var bodyendwith = Expression.Call(expressionParameter, endswith, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodyendwith, parameter);
          case OperationExpression.beginwith:
            var startswith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
            var bodystartswith = Expression.Call(expressionParameter, startswith, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodystartswith, parameter);
          case OperationExpression.includes:
            return Includes<T>(fieldValue, parameter, expressionParameter, prop.PropertyType);
          case OperationExpression.between:
            return Between<T>(fieldValue, parameter, expressionParameter, prop.PropertyType);
          default:
            throw new Exception("Not implement Operation");
        }
      }
      else
      {
        Expression<Func<T, bool>> filter = x => true;
        return filter;
      }
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T, T2>(string fieldName, OperationExpression selectedOperator, object fieldValue)
    {


      var props = TypeDescriptor.GetProperties(typeof(T));
      var prop = GetProperty(props, fieldName, true);

      var parameter = Expression.Parameter(typeof(T));
      var expressionParameter = GetMemberExpression<T>(parameter, fieldName);

      if (prop != null && fieldValue != null)
      {
        switch (selectedOperator)
        {
          case OperationExpression.any:
            return Any<T, T2>(fieldValue, parameter, expressionParameter);

          default:
            throw new Exception("Not implement Operation");
        }
      }
      else
      {
        Expression<Func<T, bool>> filter = x => true;
        return filter;
      }
    }



    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
    {
      if (expr == null)
      {
        return or;
      }

      return Expression.Lambda<Func<T, bool>>(Expression.OrElse(new SwapVisitor(expr.Parameters[0], or.Parameters[0]).Visit(expr.Body), or.Body), or.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
    {
      if (expr == null)
      {
        return and;
      }

      return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(new SwapVisitor(expr.Parameters[0], and.Parameters[0]).Visit(expr.Body), and.Body), and.Parameters);
    }

    #endregion
    #region -- Private methods --

    private static string GetOperand<T>(Expression<Func<T, object>> exp)
    {
      if (!( exp.Body is MemberExpression body ))
      {
        var ubody = (UnaryExpression)exp.Body;
        body = ubody.Operand as MemberExpression;
      }

      var operand = body.ToString();

      return operand.Substring(2);

    }

    private static MemberExpression GetMemberExpression<T>(ParameterExpression parameter, string propName)
    {
      if (string.IsNullOrEmpty(propName))
      {
        return null;
      }

      var propertiesName = propName.Split('.');
      if (propertiesName.Count() == 2)
      {
        return Expression.Property(Expression.Property(parameter, propertiesName[0]), propertiesName[1]);
      }

      return Expression.Property(parameter, propName);
    }

    private static Expression<Func<T, bool>> Includes<T>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression ,Type type)
    {
      var safetype= Nullable.GetUnderlyingType(type) ?? type;

      switch (safetype.Name.ToLower())
      {
        case  "string":
          var strlist = (IEnumerable<string>)fieldValue;
          if (strlist == null || strlist.Count() == 0)
          {
            return x => true;
          }
          var strmethod = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
          var strcallexp = Expression.Call(Expression.Constant(strlist.ToList()), strmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(strcallexp, parameterExpression);
        case "int32":
          var intlist = (IEnumerable<int>)fieldValue;
          if (intlist == null || intlist.Count() == 0)
          {
            return x => true;
          }
          var intmethod = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
          var intcallexp = Expression.Call(Expression.Constant(intlist.ToList()), intmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(intcallexp, parameterExpression);
        case "float":
          var floatlist = (IEnumerable<float>)fieldValue;
          if (floatlist == null || floatlist.Count() == 0)
          {
            return x => true;
          }
          var floatmethod = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
          var floatcallexp = Expression.Call(Expression.Constant(floatlist.ToList()), floatmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(floatcallexp, parameterExpression);
        default:
          return x => true;
      }
      
    }
    private static Expression<Func<T, bool>> Between<T>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression, Type type)
    {
      
      var safetype = Nullable.GetUnderlyingType(type) ?? type;
      switch (safetype.Name.ToLower())
      {
        case "datetime":
          var datearray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var start = Convert.ToDateTime(datearray[0] + " 00:00:00", CultureInfo.CurrentCulture);
          var end = Convert.ToDateTime(datearray[1] + " 23:59:59", CultureInfo.CurrentCulture);
          var greater = Expression.GreaterThan(memberExpression, Expression.Constant(start, type));
          var less = Expression.LessThan(memberExpression, Expression.Constant(end, type));
          return Expression.Lambda<Func<T, bool>>(greater, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(less, parameterExpression));
        case "int":
        case "int32":
          var intarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var min = Convert.ToInt32(intarray[0] , CultureInfo.CurrentCulture);
          var max = Convert.ToInt32(intarray[1], CultureInfo.CurrentCulture);
          var maxthen = Expression.GreaterThan(memberExpression, Expression.Constant(min, type));
          var minthen = Expression.LessThan(memberExpression, Expression.Constant(max, type));
          return Expression.Lambda<Func<T, bool>>(maxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(minthen, parameterExpression));
        case "decimal":
          var decarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var dmin = Convert.ToDecimal(decarray[0], CultureInfo.CurrentCulture);
          var dmax = Convert.ToDecimal(decarray[1], CultureInfo.CurrentCulture);
          var dmaxthen = Expression.GreaterThan(memberExpression, Expression.Constant(dmin, type));
          var dminthen = Expression.LessThan(memberExpression, Expression.Constant(dmax, type));
          return Expression.Lambda<Func<T, bool>>(dmaxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(dminthen, parameterExpression));
        case "float":
          var farray = ((string)fieldValue).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var fmin = Convert.ToDecimal(farray[0], CultureInfo.CurrentCulture);
          var fmax = Convert.ToDecimal(farray[1], CultureInfo.CurrentCulture);
          var fmaxthen = Expression.GreaterThan(memberExpression, Expression.Constant(fmin, type));
          var fminthen = Expression.LessThan(memberExpression, Expression.Constant(fmax, type));
          return Expression.Lambda<Func<T, bool>>(fmaxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(fminthen, parameterExpression));
        case "string":
          var strarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var smin = strarray[0];
          var smax = strarray[1];
        
          var strmethod = typeof(string).GetMethod("Contains");
          var mm = Expression.Call(memberExpression, strmethod, Expression.Constant(smin, type));
          var nn = Expression.Call(memberExpression, strmethod, Expression.Constant(smax, type));


          return Expression.Lambda<Func<T, bool>>(mm, parameterExpression)
            .Or(Expression.Lambda<Func<T, bool>>(nn, parameterExpression));
        default:
          return x => true;
      }

    }



    private static Expression<Func<T, bool>> Any<T, T2>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression)
    {
      var lambda = (Expression<Func<T2, bool>>)fieldValue;
      var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
      .First(m => m.Name == "Any" && m.GetParameters().Count() == 2).MakeGenericMethod(typeof(T2));

      var body = Expression.Call(anyMethod, memberExpression, lambda);

      return Expression.Lambda<Func<T, bool>>(body, parameterExpression);
    }

    private static PropertyDescriptor GetProperty(PropertyDescriptorCollection props, string fieldName, bool ignoreCase)
    {
      if (!fieldName.Contains('.'))
      {
        return props.Find(fieldName, ignoreCase);
      }

      var fieldNameProperty = fieldName.Split('.');
      return props.Find(fieldNameProperty[0], ignoreCase).GetChildProperties().Find(fieldNameProperty[1], ignoreCase);

    }
    #endregion
  }

  internal class SwapVisitor : ExpressionVisitor
  {
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to)
    {
      this.from = from;
      this.to = to;
    }
    public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
  }
  public enum OperationExpression
  {
    equal,
    notequal,
    less,
    lessorequal,
    greater,
    greaterorequal,
    contains,
    beginwith,
    endwith,
    includes,
    between,
    any
  }
}

View Code

 1 public async Task<JsonResult> GetData(int page = 1, int rows = 10, string sort = "Id", string order = "asc", string filterRules = "")
 2     {
 3       try
 4       {
 5         var filters = PredicateBuilder.FromFilter<Company>(filterRules);
 6         var total = await this.companyService
 7                              .Query(filters)
 8                              .AsNoTracking()
 9                              .CountAsync()
10                               ;
11         var pagerows = (await this.companyService
12                              .Query(filters)
13                               .AsNoTracking()
14                            .OrderBy(n => n.OrderBy(sort, order))
15                            .Skip(page - 1).Take(rows)
16                            .SelectAsync())
17                            .Select(n => new
18                            {
19                              Id = n.Id,
20                              Name = n.Name,
21                              Code = n.Code,
22                              Address = n.Address,
23                              Contect = n.Contect,
24                              PhoneNumber = n.PhoneNumber,
25                              RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
26                            }).ToList();
27         var pagelist = new { total = total, rows = pagerows };
28         return Json(pagelist);
29       }
30       catch(Exception e) {
31         throw e;
32         }
33 
34     }

配合使用的代碼

  • 對於固定查詢邏輯的封裝和復用,當然除了復用還可以明顯的提高代碼的可讀性.
public class OrderSalesQuery : QueryObject<Order>
{
    public decimal Amount { get; set; }
    public string Country { get; set; }
    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }

    public override Expression<Func<Order, bool>> Query()
    {
        return (x => 
            x.OrderDetails.Sum(y => y.UnitPrice) > Amount &&
            x.OrderDate >= FromDate &&
            x.OrderDate <= ToDate &&
            x.ShipCountry == Country);
    }
}

查看訂單的銷售情況,條件 金額,國家,日期

var orderRepository = new Repository<Order>(this);
    
var orders = orderRepository
    .Query(new OrderSalesQuery(){ 
        Amount = 100, 
        Country = "USA",
        FromDate = DateTime.Parse("01/01/1996"), 
        ToDate = DateTime.Parse("12/31/1996" )
    })
    .Select();

調用查詢方法

public class CustomerLogisticsQuery : QueryObject<Customer>
{
    public CustomerLogisticsQuery FromCountry(string country)
    {
        Add(x => x.Country == country);
        return this;
    }

    public CustomerLogisticsQuery LivesInCity(string city)
    {   
        Add(x => x.City == city);
        return this;
    }
}

客戶查詢 根據國家和城市查詢

public class CustomerSalesQuery : QueryObject<Customer>
{
    public CustomerSalesQuery WithPurchasesMoreThan(decimal amount)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.UnitPrice * z.Quantity) > amount);

        return this;
    }

    public CustomerSalesQuery WithQuantitiesMoreThan(decimal quantity)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.Quantity) > quantity);

        return this;
    }
}

客戶的銷售情況,金額和數量

var customerRepository = new Repository<Customer>(this);

var query1 = new CustomerLogisticsQuery()
    .LivesInCity("London");

var query2 = new CustomerSalesQuery()
    .WithPurchasesMoreThan(100)
    .WithQuantitiesMoreThan(10);

customerRepository
    .Query(query1.And(query2))
    .Select()
    .Dump();

復用上面的定義的查詢方法

以上這些都是改項目提供的方法,非常的好用

 

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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