長江EV:要做完美的中國智慧電動汽車

4月17日上午,長江汽車集團董事長曹忠正式發佈了電動汽車品牌“長江EV”,同時,國內規模最大、自動化程度最高、專門生產電動汽車的核心工廠——杭州長江汽車有限公司正式投產,“長江牌”純電動中巴車“奕閣”、純電動商務車 “奕勝”、純電動公車“益眾”和純電動小型SUV“逸酷”,作為首批投產的產品也正式下線。

六年來,長江汽車進行了向電動汽車發展的全面戰略規劃,建立了完整的系統的電動汽車研發能力,完成了構成電動汽車所有核心零部件的研發和集成,完成了正向開發三個系列電動汽車產品。

2個研發中心(簡式國際汽車設計(北京)有限公司、上海中聚電池研究院),2個鋰電池生產工廠(天津中聚新能源科技有限公司、吉林中聚新能源科技有限公司),2個整車生產基地(杭州長江汽車有限公司、雲南五龍汽車有限公司)。

展望未來,從產業和市場的角度來看,傳統汽車和電動車會呈現此消彼長的發展態勢,大體可分為四個階段:

第一階段是電動汽車占汽車市場的份額約在5%以下。

第二階段是電動汽車占汽車市場的份額約在5-15%。

第三階段是電動汽車占汽車市場的份額約在15-30%。

第四階段是電動汽車在有機融合智慧化的基礎上逐步完美而一躍超過傳統汽車,占汽車市場的份額50%以上。

此次長江汽車下線儀式暨品牌發佈會上,長江汽車還展示了其車聯網、車載資訊系統的最新科研成果。各系列產品都將智慧終端機和車載資訊服務系統作為標配,實現車與移動智慧設備的無縫對接,滿足人與車、車與路、車與網的資訊交互和解決方案。

 

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

福特電動休旅車野馬 Mach-E 接近實車照首次曝光

福特曾表示野馬 Mach-E 將在 2020 秋天開始組裝,但最近在福特墨西哥廠外卻出現一批電動野馬的身影,且看起來像幾乎可以交車了。一起來先睹為快這批電動野馬的實車英姿吧。

野馬 Mach-E(馬赫 E)是福特首輛純電車,不僅擁有全新心臟,還承繼了傳奇車款野馬的設計靈魂,因此開放預購以來業績奇佳,連一向高傲的特斯拉老闆馬斯克(Elon Musk)都發文恭喜福特,做出一台好車。

不過第一台電動車製造並沒那麼容易,因此福特將交車日期估得很晚,最快要到 2020 年底才交車,而頂規版的 Mach-E GT 更是要等到 2021 年第一季才會交車。此外,福特也說過,正式生產要到 2020 年秋天才會啟動,請大家不要太著急。

上週在墨西哥廠外出現的這批 Mach-E 卻讓大家嚇了一跳,因為看起來不僅比之前合成圖帥氣,且幾乎就像已完成組裝,而不是一般原型車或測試車。

無論原因是什麼,這台 Mach-E 是目前為止最接近實車的照片,這個造型是否打動了你的心?

今年底將交車的 Mach-E,續航里程預估有 480 公里,並擁有 332 馬力,是目前市場少數能與特斯拉並肩的數據,定價也只有 5 萬美元(台灣售價未定)。

Mach-E 售價與性能幾乎都衝著特斯拉 Model Y 而來,不過 Model Y 的交車日期可能就在未來兩個月開始,相信所有人(包括福特)都期望 Mach-E 也能提前交車,與 Model Y 一較高下。

(合作媒體:。圖片來源:)

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

【其他文章推薦】

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

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

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

南投搬家前需注意的眉眉角角,別等搬了再說!

計算機硬件—調度與死鎖

進程調度原因及調度切換時機,進程調度方式與實現及各種調度算法的個人總結:

1.一般調度概念 1)什麼是調度 就是選出待分派的作業或進程。 操作系統管理了系統的有限資源,當有多個進程(或作業)發出請求要使用這些資源時,因為資源的有限性,必須按照一定的原則選擇進程(或作業)來佔用資源。這就是調度。

2)調度目的 1.控制資源使用者的數量 2.選取資源使用者 3.許可哪些使用者佔用資源 4.讓使用者直接佔用資源。

二、調度類型 1、高級調度(長程調度)。 即作業調度,選取輸入井中的作業,生成根進程。目的是控制使用系統資源的進程數。

2、中級調度(中程調度)。 指選取進程佔用內存或有資格佔用內存,又稱進程滾入滾出,中級調度將在存儲管理章節中介紹,有頁式調度、段式調度、段頁式調度等。

3、低級調度(短程調度) 指選取進程佔用處理機,又稱進程調度。 4、I/O調度 選取進程佔用I/O設備。

三、調度和狀態轉換 在許多系統中,調度被分為三種:長程、中程和短程。 1)調度和進程狀態轉換

 

從狀態轉換的觀點: 1、長程調度(作業調度)就是將一個或一批作業從後備狀態變為運行狀態。一個作業一旦被高級調度選中,便可獲得所需要的基本內存和設備資源,並被裝入內存,此後就以進程形式參与併發執行,與其它進程競爭CPU。

從狀態轉換的觀點: 2、中程調度就是將進程從活動態變為靜止的掛起態,或者將進程從掛起態變為就緒或阻塞態。 3、短程調度就是將某個進程從就緒態變為(在CPU上運行的)執行態。

2)調度的層次(調度作用的嵌套關係)

 

 

 

3)三級調度示意圖 調度從根本上講,是要使隊列延遲的時間最小,並優化系統的執行效率

 

 

 

 

 

 

 

4.1.2作業調度的功能

①記錄系統中各個作業的情況。 ②按照某種調度算法從後備作業隊列中挑選作業,即決定接納多少個作業進入內存和挑選哪些作業進入內存。

③為選中的作業分配內存和外設等資源。 ④為選中的作業建立相應的進程,並把該進程放入就緒隊列中。 ⑤作業結束後進行善後處理工作。如輸出必要的信息,收回該作業所佔用的全部資源,撤消與該作業相關的全部進程和該作業的JCB 。

 

4.1.3進程調度的功能與調度時機 一、進程調度的功能 (1)保存現場(2)挑選進程(3)恢復現場

 

 

 

二、進程調度的時機 一般在下列事件發生后要執行進程調度。 (1)創建進程。 當進程創建時,要決定是運行父進程還是子進程。 (2)進程終止。 (3)等待事件。 (4)中斷髮生。 (5)運行到時。

4.1.4進程調度的基本方式 1)非剝奪調度(非搶佔) 只有當處理機上的進程主動放棄處理機時,才重新調度。 2)剝奪調度(搶佔) 當進程運行時可以被系統以某種原則為由剝奪其處理機。

例:只有一部電話機,小李正在通電話,此時小張有急事也要打電話,此時小張的調度方式有兩種: 一種是非搶佔,即等待小李打完電話,自己再打電話。 另一種是搶佔,不等小李通完電話,搶過話筒就撥打電話。

3)進程調度在核心態進行。 CPU狀態有兩種,目態和管態。 管態——也稱核心態或系統態,系統程序在CPU上運行的狀態。 目態——用戶程序在CPU上運行的狀態。

 

 

 

4.2 調度算法 4.2.1 常用的調度算法

1. FCFS 誰先到就緒隊列就將處理機分給誰。 2. 短作業優先 取一個下次所需運行時間最短的作業(該算法能使平均等待時間最短)。

3. 優先級調度 選優先級最高的進程佔用處理機(優先級可動態改變)。 4.輪轉調度法 以先來後到的次序+ 時間片輪轉。

5.多隊列調度法 按屬性將就緒進程分類,不同類進程可有不同的調度算法。 6.多級反饋隊列調度法 設置多條就緒隊列,進程被調度執行后,在被剝奪或放棄處理機后而在就緒時,可以改變其就緒隊列(見下圖)。

 

 

又一個多級反饋隊列調度算法的例子:使用優先權實現調度。做法如下:

(1)以優先級設置多隊列。 (2)各隊列的調度算法採用FCFS+時間片輪轉. (3)進程優先級升降原則是:等待過久升,輸入/輸出完成時升,運行完一個完整時間片降……

(4)進程最初進入就緒隊列以用戶初置優先級為參數。 (5)開始調度時,先從最高優先權的就緒隊列(RQ0)開始選取一個進程,如果RQ0空,則檢查RQ1,如此下去。見下圖示:

 

 

4.2.2 調度策略的討論

一、調度性能評價準則 1、CPU利用率。 2、吞吐率。即每單位時間所完成的作業數目。 3、周轉時間。即從作業提交直至完成這一作業的時間間隔。

4、等待時間。即作業在就緒隊列中等待所花的時間。 5、響應時間。即從作業提交到首次產生回答信息之間的時間。

二、主要的調度算法 下面以表所示的進程集作為範例討論不同的調度策略。 到達時間:進程或作業提交時間。 服務時間:進程要求服務的時間,或完成作業所需的時間。

 

一、FCFS調度策略(或先進先出)

 

 

 

 

 

 

 

FCFS策略更適合於長進程。例:

 

 

注:周轉時間=完成時間—到達時間 tq/ts :帶權周轉時間=周轉時間/服務時間

(1)進程C的等待標準化時間是不能容忍的。它位於系統的總時間是它所需服務時間的100倍。無論何時,一個長進程到后,一個短進程就會出現較長的等待。 (2)進程D的標準化等待時間尚可容忍,小於2.0。進程D的輪轉時間差不多是C的兩倍。

總結: FCFS(First Come First Served)即先來先服務,故它的本質是非搶佔的。它簡單易行,但調度性能較差,有可能使短的、重要的或緊迫的作業等進程長期等待,其實現過程容易,可採用FIFO隊列管理。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

深入理解@LoadBalanced註解的實現原理與客戶端負載均衡

前提

在閱讀這篇博客之前,希望你對SpringCloud套件熟悉和理解,更希望關注下

概述

在使用springcloud ribbon客戶端負載均衡的時候,可以給RestTemplate bean 加一個@LoadBalanced註解,就能讓這個RestTemplate在請求時擁有客戶端負載均衡的能力,先前有細嚼過但是沒有做過筆記,剛好處理此類問題記錄下

@LoadBalanced

/**
 * 註釋將RestTemplate bean標記為配置為使用LoadBalancerClient。
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

通過源碼可以發現這是一個LoadBalanced標記註解並且標記了@Qualifier(基於Spring Boot的自動配置機制),我們可以溯源到LoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration

/**
 * 功能區的自動配置(客戶端負載平衡)
 */
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();   //這裏持有@LoadBalanced標記的RestTemplate實例

    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
          //為restTemplate添加定製
                    customizer.customize(restTemplate);
                }
            }
        });
    }

   // ... 

    /**
     * 以下針對classpath存在RetryTemplate.class的情況配置,先忽略
     */
    @Configuration
    @ConditionalOnClass(RetryTemplate.class)
    public static class RetryAutoConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryFactory() {
            return new LoadBalancedRetryFactory() {
            };
        }
    }

  // ... 
}

@LoadBalanced@Autowried結合使用,意思就是這裏注入的RestTempate Bean是所有加有@LoadBalanced註解標記的(持有@LoadBalanced標記的RestTemplate實例)

這段自動裝配的代碼的含義不難理解,就是利用了RestTempllate的攔截器,使用RestTemplateCustomizer對所有標註了@LoadBalanced的RestTemplate Bean添加了一個LoadBalancerInterceptor攔截器,而這個攔截器的作用就是對請求的URI進行轉換獲取到具體應該請求哪個服務實例ServiceInstance。

關鍵問下自己:為什麼?

  • RestTemplate實例是怎麼被收集的?
  • 怎樣通過負載均衡規則獲取具體的具體的server?

繼續扒看源碼>
上面可以看出,會LoadBalancerAutoConfiguration類對我們加上@LoadBalanced註解的bean 添加loadBalancerInterceptor攔截器

LoadBalancerInterceptor

/**
* 功能區的自動配置(客戶端負載平衡)。
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

    private LoadBalancerClient loadBalancer;

    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
            LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        // for backwards compatibility
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    @Override
    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
            final ClientHttpRequestExecution execution) throws IOException {
        final URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null,
                "Request URI does not contain a valid hostname: " + originalUri);
        return this.loadBalancer.execute(serviceName,
                this.requestFactory.createRequest(request, body, execution));
    }

}

重點看intercept方法 當我們restTemplate執行請求操作時,就會被攔截器攔截進入intercept方法,而loadBalancer是LoadBalancerClient的具體實現

RibbonLoadBalancerClient

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
            throws IOException {
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        Server server = getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        }
        RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                isSecure(server, serviceId),
                serverIntrospector(serviceId).getMetadata(server));

        return execute(serviceId, ribbonServer, request);
    }

看到這裏相信都遇到過類似的錯誤,恍然大悟

No instances available for  xxxxx

總結

  • 1.根據serviceId 獲取對應的loadBalancer
  • 2.根據loadBalancer獲取具體的server(這裏根據負載均衡規則,獲取到具體的服務實例)
  • 3.創建RibbonServer
  • 4.執行具體請求

這裏

注意: @LoadBalanced 標記註解獲取到最後通過負載均衡規則獲取具體的具體的server來發起請求

案例

/**
 * 服務註冊中心配置
 *
 * @author <a href="mailto:shangzhi.ibyte@gmail.com">iByte</a>
 * @since 1.0.1
 */
@Configuration
@EnableConfigurationProperties(ModuleMappingHelper.class)
public class DiscoveryConfig {
    @Autowired
    Environment environment;

    /**
     * DiscoveryHeaderHelper默認bean
     * @return
     */
    @Bean
    public DiscoveryHeaderHelper discoveryHeaderHelper() {
        DiscoveryHeaderHelper discoveryHeaderHelper = new DiscoveryHeaderHelper(environment);
        DiscoveryHeaderHelper.INSTANCE = discoveryHeaderHelper;
        return discoveryHeaderHelper;
    }

    /**
     * resttemplate構建
     */
    @Resource
    private RestTemplateBuilder restTemplateBuilder;

    /**
     * resttemplate請求bean,更改系統本身的builder
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = restTemplateBuilder.configure(new RestTemplate());
        //RestTemplate interceptors 遠程調用請求增加頭部信息處理
        restTemplate.getInterceptors().add(new RestApiHeaderInterceptor());
        //RestTemplate Set the error handler 錯誤處理
        restTemplate.setErrorHandler(new RestResponseErrorHandler());
        return  restTemplate;
    }

    @Bean
    public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
        DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
        discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new DiscoveryHeaderClientFilter()));
        discoveryClientOptionalArgs.setEventListeners(Collections.singleton(new EurekaClientEventListener()));
        return discoveryClientOptionalArgs;
    }
}

源碼地址 >

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

【其他文章推薦】

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

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

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

南投搬家前需注意的眉眉角角,別等搬了再說!

高性能Web動畫和渲染原理系列(4)“Compositor-Pipeline演講PPT”學習摘要

目錄

示例代碼託管在:

博客園地址:

華為雲社區地址:

附件PPT來自開發文檔。術語里的cc指的是Chromium Compositor

一直以來都想了解瀏覽器合成層的運作機制,但是相關的中文資料大多比較關注框架和開發技術,這方面的資料實在是太少了,後來在chromium官方網站的文檔里找到了項目組成員malaykeshav在 2019年4月的一份關於瀏覽器合成流水線的演講PPT,個人感覺裏面講的非常清楚了,由於沒有找到視頻,有些部分只能自行理解,本文僅對關鍵信息做一些筆記,對此感興趣的讀者可以在文章開頭的github倉庫或附件中拿到這個PPT自行學習。

摘要

1.合成流水線

合成流水線,就是指瀏覽器處理合成層的工作流程,其基本步驟如下:

大致的流程就是說Paint環節會生成一個列表,列表裡登記了頁面元素的繪製指令,接着這個列表需要經過Raster光柵化處理,並在合成幀中處理紋理,最後的Draw環節才是將這些紋理圖展示在瀏覽器內容區。

2. 預定義UI層

chromium中預定義了一些指定類型的UI層,大致分為:

  • Not Drawn – 為了處理透明度或濾鏡效果、transform變形或者clip剪裁的非繪製層
  • Solid color layer – 固有顏色層
  • Painted texture layer – Texture紋理會在這個層執行paint渲染和後續的rasterized光柵化任務
  • Transferable resource layer – 共享資源層,可能是GPU裏面的Texture紋理也可能未來會發給GPU的位圖
  • Surface layer – 臨時佔位層,因為自頂向下遍歷layer樹時子樹都還沒處理,需要先佔位最後再填充
  • Nine patch layer – 用於實現陰影的層

3. paint是什麼意思

每個層layer是由若干個views組成的,所謂paint,就是每個views將自己對應圖形的繪製指令添加到層的可展示元素列表Display Item List里,這個列表會被添加到一個延遲執行的光柵化任務中,並最終生成當前層的texture紋理(可以理解為當前層的繪製結果),考慮到傳輸性能以及未來增量更新的需求,光柵化的結果會以tiles瓦片形式保存。在chrome中也可以看到頁面瓦片化拆分的結果:

4. 分層的優勢和劣勢

分層的優勢和劣勢也在此進行了說明,和之前我們主動思考的答案基本一致(暗爽一下)。

5. 視圖屬性及其處理方式

views中支持的屬性包含Clip剪裁,transform變換,effect效果(如半透明或濾鏡等),mask遮罩,通常按照後序遍歷的方式自底向上進行遍歷處理。

clip剪裁的處理方式是在父節點和子節點之間插入一個剪裁層,用來將其子樹的渲染結果剪裁到限定的範圍內,然後再向上與父級進行合併;

transform變換直接作用於父節點,處理到這個節點時其子樹都已經處理完畢,直接將整體應用變形即可;

effect效果一般直接作用於當前處理的節點,有時也會產生交叉依賴的場景;

PPT第40頁中在介紹effect效果處理時描述了兩種不同的透明度處理需求,從而引出了一個Render Surface的概念,它相當於一個臨時的層,它的子樹需要先繪製在這個層上,然後再向上與父節點進行合併,屏幕就是是根級的Render Surface

6. Quads

Layer遍歷處理輸出的結果被稱為Quads(從意思上理解好像就是指輸出了很多個矩形方塊),每個quad都持有它被繪製到目標緩衝區所需要的資源,根據它持有的資源不同可以分為:

  • Solid Color-固定顏色型
  • Texture– 紋理型
  • Tile– 瓦片型
  • Surface– 臨時繪圖表面型
  • Video – 視頻幀型
  • Render PassRender Surface類型的佔位區,Render Surface子樹處理完后填充到關聯的Render Pass

7. Compositor Frame

合成層真正的工作要開始了,主角概念Compositor Frame(合成幀)登場,它負責將quads合併繪製在一起,膠片里59-62頁非常清楚地展示了合成的過程,最終輸出的結果就是根節點的紋理。

chromium是多進程架構,Browser Process瀏覽器進程會對菜單欄等等容器部分的畫面生成合成幀來輸出,每個網頁的Render Process渲染進程會對頁面內容生成合成幀來輸出,最終的結果都被共享給GPU ProcessGPU進程進行聚合併生成最終完整的合成表面,接着在Display Compositor環節將最後的位圖展示在屏幕上。

8. 關於光柵化以及渲染方式

膠片里並沒有描述具體的光柵化的處理過程,但是layer輸出的quads看起來應該是光柵化以後的結果,推測應該是處理Display Item List中的繪圖指令時也和WebGL類似,經過頂點着色器片元着色器的遍歷式處理機制,並在過程中自動完成像素插值。

9.【重要】軟件渲染和硬件渲染的區別

聲明:本節內容是個人理解,僅用作技術交流,不保證對!

軟件渲染和硬件渲染的區別對筆者而言一直非常抽象,只是知道基本概念。後來在(國內可能無法訪問)中《Compositor Thread Architecture》這篇合成器線程架構的文章中找到了一些相關描述,也解開了筆者心中一直以來的疑惑,相關部分摘抄如下:

Texture Upload

One challenge with all these textures is that we rasterize them on the main thread of the renderer process, but need to actually get them into the GPU memory. This requires handing information about these textures (and their contents) to the impl thread, then to the GPU process, and once there, into the GL/D3D driver. Done naively, this causes us to copy a single texture over and over again, something we definitely don’t want to do.

We have two tricks that we use right now to make this a bit faster. To understand them, an aside on “painting” versus “rasterization.”

  • Painting is the word we use for telling webkit to dump a part of its RenderObject tree to a GraphicsContext. We can pass the painting routine a GraphicsContext implementation that executes the commands as it receives them, or we can pass it a recording context that simply writes down the commands as it receives them.
  • Rasterization is the word we use for actually executing graphics context commands. We typically execute the rasterization commands with the CPU (software rendering) but could also execute them directly with the GPU using Ganesh.
  • Upload: this is us actually taking the contents of a rasterized bitmap in main memory and sending it to the GPU as a texture.With these definitions in mind, we deal with texture upload with the following tricks:
  • Per-tile painting: we pass WebKit paint a recording context that simply records the GraphicsContext operations into an SkPicture data structure. We can then rasterize several texture tiles from that one picture.
  • SHM upload: instead of rasterizing into a void* from the renderer heap, we allocate a shared memory buffer and upload into that instead. The GPU process then issues its glTex* operations using that shared memory, avoiding one texture copy.The holy grail of texture upload is “zero copy” upload. With such a scheme, we manage to get a raw pointer inside the renderer process’ sandbox to GPU memory, which we software-rasterize directly into. We can’t yet do this anywhere, but it is something we fantasize about.

大概翻譯一下,方便英語水平一般的小夥伴理解,GPU處理圖片的方式是按照Texture進行貼圖的,對此不熟悉的小夥伴可以查看筆者以前發的有關Three.js相關的博文。

紋理上傳:
處理紋理的挑戰之一就是它是在渲染進程(可以理解為單個Tab網頁的進程)的主線程里進行的,但是最終需要將其放入GPU內存。這就需要將紋理數據遞交給合成器線程,然後再交給GPU進程(Chromium架構里有專門的GPU進程用來專門處理和GPU之間的協作任務),最後再傳遞給底層的Direct3DOpenGL(也就是圖形學的底層技術),如果只是按照常規流程來處理,就會需要一次又一次來複制生成的紋理數據,這顯然不是我們想要的。
我們現在使用了兩個小方法來使這個流程變得快一點。它們分別作用於painting(繪製)和rasterization(光柵化)兩個階段。

  • 1號知識點!!!Painting我們用來告訴webkit為RenderObject Tree的來生成對應的GraphicsContext。通過給painting routine(繪製流程)傳遞一個GraphicsContext的具體實現來執行這些已經編排好的繪製命令,也可以傳遞一個record context(記錄上下文)只是簡單地把繪圖命令都記錄下來。
  • 2號知識點!!!Rasterization(光柵化)是指Graphics context關聯的繪圖命令實際被執行的過程。通常我們使用CPU(也就是軟件渲染的方式)來執行光柵化任務,也可以直接使用GPU來渲染(也就是硬件渲染的方式)。
  • 上傳:指在主線程存儲區獲取到光柵化以後的位圖內容然後將它作為紋理上傳給GPU的過程,考慮到上述已經提及的定義,上傳過程是如下來處理的:
    • 瓦片繪製:我們在webkit中使用recording context來簡單地記錄Graphics Context的操作指令,將它存儲為SkPicture類型(直接使用軟件光柵化時生成的是SkBitmap類型),隨後可以從一張picture裏面光柵化處理得到多個紋理瓦片
    • 共享內存:在軟件渲染的方式中,光柵化的結果會被存儲在renderer進程的堆內存里,現在不這樣搞了,我們重新分配了一塊共享緩衝區,然後通過它來傳遞相關對象,GPU進程隨後在獲取紋理時直接從共享內存中獲取就行了,這樣就避免了數據的拷貝。
      總的來說,紋理上傳的過程幾乎是零拷貝的。利用這樣的結構,我們在renderer進程(也就是網頁的渲染進程)的沙箱環境內也可以獲取到指向GPU 內存的指針,而在軟件光柵化的過程中,是直接將位圖結果放在這裏的。
  • Painting: this is the process of asking Layers for their content. This is where we ask webkit to tell us what is on a layer. We might then rasterize that content into a bitmap using software, or we might do something fancier. Painting is a main thread operation.
  • Drawing: this is the process of taking the layer tree and smashing it together with OpenGL onto the screen. Drawing is an impl-thread operation.
  • painting:表示的過程是向Layers對象查詢層內容,也就是讓webkit告訴我們每一層上面到底有什麼。接下來我們就可以使用軟件光柵化的方式將這些內容處理為位圖,也可以做一些更牛的事情,painting是一個主線程行為。
  • drawing:是指將Layer中的內容用OpenGL繪製在屏幕上的過程,它是另一個線程中的操作。

概念比較多沒有基礎的讀者可能理解起來有難度,我嘗試用自己的話複述一下:

【軟件渲染】的模式下,在paint時會直接利用Graphics Context繪圖上下文將結果繪製出來,在一個SkBitmap實例中保存為位圖信息;【硬件渲染】的模式下,在paint時傳入一個SkPicture實例,將需要執行的繪圖命令保存在裏面先不執行,然後通過共享內存將它傳給GPU進程,藉助GPU來最終去執行繪圖命令,生成多個瓦片化的位圖紋理結果(OpenGL中頂點着色器向片元着色器傳遞數據時可以自動進行數據插值,完成光柵化的任務)。 純軟件渲染里嚴格說是沒有合成層概念的,因為最終輸出的只有一張位圖,按照順序從下往上畫,和畫到一個新層上再把新層貼到已有結果上其實是一樣的。

不管使用哪種途徑,paint動作都是得到位圖數據,而最終的draw這個動作是藉助OpenGL和位圖數據最終把圖形显示在显示器上。

所以【硬件渲染】就是渲染進程把要做的事情和需要的數據都寫好,然後打包遞給GPU讓它去幹活。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

高性能Web動畫和渲染原理系列(4)“Compositor-Pipeline演講PPT”學習摘要

目錄

示例代碼託管在:

博客園地址:

華為雲社區地址:

附件PPT來自開發文檔。術語里的cc指的是Chromium Compositor

一直以來都想了解瀏覽器合成層的運作機制,但是相關的中文資料大多比較關注框架和開發技術,這方面的資料實在是太少了,後來在chromium官方網站的文檔里找到了項目組成員malaykeshav在 2019年4月的一份關於瀏覽器合成流水線的演講PPT,個人感覺裏面講的非常清楚了,由於沒有找到視頻,有些部分只能自行理解,本文僅對關鍵信息做一些筆記,對此感興趣的讀者可以在文章開頭的github倉庫或附件中拿到這個PPT自行學習。

摘要

1.合成流水線

合成流水線,就是指瀏覽器處理合成層的工作流程,其基本步驟如下:

大致的流程就是說Paint環節會生成一個列表,列表裡登記了頁面元素的繪製指令,接着這個列表需要經過Raster光柵化處理,並在合成幀中處理紋理,最後的Draw環節才是將這些紋理圖展示在瀏覽器內容區。

2. 預定義UI層

chromium中預定義了一些指定類型的UI層,大致分為:

  • Not Drawn – 為了處理透明度或濾鏡效果、transform變形或者clip剪裁的非繪製層
  • Solid color layer – 固有顏色層
  • Painted texture layer – Texture紋理會在這個層執行paint渲染和後續的rasterized光柵化任務
  • Transferable resource layer – 共享資源層,可能是GPU裏面的Texture紋理也可能未來會發給GPU的位圖
  • Surface layer – 臨時佔位層,因為自頂向下遍歷layer樹時子樹都還沒處理,需要先佔位最後再填充
  • Nine patch layer – 用於實現陰影的層

3. paint是什麼意思

每個層layer是由若干個views組成的,所謂paint,就是每個views將自己對應圖形的繪製指令添加到層的可展示元素列表Display Item List里,這個列表會被添加到一個延遲執行的光柵化任務中,並最終生成當前層的texture紋理(可以理解為當前層的繪製結果),考慮到傳輸性能以及未來增量更新的需求,光柵化的結果會以tiles瓦片形式保存。在chrome中也可以看到頁面瓦片化拆分的結果:

4. 分層的優勢和劣勢

分層的優勢和劣勢也在此進行了說明,和之前我們主動思考的答案基本一致(暗爽一下)。

5. 視圖屬性及其處理方式

views中支持的屬性包含Clip剪裁,transform變換,effect效果(如半透明或濾鏡等),mask遮罩,通常按照後序遍歷的方式自底向上進行遍歷處理。

clip剪裁的處理方式是在父節點和子節點之間插入一個剪裁層,用來將其子樹的渲染結果剪裁到限定的範圍內,然後再向上與父級進行合併;

transform變換直接作用於父節點,處理到這個節點時其子樹都已經處理完畢,直接將整體應用變形即可;

effect效果一般直接作用於當前處理的節點,有時也會產生交叉依賴的場景;

PPT第40頁中在介紹effect效果處理時描述了兩種不同的透明度處理需求,從而引出了一個Render Surface的概念,它相當於一個臨時的層,它的子樹需要先繪製在這個層上,然後再向上與父節點進行合併,屏幕就是是根級的Render Surface

6. Quads

Layer遍歷處理輸出的結果被稱為Quads(從意思上理解好像就是指輸出了很多個矩形方塊),每個quad都持有它被繪製到目標緩衝區所需要的資源,根據它持有的資源不同可以分為:

  • Solid Color-固定顏色型
  • Texture– 紋理型
  • Tile– 瓦片型
  • Surface– 臨時繪圖表面型
  • Video – 視頻幀型
  • Render PassRender Surface類型的佔位區,Render Surface子樹處理完后填充到關聯的Render Pass

7. Compositor Frame

合成層真正的工作要開始了,主角概念Compositor Frame(合成幀)登場,它負責將quads合併繪製在一起,膠片里59-62頁非常清楚地展示了合成的過程,最終輸出的結果就是根節點的紋理。

chromium是多進程架構,Browser Process瀏覽器進程會對菜單欄等等容器部分的畫面生成合成幀來輸出,每個網頁的Render Process渲染進程會對頁面內容生成合成幀來輸出,最終的結果都被共享給GPU ProcessGPU進程進行聚合併生成最終完整的合成表面,接着在Display Compositor環節將最後的位圖展示在屏幕上。

8. 關於光柵化以及渲染方式

膠片里並沒有描述具體的光柵化的處理過程,但是layer輸出的quads看起來應該是光柵化以後的結果,推測應該是處理Display Item List中的繪圖指令時也和WebGL類似,經過頂點着色器片元着色器的遍歷式處理機制,並在過程中自動完成像素插值。

9.【重要】軟件渲染和硬件渲染的區別

聲明:本節內容是個人理解,僅用作技術交流,不保證對!

軟件渲染和硬件渲染的區別對筆者而言一直非常抽象,只是知道基本概念。後來在(國內可能無法訪問)中《Compositor Thread Architecture》這篇合成器線程架構的文章中找到了一些相關描述,也解開了筆者心中一直以來的疑惑,相關部分摘抄如下:

Texture Upload

One challenge with all these textures is that we rasterize them on the main thread of the renderer process, but need to actually get them into the GPU memory. This requires handing information about these textures (and their contents) to the impl thread, then to the GPU process, and once there, into the GL/D3D driver. Done naively, this causes us to copy a single texture over and over again, something we definitely don’t want to do.

We have two tricks that we use right now to make this a bit faster. To understand them, an aside on “painting” versus “rasterization.”

  • Painting is the word we use for telling webkit to dump a part of its RenderObject tree to a GraphicsContext. We can pass the painting routine a GraphicsContext implementation that executes the commands as it receives them, or we can pass it a recording context that simply writes down the commands as it receives them.
  • Rasterization is the word we use for actually executing graphics context commands. We typically execute the rasterization commands with the CPU (software rendering) but could also execute them directly with the GPU using Ganesh.
  • Upload: this is us actually taking the contents of a rasterized bitmap in main memory and sending it to the GPU as a texture.With these definitions in mind, we deal with texture upload with the following tricks:
  • Per-tile painting: we pass WebKit paint a recording context that simply records the GraphicsContext operations into an SkPicture data structure. We can then rasterize several texture tiles from that one picture.
  • SHM upload: instead of rasterizing into a void* from the renderer heap, we allocate a shared memory buffer and upload into that instead. The GPU process then issues its glTex* operations using that shared memory, avoiding one texture copy.The holy grail of texture upload is “zero copy” upload. With such a scheme, we manage to get a raw pointer inside the renderer process’ sandbox to GPU memory, which we software-rasterize directly into. We can’t yet do this anywhere, but it is something we fantasize about.

大概翻譯一下,方便英語水平一般的小夥伴理解,GPU處理圖片的方式是按照Texture進行貼圖的,對此不熟悉的小夥伴可以查看筆者以前發的有關Three.js相關的博文。

紋理上傳:
處理紋理的挑戰之一就是它是在渲染進程(可以理解為單個Tab網頁的進程)的主線程里進行的,但是最終需要將其放入GPU內存。這就需要將紋理數據遞交給合成器線程,然後再交給GPU進程(Chromium架構里有專門的GPU進程用來專門處理和GPU之間的協作任務),最後再傳遞給底層的Direct3DOpenGL(也就是圖形學的底層技術),如果只是按照常規流程來處理,就會需要一次又一次來複制生成的紋理數據,這顯然不是我們想要的。
我們現在使用了兩個小方法來使這個流程變得快一點。它們分別作用於painting(繪製)和rasterization(光柵化)兩個階段。

  • 1號知識點!!!Painting我們用來告訴webkit為RenderObject Tree的來生成對應的GraphicsContext。通過給painting routine(繪製流程)傳遞一個GraphicsContext的具體實現來執行這些已經編排好的繪製命令,也可以傳遞一個record context(記錄上下文)只是簡單地把繪圖命令都記錄下來。
  • 2號知識點!!!Rasterization(光柵化)是指Graphics context關聯的繪圖命令實際被執行的過程。通常我們使用CPU(也就是軟件渲染的方式)來執行光柵化任務,也可以直接使用GPU來渲染(也就是硬件渲染的方式)。
  • 上傳:指在主線程存儲區獲取到光柵化以後的位圖內容然後將它作為紋理上傳給GPU的過程,考慮到上述已經提及的定義,上傳過程是如下來處理的:
    • 瓦片繪製:我們在webkit中使用recording context來簡單地記錄Graphics Context的操作指令,將它存儲為SkPicture類型(直接使用軟件光柵化時生成的是SkBitmap類型),隨後可以從一張picture裏面光柵化處理得到多個紋理瓦片
    • 共享內存:在軟件渲染的方式中,光柵化的結果會被存儲在renderer進程的堆內存里,現在不這樣搞了,我們重新分配了一塊共享緩衝區,然後通過它來傳遞相關對象,GPU進程隨後在獲取紋理時直接從共享內存中獲取就行了,這樣就避免了數據的拷貝。
      總的來說,紋理上傳的過程幾乎是零拷貝的。利用這樣的結構,我們在renderer進程(也就是網頁的渲染進程)的沙箱環境內也可以獲取到指向GPU 內存的指針,而在軟件光柵化的過程中,是直接將位圖結果放在這裏的。
  • Painting: this is the process of asking Layers for their content. This is where we ask webkit to tell us what is on a layer. We might then rasterize that content into a bitmap using software, or we might do something fancier. Painting is a main thread operation.
  • Drawing: this is the process of taking the layer tree and smashing it together with OpenGL onto the screen. Drawing is an impl-thread operation.
  • painting:表示的過程是向Layers對象查詢層內容,也就是讓webkit告訴我們每一層上面到底有什麼。接下來我們就可以使用軟件光柵化的方式將這些內容處理為位圖,也可以做一些更牛的事情,painting是一個主線程行為。
  • drawing:是指將Layer中的內容用OpenGL繪製在屏幕上的過程,它是另一個線程中的操作。

概念比較多沒有基礎的讀者可能理解起來有難度,我嘗試用自己的話複述一下:

【軟件渲染】的模式下,在paint時會直接利用Graphics Context繪圖上下文將結果繪製出來,在一個SkBitmap實例中保存為位圖信息;【硬件渲染】的模式下,在paint時傳入一個SkPicture實例,將需要執行的繪圖命令保存在裏面先不執行,然後通過共享內存將它傳給GPU進程,藉助GPU來最終去執行繪圖命令,生成多個瓦片化的位圖紋理結果(OpenGL中頂點着色器向片元着色器傳遞數據時可以自動進行數據插值,完成光柵化的任務)。 純軟件渲染里嚴格說是沒有合成層概念的,因為最終輸出的只有一張位圖,按照順序從下往上畫,和畫到一個新層上再把新層貼到已有結果上其實是一樣的。

不管使用哪種途徑,paint動作都是得到位圖數據,而最終的draw這個動作是藉助OpenGL和位圖數據最終把圖形显示在显示器上。

所以【硬件渲染】就是渲染進程把要做的事情和需要的數據都寫好,然後打包遞給GPU讓它去幹活。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

【故障公告】新版博客後台部署時的配置問題引發故障

最近,我們對新版博客後台(Angular 8.2.7 + .NET Core 3.0)進行了灰度發布,如果您訪問博客後台時跳轉到 ,說明使用的就是新版博客後台。

今天我們在一次基於 gitlab-ci 的自動化發布過程中,由於操作問題在發布前沒有對 appsettings.Production.json 的修改進行保存,造成容器在啟動時使用了舊版的配置文件,再加上容器的健康檢查不能檢查出這種不正常情況(這個地方的改進還沒完成),最不該的是在發布后沒有對關鍵功能進行測試驗證以及值班人員沒有及時處理用戶反饋,從而造成 18:22~19:27 期間使用新版博客后的用戶無法正常發布博文,非常抱歉由此給您帶來了麻煩,請您諒解。

我們會吸取教訓,並採取以下改進措施:

  • 更高優先級改進健康檢查。一是容器的健康檢查,二是阿里云云監控的健康檢查。當關鍵功能不可用時,讓健康檢查失敗(之前的健康檢查沒有對業務功能進行檢查)。這樣發布時如果出現問題,容器健康檢查失敗,docker swarm 就不會部署新容器。當正在運行的容器出現問題影響關鍵功能的使用時及時報警。
  • 盡可能實現在生產環境發布後用“機器人”對關鍵功能進行測試驗證。
  • 每次自動化發布時在值班群發消息通知值班人員留意用戶反饋。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

Abp vNext 自定義 Ef Core 倉儲引發異常

問題

在使用自定義 Ef Core 倉儲和 ABP vNext 注入的默認倉儲時,通過兩個 Repository 進行 Join 操作,提示 Cannot use multiple DbContext instances within a single query execution. Ensure the query uses a single context instance. 。這個異常信息翻譯成中文的大概意思就是,你不能使用兩個 DbContext 裏面的 DbSet 進行 Join 查詢。

如果將自定義倉儲改為 IRepository<TEntity,TKey> 進行注入,是可以與 _courseRepostory 進行關聯查詢的。

我在 XXXEntityFrameworkCoreModule 的配置,以及自定義倉儲 EfCoreStudentRepository 代碼如下。

XXXEntityFrameworkCoreModule 代碼:

public class XXXEntityFrameworkCoreModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<XXXDbContext>(op =>
        {
            op.AddDefaultRepositories();
        });
        
        Configure<AbpDbContextOptions>(op => op.UsePostgreSql());
    }
}

EfCoreStudentRepository 代碼:

public class EfCoreStudentRepository : EfCoreRepository<IXXXDbContext, Student, long>, IStudentRepository
{
    public EfCoreStudentRepository(IDbContextProvider<IXXXDbContext> dbContextProvider) : base(dbContextProvider)
    {
    }

    public Task<int> GetCountWithStudentlIdAsync(long studentId)
    {
        return DbSet.CountAsync(x=>x.studentId == studentId);
    }
}

原因

原因在異常信息已經說得十分清楚了,這裏我們需要了解兩個問題。

  1. 什麼原因導致兩個倉儲內部的 DbContext 不一致?
  2. 為什麼 ABP vNext 自己實現的倉儲能夠進行關聯查詢呢?

首先我們得知道,倉儲內部的 DbContext是怎麼獲取的。我們的自定義倉儲都會繼承 EfCoreRepository ,而這個倉儲是實現了 IQuerable<T> 接口的,最終它會通過一個 IDbContextProvider<TDbContext> 獲得一個可用的 DbContext

public class EfCoreRepository<TDbContext, TEntity> : RepositoryBase<TEntity>, IEfCoreRepository<TEntity>
    where TDbContext : IEfCoreDbContext
    where TEntity : class, IEntity
{
    public virtual DbSet<TEntity> DbSet => DbContext.Set<TEntity>();

    DbContext IEfCoreRepository<TEntity>.DbContext => DbContext.As<DbContext>();

    // 這裏可以看到,是通過 IDbContextProvider 來獲得 DbContext 的。
    protected virtual TDbContext DbContext => _dbContextProvider.GetDbContext();

    protected virtual AbpEntityOptions<TEntity> AbpEntityOptions => _entityOptionsLazy.Value;

    private readonly IDbContextProvider<TDbContext> _dbContextProvider;
    private readonly Lazy<AbpEntityOptions<TEntity>> _entityOptionsLazy;

    // ... 其他代碼。
}

下面就是 IDbContextProvider<TDbContext> 內部的核心代碼:

public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbContext> where TDbContext : IEfCoreDbContext
{
    private readonly IUnitOfWorkManager _unitOfWorkManager;
    private readonly IConnectionStringResolver _connectionStringResolver;

    // ... 其他代碼。

    public TDbContext GetDbContext()
    {
        var unitOfWork = _unitOfWorkManager.Current;
        if (unitOfWork == null)
        {
            throw new AbpException("A DbContext can only be created inside a unit of work!");
        }

        var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
        var connectionString = _connectionStringResolver.Resolve(connectionStringName);

        // 會構造一個 Key,而這個 Key 剛好是泛型類型的 FullName。
        var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}";

        // 內部是從一個字典當中,根據 dbContextKey 獲取 DbContext。如果不存在的話則調用工廠方法創建一個新的 DbContext。
        var databaseApi = unitOfWork.GetOrAddDatabaseApi(
            dbContextKey,
            () => new EfCoreDatabaseApi<TDbContext>(
                CreateDbContext(unitOfWork, connectionStringName, connectionString)
            ));

        return ((EfCoreDatabaseApi<TDbContext>)databaseApi).DbContext;
    }

    // ... 其他代碼。
}

通過以上代碼我們就可以知道,ABP vNext 在倉儲的內部是通過 IDbContextProvider<TDbContext> 中的 TDbContext 泛型,來確定是否構建一個新的 DbContext 對象。

不論是 ABP vNext 針對 IRepository<TEntity,TKey> ,還是我們自己實現的自定義倉儲,它們最終的實現都是基於 EfCoreRepository<TDbContext,TEntity,TKey> 的。而我們 IDbContextProvider<TDbContext> 的泛型,也是這個倉儲基類提供的,後者的 TDbContext 就是前者的泛型參數。

所以當我們在模塊添加 DbContext 的過城中,只要調用了 AddDefaultRepositories() 方法,ABP vNext 就會遍歷你提供的 TDbContext 所定義的實體,然後為這些實體建立默認的倉儲。

在注入倉儲的時候,找到了獲得默認倉儲實現類型的方法,可以看到這裏它使用的是 DefaultRepositoryDbContextType 作為默認的 TDbContext 類型。

protected virtual Type GetDefaultRepositoryImplementationType(Type entityType)
{
    var primaryKeyType = EntityHelper.FindPrimaryKeyType(entityType);

    // 重點在於構造倉儲類型時,傳遞的 Options.DefaultRepositoryDbContextType 參數,這個參數就是後面 EfCoreRepository 的 TDbContext 泛型。
    if (primaryKeyType == null)
    {
        return Options.SpecifiedDefaultRepositoryTypes
            ? Options.DefaultRepositoryImplementationTypeWithoutKey.MakeGenericType(entityType)
            : GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType);
    }

    return Options.SpecifiedDefaultRepositoryTypes
        ? Options.DefaultRepositoryImplementationType.MakeGenericType(entityType, primaryKeyType)
        : GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType, primaryKeyType);
}

最後我發現這個就是在模塊調用 AddAbpContext<TDbContext> 所提供的泛型參數。

public abstract class AbpCommonDbContextRegistrationOptions : IAbpCommonDbContextRegistrationOptionsBuilder
{
    // ... 其他代碼

    protected AbpCommonDbContextRegistrationOptions(Type originalDbContextType, IServiceCollection services)
    {
        OriginalDbContextType = originalDbContextType;
        Services = services;
        DefaultRepositoryDbContextType = originalDbContextType;
        CustomRepositories = new Dictionary<Type, Type>();
        ReplacedDbContextTypes = new List<Type>();
    }

    // ... 其他代碼
}

public class AbpDbContextRegistrationOptions : AbpCommonDbContextRegistrationOptions, IAbpDbContextRegistrationOptionsBuilder
{
    public Dictionary<Type, object> AbpEntityOptions { get; }

    public AbpDbContextRegistrationOptions(Type originalDbContextType, IServiceCollection services)
        : base(originalDbContextType, services) // 之類調用的就是上面的構造方法。
    {
        AbpEntityOptions = new Dictionary<Type, object>();
    }
}

public static class AbpEfCoreServiceCollectionExtensions
{
    public static IServiceCollection AddAbpDbContext<TDbContext>(
        this IServiceCollection services, 
        Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null)
        where TDbContext : AbpDbContext<TDbContext>
    {
        // ... 其他代碼。
        
        var options = new AbpDbContextRegistrationOptions(typeof(TDbContext), services);

        // ... 其他代碼。

        return services;
    }
}

所以,我們的默認倉儲的 dbContextKeyXXXDbContext,我們的自定義倉儲繼承 EfCoreRepository<IXXXDbContext,TEntity,TKey> ,所以它的 dbContextKey 就是 IXXXDbContext 。所以自定義倉儲獲取到的 DbContext 就與自定義倉儲的不一致了,從而提示上述異常。

解決

找到自定自定義倉儲的定義,修改它 EfCoreReposiotry<TDbContext,TEntity,TKey>TDbContext 泛型參數,變更為 XXXDbContext 即可。

public class EfCoreStudentRepository : EfCoreRepository<XXXDbContext, Student, long>, IStudentRepository
{
    public EfCoreStudentRepository(IDbContextProvider<XXXDbContext> dbContextProvider) : base(dbContextProvider)
    {
    }

    public Task<int> GetCountWithStudentlIdAsync(long studentId)
    {
        return DbSet.CountAsync(x=>x.studentId == studentId);
    }
}

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

【其他文章推薦】

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

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

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

南投搬家前需注意的眉眉角角,別等搬了再說!

全球最長壽黑犀牛 57歲壽終正寢

摘錄自2019年12月28日中央通訊社綜合報導

坦尚尼亞保護區管理局指出,據信是全球最長壽的一頭雌黑犀牛昨天(27日)在恩戈羅恩戈羅保護區壽終正寢,享壽57歲。

恩戈羅恩戈羅保護區管理局(Ngorongoro Conservation Area Authority)28日發表聲說,名為佛斯塔(Fausta)的這頭雌黑犀牛,於12月27日在保護區內據信因自然原因死亡,牠生前絕大部分時間都是在野外生活。

恩戈羅恩戈羅保護區管理局估計,野生犀牛的壽命介於37到43歲間,圈養犀牛則能活到50歲以上。聲明指出,紀錄顯示,佛斯塔較全球任何其他犀牛都更長壽,在恩戈羅恩戈羅放養超過54年,2016年才移至庇護區。

聲明又說:「三蘭港大學(University of Dar Es Salaam)一位科學家於1965年首度在恩戈羅恩戈羅火山口發現佛斯塔,當時牠的年齡介於3至4歲間。繼多次遭鬣狗攻擊且嚴重受傷後,牠的健康狀況於2016年開始惡化,我們不得不把牠置於圈養狀態。」

而全球最長壽的白犀牛,則是55歲的雌南方白犀牛沙納(Sana),於2017年在圈養地法國奇幻星球動物園(La Planete Sauvage Zoological park)死亡。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

非洲豬瘟肆虐 越南求助美國與荷蘭合製疫苗

摘錄自2019年12月28日中央通訊社報導

越南非洲豬瘟疫情升溫,嚴重打擊養豬業,造成境內豬肉供應短缺。越南農業機構表示,將與美國與荷蘭合作研製非洲豬瘟疫苗,同時擴大豬肉進口以彌補境內缺口,鼓勵農戶重新養豬。

越南農業暨農村發展部數據顯示,非洲豬瘟疫情2月爆發以來,全國63省市、667縣、8532個村落淪陷,被迫撲殺染病豬隻約600萬頭,重量約34萬2000噸,占全國豬肉總重量的9%。由於豬肉供給減少,越南各地豬肉價格最近飆漲不斷,創下10年來新高,消費者肉品食用習慣受到影響。豬肉價格飆漲,造成越南12月消費者物價指數(CPI)較11月上漲1.4%,創下9年來新高。

「線上知識報」新聞網站26日報導,越南農業暨農村發展部部長阮春強(Nguyen Xuan Cuong)受訪表示,越南非洲豬瘟疫苗自主研發工作目前取得了初步進展,未來將與美國與荷蘭合作研製疫苗。

阮春強表示,美國農業專家明年1月將前來越南,共同合作研發與生產非洲豬瘟疫苗,「在不久的將來,與確保生物安全等措施的同時,我們正在為推動養殖業永續發展創造便利條件」。越南農業機構表示,目前非洲豬瘟疫情逐漸趨緩,因此鼓勵農戶在確保生物安全的情況下重新養豬,推動家禽與其他家畜等養殖業發展,同時加強宣導改變消費者以豬肉為主的肉品食用習慣。

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

【其他文章推薦】

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

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

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

南投搬家前需注意的眉眉角角,別等搬了再說!