.Net Core微服務入門全紀錄(一)——項目搭建

前言

寫這篇博客主要目的是記錄一下自己的學習過程,只能是簡單入門級別的,因為水平有限就寫到哪算哪吧,寫的不對之處歡迎指正。
代碼放在:https://github.com/xiajingren/NetCoreMicroserviceDemo

什麼是微服務?

關於微服務的概念解釋網上有很多…
個人理解,微服務是一種系統架構模式,它和語言無關,和框架無關,和工具無關,和服務器環境無關…
微服務思想是將傳統的單體系統按照業務拆分成多個職責單一、且可獨立運行的接口服務。至於服務如何拆分,沒有明確的定義。
幾乎任何後端語言都能做微服務開發。
微服務也並不是完美無缺的,微服務架構會帶來更多的問題,增加系統的複雜度,引入更多的技術棧…

創建項目

一個客戶端,一個產品服務,一個訂單服務。3個項目都是asp.net core web應用程序。創建項目的時候記得啟用一下Docker支持,或者後面添加也行。

為產品、訂單服務添加一些基礎代碼,就簡單的返回一下 服務名稱,當前時間,服務的ip、端口。

在Docker中運行服務

為了方便,我使用Docker來運行服務,不用Docker也行,關於docker的安裝及基本使用就不介紹了。

  • build鏡像:

在項目根目錄打開PowerShell窗口執行:docker build -t productapi -f ./Product.API/Dockerfile .

Successfully代表build成功了。

  • 運行容器:

執行:docker run -d -p 9050:80 --name productservice productapi

執行:docker ps查看運行的容器:

沒問題,使用瀏覽器訪問一下接口:

也沒問題,其中的ip端口是Docker容器內部的ip端口,所以端口是80,這個無所謂。

  • 產品服務部署好了,下面部署一下訂單服務,也是同樣的流程,就把指令簡單貼一下吧:

build鏡像:docker build -t orderapi -f ./Order.API/Dockerfile .
運行容器:docker run -d -p 9060:80 --name orderservice orderapi
瀏覽器訪問一下:

OK,訂單服務也部署完成了。

客戶端調用

客戶端我這裏只做了一個web客戶端,實際可能是各種業務系統、什麼PC端、手機端、小程序。。。這個明白就好,為了簡單就不搞那麼多了。

  • 因為客戶端需要http請求服務端接口,所以需要一個http請求客戶端,我個人比較習慣RestSharp,安利一波:https://github.com/restsharp/RestSharp

  • 添加基礎代碼:

IServiceHelper.cs:

    public interface IServiceHelper
    {
        /// <summary>
        /// 獲取產品數據
        /// </summary>
        /// <returns></returns>
        Task<string> GetProduct();

        /// <summary>
        /// 獲取訂單數據
        /// </summary>
        /// <returns></returns>
        Task<string> GetOrder();
    }

ServiceHelper.cs:

    public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string serviceUrl = "http://localhost:9060";//訂單服務的地址,可以放在配置文件或者數據庫等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string serviceUrl = "http://localhost:9050";//產品服務的地址,可以放在配置文件或者數據庫等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }

Startup.cs:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            
            //注入IServiceHelper
            services.AddSingleton<IServiceHelper, ServiceHelper>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

HomeController.cs:

    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IServiceHelper _serviceHelper;

        public HomeController(ILogger<HomeController> logger, IServiceHelper serviceHelper)
        {
            _logger = logger;
            _serviceHelper = serviceHelper;
        }

        public async Task<IActionResult> Index()
        {
            ViewBag.OrderData = await _serviceHelper.GetOrder();
            ViewBag.ProductData = await _serviceHelper.GetProduct();

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }

Index.cshtml:

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>
        @ViewBag.OrderData
    </p>
    <p>
        @ViewBag.ProductData
    </p>
</div>

代碼比較簡單,這裏就不用docker了,直接控制台啟動,使用瀏覽器訪問:

  • 一切正常。進行到這裏,各個服務也獨立運行了,客戶端也能正常調用了,貌似算是完成一個簡易的微服務了。但是,微服務架構最重要的原則就是——“高可用”。以上的做法明顯不能滿足高可用性,因為任何一個服務掛掉,所有依賴這個服務的業務系統都會受影響。

停止一下訂單服務:docker stop orderservice

訂單服務停止,導致客戶端業務系統無法獲取訂單數據。
要解決這個問題,很容易想到:集群。

簡單的服務集群

既然單個服務實例有掛掉的風險,那麼部署多個服務實例就好了嘛,只要大家不同時全掛就行。

  • 使用docker運行多個服務實例:
docker run -d -p 9061:80 --name orderservice1 orderapi
docker run -d -p 9062:80 --name orderservice2 orderapi
docker run -d -p 9051:80 --name productservice1 productapi
docker run -d -p 9052:80 --name productservice2 productapi

現在訂單服務和產品服務都增加到3個服務實例。

  • 那麼稍微改造一下客戶端代碼吧:
    ServiceHelper.cs:
public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string[] serviceUrls = { "http://localhost:9060", "http://localhost:9061", "http://localhost:9062" };//訂單服務的地址,可以放在配置文件或者數據庫等等...

            //每次隨機訪問一個服務實例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string[] serviceUrls = { "http://localhost:9050", "http://localhost:9051", "http://localhost:9052" };//產品服務的地址,可以放在配置文件或者數據庫等等...

            //每次隨機訪問一個服務實例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }

當然拿到這些服務地址可以自己做複雜的負載均衡策略,比如輪詢,隨機,權重等等 都行,甚至在中間弄個nginx也可以。這些不是重點,所以就簡單做一個隨機吧,每次請求來了隨便訪問一個服務實例。

  • 瀏覽器測試一下:

    可以看到請求被隨機分配了。但是這種做法依然不安全,如果隨機訪問到的實例剛好掛掉,那麼業務系統依然會出問題。
    簡單處理思路是:
    1.如果某個地址請求失敗了,那麼換一個地址接着執行。
    2.如果某個地址的請求連續多次失敗了,那麼就移除這個地址,下次就不會訪問到它了。
    。。。。。。
    業務系統實現以上邏輯,基本上風險就很低了,也算是大大增加了系統可用性了。

  • 然後思考另一個問題:

實際應用中,上層的業務系統可能非常多,為了保證可用性,每個業務系統都去考慮服務實例掛沒掛掉嗎?
而且實際應用中服務實例的數量或者地址大多是不固定的,例如雙十一來了,流量大了,增加了一堆服務實例,這時候每個業務系統再去配置文件里配置一下這些地址嗎?雙十一過了又去把配置刪掉嗎?顯然是不現實的,服務必須要做到可靈活伸縮。

  • 這時候就引入一個名詞:服務註冊與發現

未完待續…

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

【其他文章推薦】

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

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

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

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

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

基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(四)

系列文章

  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 開發博客項目 – 博客接口實戰篇(四)
  20. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(五)
  21. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(一)
  22. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(二)
  23. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(三)
  24. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(四)
  25. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(五)
  26. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(六)
  27. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(七)
  28. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(八)
  29. 基於 abp vNext 和 .NET Core 開發博客項目 – Blazor 實戰系列(九)
  30. 基於 abp vNext 和 .NET Core 開發博客項目 – 終結篇之發布項目

上一篇完成了博客的分頁查詢文章列表頁面的數據綁定和分頁功能,本篇將繼續完成剩下的幾個頁面。

在開始主題之前重新解決上一篇的最後一個問題,當點擊了頭部組件的/posts鏈接時直接強制刷新了頁面,經過查看文檔和實踐有了更好的解決方案。

先將頭部組件Header.razor中的NavLink恢復成<NavLink class="menu-item" href="posts">Posts</NavLink>,不需要點擊事件了。

然後在Posts.razor中添加生命周期函數OnParametersSetAsync(),在初始化完成后執行。

/// <summary>
/// 初始化完成后執行
/// </summary>
/// <returns></returns>
protected override async Task OnParametersSetAsync()
{
    if (!page.HasValue)
    {
        page = 1;
        await RenderPage(page);
    }
}

判斷當前page參數是否有值,有值的話說明請求肯定是來自於翻頁,當page沒有值的時候就說明是頭部的菜單點進來的。那麼此時給page賦值為1,調用API加載數據即可。

分類列表

Categories.razor是分類列表頁面,上篇文章已經實現了從API獲取數據的方法,所以這裏就很簡單了,指定接受類型,然後在生命周期初始化OnInitializedAsync()中去獲取數據。

@code{
    /// <summary>
    /// categories
    /// </summary>
    private ServiceResult<IEnumerable<QueryCategoryDto>> categories;

    /// <summary>
    /// 初始化
    /// </summary>
    protected override async Task OnInitializedAsync()
    {
        // 獲取數據
        categories = await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryDto>>>($"/blog/categories");
    }
}

當獲取到數據的時候進行綁定,沒有數據的時候還是显示加載中的組件<Loading />讓他轉圈圈。

@if (categories == null)
{
    <Loading />
}
else
{
    <div class="container">
        <div class="post-wrap categories">
            <h2 class="post-title">-&nbsp;Categories&nbsp;-</h2>
            <div class="categories-card">
                @if (categories.Success && categories.Result.Any())
                {
                    @foreach (var item in categories.Result)
                    {
                        <div class="card-item">
                            <div class="categories">
                                <a href="/category/@item.DisplayName/">
                                    <h3>
                                        <i class="iconfont iconcode" style="padding-right:3px"></i>
                                        @item.CategoryName
                                    </h3>
                                    <small>(@item.Count)</small>
                                </a>
                            </div>
                        </div>
                    }
                }
                else
                {
                    <ErrorTip />
                }
            </div>
        </div>
    </div>
}

直接循環返回的數據列表categories.Result,綁定數據就好,當獲取失敗或者沒有返回數據的時候显示錯誤提示組件<ErrorTip />

標籤列表

Categories.razor是標籤列表頁面,和分類列表HTML結構差不多一樣的,除了返回類型和接口地址不一樣,將上面代碼複製過來改改即可。

@code{
    /// <summary>
    /// tags
    /// </summary>
    private ServiceResult<IEnumerable<QueryTagDto>> tags;

    /// <summary>
    /// 初始化
    /// </summary>
    protected override async Task OnInitializedAsync()
    {
        // 獲取數據
        tags = await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryTagDto>>>($"/blog/tags");
    }
}
@if (tags == null)
{
    <Loading />
}
else
{
    <div class="container">
        <div class="post-wrap tags">
            <h2 class="post-title">-&nbsp;Tags&nbsp;-</h2>
            <div class="tag-cloud-tags">
                @if (tags.Success && tags.Result.Any())
                {
                    @foreach (var item in tags.Result)
                    {
                        <a href="/tag/@item.DisplayName/">@item.TagName<small>(@item.Count)</small></a>
                    }
                }
                else
                {
                    <ErrorTip />
                }
            </div>
        </div>
    </div>
}

友鏈列表

FriendLinks.razor是友情鏈接列表頁面,實現方式和上面兩個套路一模一樣。

@code {
    /// <summary>
    /// friendlinks
    /// </summary>
    private ServiceResult<IEnumerable<FriendLinkDto>> friendlinks;

    /// <summary>
    /// 初始化
    /// </summary>
    protected override async Task OnInitializedAsync()
    {
        // 獲取數據
        friendlinks = await Http.GetFromJsonAsync<ServiceResult<IEnumerable<FriendLinkDto>>>($"/blog/friendlinks");
    }
}
@if (friendlinks == null)
{
    <Loading />
}
else
{
    <div class="container">
        <div class="post-wrap categories">
            <h2 class="post-title">-&nbsp;FriendLinks&nbsp;-</h2>
            <div class="categories-card">
                @if (friendlinks.Success && friendlinks.Result.Any())
                {
                    @foreach (var item in friendlinks.Result)
                    {
                        <div class="card-item">
                            <div class="categories">
                                <a target="_blank" href="@item.LinkUrl">
                                    <h3>@item.Title</h3>
                                </a>
                            </div>
                        </div>
                    }
                }
                else
                {
                    <ErrorTip />
                }
            </div>
        </div>
    </div>
}

文章列表(分類)

Posts.Category.razor是根據分類查詢文章列表頁面,他接受一個參數name,我們要根據name去API查詢數據然後綁定頁面即可。

這裏的參數name實際上就是從標籤列表傳遞過來的DisplayName的值,它是一個比較友好的名稱,我們還要通過這個值去查詢真正的分類名稱進行展示,所以這裏需要調用兩個API,這點在設計API的時候沒有考慮好,我們其實可以將這兩個API合併變成一個,後續再進行優化吧,這裏就請求兩次。

添加兩個接收參數:分類名稱和返回的文章列表數據。

/// <summary>
/// 分類名稱
/// </summary>
private string categoryName;

/// <summary>
/// 文章列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryPostDto>> posts;

然後在OnInitializedAsync()初始化方法中調用API獲取數據,賦值給變量。

/// <summary>
/// 初始化
/// </summary>
protected override async Task OnInitializedAsync()
{
    // TODO:獲取數據,可以在API中合併這兩個請求。
    var category = await Http.GetFromJsonAsync<ServiceResult<string>>($"/blog/category?name={name}");
    posts = await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryPostDto>>>($"/blog/posts/category?name={name}");

    if (category.Success)
    {
        categoryName = category.Result;
    }
}

有了數據,直接在頁面上進行循環綁定。

@if (posts == null)
{
    <Loading />
}
else
{
    <div class="container">
        <div class="post-wrap tags">
            @if (categoryName != null)
            {
                <h2 class="post-title">-&nbsp;Category&nbsp;·&nbsp;@categoryName&nbsp;-</h2>
            }
        </div>
        <div class="post-wrap archive">
            @if (posts.Success && posts.Result.Any())
            {
                @foreach (var item in posts.Result)
                {
                    <h3>@item.Year</h3>
                    @foreach (var post in item.Posts)
                    {
                        <article class="archive-item">
                            <NavLink href="@("/post"+post.Url)">@post.Title</NavLink>
                            <span class="archive-item-date">@post.CreationTime</span>
                        </article>
                    }
                }
            }
            else
            {
                <ErrorTip />
            }
        </div>
    </div>
}

文章列表(標籤)

Posts.Tag.razor是根據標籤查詢文章列表,這個和分類查詢文章列表實現方式一樣,直接上代碼。

@code {
    /// <summary>
    /// 標籤名稱參數
    /// </summary>
    [Parameter]
    public string name { get; set; }

    /// <summary>
    /// 標籤名稱
    /// </summary>
    private string tagName;

    /// <summary>
    /// 文章列表數據
    /// </summary>
    private ServiceResult<IEnumerable<QueryPostDto>> posts;

    /// <summary>
    /// 初始化
    /// </summary>
    protected override async Task OnInitializedAsync()
    {
        // TODO:獲取數據,可以在API中合併這兩個請求。
        var tag = await Http.GetFromJsonAsync<ServiceResult<string>>($"/blog/tag?name={name}");
        posts = await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryPostDto>>>($"/blog/posts/tag?name={name}");

        if (tag.Success)
        {
            tagName = tag.Result;
        }
    }
}
@if (posts == null)
{
    <Loading />
}
else
{
    <div class="container">
        <div class="post-wrap tags">
            @if (tagName != null)
            {
                <h2 class="post-title">-&nbsp;Tag&nbsp;·&nbsp;@tagName&nbsp;-</h2>
            }
        </div>
        <div class="post-wrap archive">
            @if (posts.Success && posts.Result.Any())
            {
                @foreach (var item in posts.Result)
                {
                    <h3>@item.Year</h3>
                    @foreach (var post in item.Posts)
                    {
                        <article class="archive-item">
                            <NavLink href="@("/post"+post.Url)">@post.Title</NavLink>
                            <span class="archive-item-date">@post.CreationTime</span>
                        </article>
                    }
                }
            }
            else
            {
                <ErrorTip />
            }
        </div>
    </div>
}

以上完成了以上幾個頁面的數據綁定,頁面之間的跳轉已經關聯起來了,然後還剩下文章詳情頁,大家可以先自己動手完成它,今天就到這裏,未完待續…

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

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

吉利大法好!沃爾沃去年銷量創紀錄,換髮第二春!

2016年沃爾沃汽車進一步確立了在自動駕駛、電氣化及安全領域的領先地位,建立了新的商業聯盟,並不斷推出全新的產品,打造立足全球的生產製造基地。2016年沃爾沃汽車斥資5億美元在美國南卡羅來納州興建工廠,將生產基於SpA架構的車型,初期將聘用2,000餘名員工。

沃爾沃汽車集團近日發布2016年銷售業績显示,2016年沃爾沃汽車全球共實現銷量534,332輛,同比增長6.2%,連續三年創銷量紀錄。2016年沃爾沃汽車在全球各大市場銷量齊頭並進,在中國和北美兩大市場均實現了兩位數增幅,西歐市場表現強勁,沃爾沃汽車全球復興第二階段持續加速。

S90

沃爾沃全新90系車型2016年銷量飄紅,其中XC90車型銷量較2015年激增了125%,印證了沃爾沃全新的設計語言及創新科技在全球取得成功,為未來沃爾沃全新車型的上市,打下堅實的基礎。同時,沃爾沃XC60車型年銷量達到161,092輛,自2008年投放市場以來,連續九年屢創銷量紀錄。

XC90

2016年沃爾沃汽車在中國市場銷量達90,930輛,同比增長11.5%。中國依然是沃爾沃汽車全球最大單一市場。其中,國產沃爾沃XC60和S60L是沃爾沃汽車在中國市場最暢銷的車型。

沃爾沃汽車2016年在美國市場銷量增幅達18.1%,是美國增速最快的豪華汽車品牌之一,實現年銷量82,726輛。其中沃爾沃XC90和XC60最受美國消費者歡迎,市場表現出眾。得益於德國、英國、法國和意大利等主要市場強勁業績的推動,2016年沃爾沃汽車在西歐銷量增長4.1%,達到206,144輛。

沃爾沃汽車2016年實現銷量破紀錄的同時,通過全球復興和品牌重新定位持續強化與其他豪華品牌的競爭優勢。2016年沃爾沃汽車進一步確立了在自動駕駛、電氣化及安全領域的領先地位,建立了新的商業聯盟,並不斷推出全新的產品,打造立足全球的生產製造基地。

2016年沃爾沃汽車斥資5億美元在美國南卡羅來納州興建工廠,將生產基於SpA架構的車型,初期將聘用2,000餘名員工。2016年沃爾沃汽車發布了中國製造戰略,在提升產能的同時,將中國打造成了面向全球市場的生產和出口基地。沃爾沃大慶工廠生產旗艦級全新S90系家族,成都工廠生產現款60系及未來全新60系車型,基於CMA架構的全新40系車型正在規劃中,將在距上海以南350公里的路橋工廠投產。

2016年9月,隨着沃爾沃全新V90 Cross Country旅行越界車的上市,沃爾沃全新90系車型已全部完成換代。其中XC90車型更是榮獲120多個國際大獎,充分展現了沃爾沃全新SpA架構在設計、技術等方面的領先優勢。

V90 Cross Country

未來幾年,沃爾沃汽車將以每年兩款全新車型的速度完成全部產品換代。2017年將推出基於SpA架構的全新XC60車型,以及基於CMA架構的首款40系產品——全新XC40車型;在新能源領域,2016年沃爾沃汽車發布了全方位的電氣化戰略,將在全系車型中引入插電式混合動力系統,並在2019年之前推出首款純電動車,到2025年將實現新能源車型累計銷量100萬輛。

2016年沃爾沃汽車與優步(Uber)公司攜手合作開發自動駕駛技術,與瑞典奧托立夫公司(Autoliv)合作建立了合資公司——Zenuity,致力於設計和開發自動駕駛軟件及高級駕駛輔助系統,將為快速發展的全球市場提供自動駕駛軟件等服務。這也是豪華汽車品牌首次與一線供應商聯手開發相關技術,將為汽車行業帶來重大變革。

2017年沃爾沃汽車將在瑞典總部哥德堡進一步推動Drive Me自動駕駛測試項目。作為目前全球最先進、最前沿的自動駕駛測試項目,沃爾沃汽車將提供100輛XC90自動駕駛汽車用於普通居民在真實的日常環境中出行使用。未來,沃爾沃汽車還將在中國與英國啟動DriveMe自動駕駛測試項目。

隨着全球復興進度的加快及全新商業模式的拓展,沃爾沃汽車不僅是全球豪華汽車製造商,還將成為了一家全球高端移動出行公司。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

20萬就能買到全新寶馬轎車?!因為它真的來了

新車基於UKL前驅平台打造,1系三廂將會是前驅車型,新車的動力系統或為1。5T 136馬力+6速自動變速箱,2。0T 192馬力/231馬力+8擋手自一體變速箱,其中官方聲稱1。5T車型的百公里油耗最低可以達到5。5L。新車將會有無鑰匙啟動、抬頭显示、倒車影像、全景天窗、LED大燈、pM2。

寶馬1系三廂自從誕生的那一刻就備受關注,吸睛無數,小編也和大家一樣,時刻關注着寶馬1系三廂的所有信息。畢竟,哪個男人心裏沒有這一個藍天白雲夢,開寶馬也是很多人的心愿。

但是寶馬作為豪華品牌,價格不是每個消費者都能承受的起的。寶馬進口1系的價格不是很貴,但是國人就是對兩廂車有一定的排斥心理,比較鍾愛三廂車,所以即使進口1系兩廂的價格足夠便宜,但是買單的人照樣不多。

當然,寶馬3系是三廂車,但是3系的價格在30萬左右,還是有點小貴。所以在20萬級別這個領域,出現了一個市場空缺。如果能有一台20萬的三廂寶馬,這應該是極好的選擇。

看看奧迪A3就知道了,作為豪華品牌的A3將價格做到了20萬元左右,在這個沒有直接競爭對手的領域,A3的銷量好的一塌糊塗,A3在11月份交出了9883輛的銷量,將近萬輛的銷量足以看出來這個細分市場有很大的潛力。

所以寶馬義不容辭的推出了國產版的1系三廂。

寶馬1系三廂版基於Compact Sedan概念車打造出來的,新車的設計借鑒了概念車的設計元素,同時也具有着寶馬家族化的設計風格,前大燈的造型與3系比較相似,側麵線條比較平直,從外觀看像是縮小的3系,看起來短小精悍,富有運動感。

內飾看起來還是那麼熟悉的感覺,畢竟也是採用了寶馬家族化的設計特徵,中控台造型很有層次感,並配備了8.8英寸液晶屏,同時還有大面積的鍍鉻裝飾,可以增強內飾的精緻感。

新車基於UKL前驅平台打造,1系三廂將會是前驅車型,新車的動力系統或為1.5T 136馬力+6速自動變速箱,2.0T 192馬力/231馬力+8擋手自一體變速箱,其中官方聲稱1.5T車型的百公里油耗最低可以達到5.5L。

新車將會有無鑰匙啟動、抬頭显示、倒車影像、全景天窗、LED大燈、pM2.5濾清器等,這些都是國人比較看重的配置。新車的預售價為20.5萬起,這就意味着也許不到20萬的價格就可以買到帥氣的三廂寶馬,估計很多消費者都會心動吧!如果實際售價能比預售價還低一點的話,那麼A3就會瞬間感到壓力了。

競爭對手

奧迪A3

指導價:18.49-28.10萬

A3在國內的成就有目共睹,但是它真正的競爭對手預計會在2月份正式上市銷售,憑藉著“藍天白雲”在國內的號召力,A3將會面臨很大壓力,那麼它能否守住自己的陣地,就讓我們拭目以待吧!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

售價降低還換裝1.5T 這款合資轎車不輸思域

輪轂方面使用的是美規思域Si上的18英寸旋風式輪轂,樣式非常漂亮。內飾:基本沒有變化內飾方面也僅僅進行了一些小改動,基本保持着舊款的設計。但是增加了配色,導航也終於被加入到車機裏面。依舊使用着数字显示式儀錶盤,這是一種用過都說好的設計,数字显示的時速一目瞭然,而且較高的位置真有點抬頭显示的感覺。

前言

本田思域可謂是現今緊湊型轎車的當紅辣子雞,即使需要加價也難以阻擋人們對它的喜愛之情,其中最吸引他們的就是思域上那款動力油耗表現出色的1.5T發動機以及極高的配置。而最近本田為我們帶來2017款本田傑德,這輛大兩廂汽車更換了十代思域上的1.5T發動機,功率雖然有所降低,但是配置更為豐富,那麼它具體又有着怎樣的升級呢?又是否值得購買呢?

傑德定位非常獨特,你可以將它當做一輛旅行車,也可以將其當做是MpV。而本田定位它是一輛緊湊型汽車,在筆者的眼中它是一輛大兩廂車,首先來自老款思域的平台讓它有着轎車般的駕駛感受,其次6座的布局實在和7座MpV有差異。這次2017款傑德的定價為12.99-17.99萬,購買門檻比起之前更低了。

外觀:更換了LED光源以及新增配色,更為精神

2017款傑德增加了新翠綠、波爾多紅等車身顏色,更為個性以及好看。在前臉方面進氣格柵增加了少許鍍鉻裝飾,看着更為時尚,而這種家族史設計使得傑德前臉更像是雅閣。

而集成了LED日間行車燈的LED大燈非常醒目,亮度相當高,不過這是1.5T中高配車型的專利,1.8L以及1.5T最低配都與這LED大燈無緣。

尾燈也同樣使用了LED燈源,辨識率更高,關鍵的一點是視覺效果更佳。除此以外,傑德使用了雙邊共雙出的排氣管設計,看着有一點性能味。

離地間隙提升了10mm,舊款托底的毛病減弱不少,但是有一點需要注意,傑德是一輛兩廂車,對於兩廂車而言這個離地間隙已經是很大的了。輪轂方面使用的是美規思域Si上的18英寸旋風式輪轂,樣式非常漂亮。

內飾:基本沒有變化

內飾方面也僅僅進行了一些小改動,基本保持着舊款的設計。但是增加了配色,導航也終於被加入到車機裏面。

依舊使用着数字显示式儀錶盤,這是一種用過都說好的設計,数字显示的時速一目瞭然,而且較高的位置真有點抬頭显示的感覺。

配置:提升不少,但尚未完美

這次配置上的提升是比較明顯的,讓傑德競爭力一下子提升不少,增加了LED大燈、日間行車燈、LED尾燈、18英寸輪轂、併線輔助系統、車道偏離系統、主動剎車、自適應巡航系統等,而且1.5T車型全系標配了全景天窗。

傑德似乎是想要向思域看齊,但傑德卻沒有思域上的自動駐車以及电子手剎,稍顯遺憾,而且作為最低配車型並沒有配備ESp車身穩定系統。總的而言,傑德在配置上以及性價比上提升不少。

動力:最大的亮點

一直以來,傑德都是被當做是老款思域的重造產物,使用了同樣的平台,同樣的動力總成。底盤表現非常出色,但1.8L自然吸氣發動機卻難以挑起重擔,油耗以及耐用度優秀,卻給人白開水的感覺。這次加入的1.5T發動機,就像是辣椒一樣一下子把這款大兩廂車變得勁爆刺激味蕾。最大功率115千瓦,最大扭矩203牛米,比起思域上略有下降,依然是優秀水平,可以理解為思域上的1.5T低功率版本。

和這款1.5T渦輪增壓發動機相配合的依然是CVT變速箱,和思域一樣,根據思域上平均8.5L的實測油耗,我們有理由相信使用同款發動機的傑德油耗也能同樣優秀。

這一次2017款本田傑德的上市把之前的一些小問題幾乎都解決了,更低的價格、換上了更強悍的動力總成、加入了導航、更豐富的舒適性配置以及安全配置,這樣看來傑德是越趨完美。加上同價位並沒有相似的競爭對手,傑德可能會成為東本的第二個思域。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

為何有點小錢的人都不願買國產車?

廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。

2016年汽車圈發生了很多事,其中令叫獸印象最深的是自主品牌的叫好聲四起,銷量上也取得了空前的突破。不過當叫獸冷靜下來理智的想一想,當下自主品牌真的是形勢一片大好,即將崛起了嗎?

廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

好開、省油還有科技感,教授把這輛車推薦給挑剔的朋友們

聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。智行第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

身邊的朋友知道喜歡玩車

於是經常能收到他們的諮詢

什麼十萬買啥車、二十萬買啥車

諸如此類的問題

其實都可以輕鬆應對的

然而就像廣東話說的

“上得山多終遇虎”

也會有天被問題難住

三個月前的同學聚會

有三位老同學說他們感情好

想買同一輛車,都要二十萬出頭而且夠大氣的

首先女同學是一名女司機

作為職業女性的她,要經常跑高速

而且她還是新手

所以她要我推薦一輛好開、安全的車給她

第二位同學則是環保主義者

什麼都不管,就要油耗低

(大哥,外觀大氣的車好難找油耗低的吧)

第三位同學是一名技術宅

典型的IT男

需要一輛有技術含量的車子

聽了他們的要求后,第一時間是吐槽根本沒有這樣的車。但兢兢業業鍥而不舍的我在網上做了大量的搜索之後,發現有一輛車非常適合這三位老同學,那就是長安福特的17款新蒙迪歐。

17款新蒙迪歐指導價17.98萬起,首先在價格上是符合要求的,然後在外觀上,馬丁式的家族前臉、LED大燈、2850mm的長軸距、以及肌肉感十足的車身線條,時尚個性又不失沉穩的外觀能讓不同年齡層的人接受。

內飾方面,採用了對稱式的布局,整體的內飾簡潔明了,多媒體系統也已經升級到第三代的SYNC操作系統,操作更加流暢,功能也更加完善。除此以外,旋鈕式換擋的設計也讓車內的檔次感提升不少。

行駛方面,拿2.0T車型來說,最大馬力達到245ps,搭配調校成熟的6AT手自一體變速箱,動力表現充沛且平順,絲毫不用擔心有小馬拉大車的感覺;底盤方面採用了多連桿獨立后懸架,整體偏向於舒適的調校,對細碎顛簸的過濾十分徹底。可以看出,實際的行駛表現也很符合蒙迪歐的定位。

聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。

三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。

智行

第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

除此以外,對於這位新手司機來說,帶行人識別功能的智能感應制動保護系統、以及只需控制油門和剎車,即可輕鬆把車停在理想位置的主動泊車輔助系統和ACC全速智能自適應巡航控制系統都讓她駕駛蒙迪歐的過程更加輕鬆。另外,車廂內的10個安全氣囊的環繞式防護也讓她駕駛的過程中更加安心。

智擎

至於那位作為環保主義者的老同學,關注油耗的他對17款新蒙迪歐的EcoBoost雙渦流渦輪增壓直噴發動機贊不絕口,因為先進的雙渦流渦輪增壓技術能有效緩解低速時的遲滯性,使峰值扭矩提早爆發,帶來更佳的燃油經濟性。

而為了達到更好的節能環保效果,他選擇了Hybrid車型,17款新蒙迪歐的混合動力版本採用阿特金森循環發動機配合電動機,並搭載eCVT電控無級變速器,讓燃油與電力良好融合,高效環保,帶來煥然一新的駕駛體驗與樂趣。

智聯

最後一位老同學就是做IT男的技術宅,他最滿意的就是17款新蒙迪歐搭載的福特派TM互聯技術,只需要在手機中下載福特派TM應用,就可以遠程控制車輛並且通過手機來查看燃油剩餘量等車輛概況,十分方面。

在多媒體功能方面,車主可以通過藍牙或者USB數據線將手機和車輛相連,來拓展多媒體的功能,同時,17款新蒙迪歐的多媒體系統支持智能聲控以及智能中文手寫輸入,支持多字連寫,讓人機之間的交互變得更加簡單。

總結

其實早就知道17款新蒙迪歐的三大亮點能夠讓我的這三位老同學滿足,“智行”讓駕駛更加輕鬆而且安全、“智擎”在提供充沛動力的同時又保證了良好的燃油經濟性、“智聯”則結合了時下流行的智能手機,既提供了用車的便利,又豐富了多媒體功能。三管齊下,相信不只是我的這三位老同學,廣大的消費者都會被17款新蒙迪歐吸引到。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

大發現!國3汽車的PM2.5居然比國5低?

接下來是國三的車。所以僅從pM2。5的指數上來看,老車的成績不一定差,新車的成績也不一定好,反而車況才是更重要的因素。而那些所謂的專家。其實之前其他網友測試汽車尾氣pM2。5指數得出驚人的結果。(汽車尾氣比空氣乾淨,聽着很扯,畢竟你不可能整天去吸尾氣),就有專家出來表明,汽車的排放導致的污染並非只是直接的pM排放污染,而是“二次反應”。

一到冬季,pM指數和霧霾就成了熱門話題,這個時候汽車就會慣例被推上輿論焦點,近期國六排放標準的出台也進一步表明了相關部門的觀點—“霧霾的很大成因就是因為汽車的排放”。每次說到空氣污染提得最多的就是pM2.5指數。

pM指數那麼多,為什麼偏偏是pM2.5?

pM的英文全稱為particulate Matter (顆粒物),而数字2.5是指這種顆粒物的空氣動力學直徑(aerodynamic diameter)為2.5微米( pM10則是指粒徑等於、小於10微米的顆粒物)。粒徑越小、密度越低,顆粒沉降得越慢。如果再有一些外力引起空氣微小的擾動,這些細小的顆粒即使在無風的條件下,最終也很難沉降到地面上,它們會一直在空中“遊盪”,成為危害人體健康的罪魁禍首。

而我們常說的pM2.5的濃度或其他污染氣體的濃度是指每立方米空氣中這種污染物的質量含量,它反映了空氣的污染程度,這個值越高,就代表空氣污染越嚴重。由於空氣中污染物的種類很多,如:可吸入顆粒物、二氧化硫、氮氧化物、一氧化碳及臭氧等,為了統一評估,我國的環保部門,將每一種污染物的濃度都換算成統一的空氣污染指數,然後對外發布。俗話說病從口入,那麼我們今天就從源頭-用儀器來測試一下汽車尾氣的pM2.5指數。

在此之前我們先看下香煙的pM指數,直接爆表,所以能戒煙朋友趕緊戒煙了。

接着測試一下國4的車。(所測汽車均為公司同事車輛)

然後是國5的車!

接下來是國三的車。

所以僅從pM2.5的指數上來看,老車的成績不一定差,新車的成績也不一定好,反而車況才是更重要的因素。

而那些所謂的專家。。。

其實之前其他網友測試汽車尾氣pM2.5指數得出驚人的結果。。。

(汽車尾氣比空氣乾淨,聽着很扯,畢竟你不可能整天去吸尾氣),就有專家出來表明,汽車的排放導致的污染並非只是直接的pM排放污染,而是“二次反應”。

這樣一群吃瓜群眾就懵逼了,你是博士后,你說什麼都對,就像一直聽說F1產生的下壓力足以讓其貼在牆上跑,但是從來沒有見過,不相信這件事的人也找不到反駁的理由。

不管汽車的排放是不是元兇(因為現在霧霾這個鍋汽車已經背定了),相關部門對新車排放的要求越來越高並沒錯,畢竟排放再少也是排放。假設之前國三標準滿分是100分,當年生產的汽車再優秀,也只能得100。

現在國五標準出來了,直接就說滿分是150,100分不及格,也不給你重考的機會(限制進京、限制遷入等),這種把能夠達到要求的老車也一刀切那就有點想不通了,對於車迷來說最大的損失就是中國就永遠不會有老爺車文化,因為在相關部門眼中只有報廢車。

最後通過數據計算可以知道廣州的車輛密度比北京還要高,但是空氣質量卻要好不少,所以愛吃烤鴨的小夥伴趕緊把烤鴨吃個夠,說不定哪天烤鴨也分黃標鴨!!!

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

一個生物專業學生的內心獨白:我為什麼能去互聯網大廠?

這回,考慮到近期關注了許多新朋友,並且大多都是學生黨,可能對我還不是特別熟悉。因此我決定重新把我從非科班如何通過自學(狗屎運)進入大廠的經歷分享出來,希望能夠給予一些將要面臨秋招,或者將要準備進入互聯網行業的同學一丟丟的幫助。

早期關注我的讀者可能都約莫着記得,我是在華科讀的本碩,專業是生物醫學工程。雖說專業的的確確是帶了生物二字,但是我老實交代,實際上並不是純粹的生物技術方向上的專業。簡單科普一下:

生物醫學工程是屬於交叉程度非常高,主要面向生物醫學領域的工程學科。我們在學校的主要課程包含硬件設計、軟件設計、生化基礎以及儀器科學等。

所以呢,原諒本文的些許標題黨。但是說實話,由於學科高度交叉,我們實際上所習得的技能就是一團漿糊。在計算機科學方面的系統知識積累,與純生物專業相比,也不過是五十步笑百步。

一 啟智階段

雖說大學期間學的都不是計算機,但是我對計算機的興趣萌芽卻是十分的早。那估計都得有二十年前了,嗯誇張了點。不過應該是在小學時候了。

我是在老家鄉下上的小學,你們可能想象不到,那個時候的鄉下小學竟然都有電腦課。不過那時候不叫電腦課,叫做“微機課”。

微機課是幹嘛的呢?就是一群小朋友排排隊,拖鞋進到一個乾淨的房間里,然後幾個人圍着一台那種屁股又大又方的電腦,玩紙牌。沒錯,就是windows上流行多年的紙牌遊戲。

怎麼說呢。我玩紙牌賊溜。 大概天生對這一類東西的接受能力比較強,所以上手很快,並且除了紙牌,還有空當接龍,掃雷,3D彈球。這麼說可能有點暴露年齡了。

最早windows系統普及的這一批遊戲真的讓我對“微機”這麼個東西產生了懵懂的概念和持續的嚮往。

二 本科階段

之後的成長道路中,一直對計算機耍的挺溜。但是有點尷尬的是,沒人領進門。所以一直都是在娛樂,並沒有真正意義上接觸到計算機內部或者代碼編程的世界。

初識代碼

所以在上大學之前,基本上對編程一無所知,但是卻有一種強烈的對計算機的熱愛。不過那時對於計算機的理解僅限於【裝系統】,【裝軟件】和【拆洗主機風扇】。不要問我為什麼沒有去學計算機,要怪就只能怪考試時坐在我旁邊的那位,大概天生就得了瘋狂抖腿病的二貨。氣!

一不小心,還進了生科院。但是緣分這種東西,真的妙不可言。這一切的開始源於一場面試。

所在的大學是一所以工科著稱的高校,其中創業氣氛十分濃厚。學校因此有許多小有名氣的科創團隊,基本上是由老師主導,各專業學生組成的小團體。可不要小看這些小團隊,世界級程序設計大賽的獎牌獲得者經常就出於此類團隊。剛上大學的我們單純稚嫩,自然會被被這些團隊的大幅宣傳報和滿目的獎牌稱號所吸引膜拜。我也不例外。

當時我便懷着澎湃的心情申請了一家曾多次在微軟創新杯奪得金獎的團隊。一個從鄉下來的小伙,第一次參加面試,第一次單獨和碼農小姐姐夜晚座談,第一次參加所謂的通宵測試。

也就是在那一個晚上,開啟了我新世界的大門。

那天晚上給我一群編程小白的任務,是模仿百度首頁,實現整個網頁的設計、布局和基本鏈接。提前給出的提示是w3c的教程網址。

從來沒有接觸過編程的我在此之前,連編程的流程都不清楚,更不必說編譯環境、編譯語言甚至是源代碼閱讀(當然這個任務也涉及不到這些)。

但是也就是那一個晚上,讓我真正意義上的接觸了敲代碼這個事兒。

沒錯,我的編程起點是HTML+CSS。可惜的是,我並沒有通過那個團隊最終的面試。不過從那以後,我就開始了網站開發的自學之路。

個人自學的堅持很大程度上基於興趣,源於在室友面前一頓裝逼后的成就感。但這就是一個生科院的學生在課後的最大樂趣所在。

小試牛刀

在接觸到網站開發之後,從最初的HTML+CSS到後來的HTML5+CSS3+JS+ASP,從靜態頁面的布局到動態網頁的請求。

雖然感覺技術的成長也就是從博客的複製粘貼走向了文檔的複製粘貼。但是終於迎來了小試牛刀的機會。

學校某大型學校組織需要做一個展示網站,朋友拉上了我和幾個人承擔了這個事情。這算是第一次真正意義上的項目開發,不過整個網站的功能不多,主要還是展示為主,後台也直接通過學校網絡平台整合就行了。所以整體下來做的事情並不多。

迷茫困惑

一直以來,都是自己通過博客自學,東拼西湊的建立起知識架構。但是其實技術基礎十分不牢固,不懂計算機基礎、計算機網絡,更別說編譯原理、操作系統等基本的知識。因此在很長一段時間內,覺得好像也沒有會什麼,一直對自己的能力保持懷疑。

在這段時間內,有過跑去搞嵌入式,硬件開發,甚至是產品設計、界面設計,視頻製作。但都不一而終。

一段迷茫,一段頹廢。一不小心就成為校園裡的學長,於是開始緊緊茫茫的尋找實驗室。

新的方向

好在我們專業與生科院其它專業相比,還是帶有工科氣質的。醫學影像學本身就是這個專業最為重要的一個方向。因此懷着對編程的興趣,加入了一個專門做醫學影像的實驗室。

進入實驗室,不再是之前的漫無目的,隨處拾荒,但卻也並不會接觸到前沿新興的技術。畢竟在實驗室里,工程技術更多的只是工具。

不過值得一提的是,在實驗室里,我接觸到了算法思想。醫學圖像本來就是圖像處理領域的一大分支,因此圖像處理算法的了解同樣也是至關重要。

從這個時候開始,我已經不再搞網站開發那一套東西,專心使用C++寫我的算法。那時候深度學習還沒那麼熱門,大多數的圖像處理方式還是基於傳統的算法進行。雖然算法能力薄弱,但是倒也是我所求。

同時,為了獲得數據,還接觸到Linux系統以及一些腳本,在做研究的過程中也大大增加自己的技術面。

與此同時,為了鞏固自己的一些技術基礎,也為了督促自己的學習,我報考了計算機等級考試和國家軟考。可能對於計算機專業的學生來說,沒有什麼含金量,輕而易舉。但是對於一個非科班的學生而言,這樣的考試可以很大程度上幫助自己去重新組織零碎的知識。畢竟不是所有人都有足夠的精力去跟着計算機系的人上課。

本科階段的經歷讓我知道自己與計算機系的差距,雖然接觸了許多東西,但是也都不夠精,甚至沒有底氣去獨立承擔一個小的開發任務。

因此最終選擇了讀研。希望讓自己的技術都夠在某一個點上精進,能夠獨立的做出點東西來。當然最終的結果可能並不是自己當初所想,但是說到底還是得感謝在研究生期間所做出的努力。

三 碩士階段

我研究生的生活是從大四就開始的。因為是本校保研生,所以在大三下確定好實驗室后,基本就直接開始搬磚的苦逼生活了。當然舍友和同學在為畢業而慶祝歡快的時候,我就開始在實驗室早出晚歸。

讀研的開始

進入實驗室之後,剛開始一段時間也是沒有人帶。這能靠自己去多多實驗室專業方向的文章,然後學一點基本技能。更重要的其實是為了在實驗室混個臉熟。

實驗室的研究方向主要是核醫學成像系統,整個系統的搭建涉及到核科學研究、物理數學模型的建立、电子信息的數據獲取、自動化控制設計、机械結構設計、軟件和算法處理以及醫學實驗驗證等一系列的過程。

但對我們個人來說,主要是做某一個方向即可。當時我還是自認有一些編程基礎,於是計劃去做軟件開發或者數據處理的方向。但是最初期是沒有人帶的,所以就花三天時間自學了python3,把廖雪峰的python教程從頭到尾走了一遍。

可惜的是,學完之後也並沒有派上用場。

之後實驗室新項目確立,我被導師安排到了一個新項目組裡。在項目組中,主要負責的是圖像重建的部分。圖像重建是醫學影像領域非常重要的一個研究方向,目的是將影像系統採集到的多維多尺度的信息根據成像原理,依照不同算法還原成二維或三維的斷層圖像。所以本質上來說,圖像重建的重點也是在於算法設計和優化。

方向的多變

開始所在的項目組內,主要的工作還是基於軟件編程和算法開發,涉及到的技術棧主要還是以C++為主,然後加上一些圖型庫,如QtopenGLVTK等等。不過都是處於調用函數調包的階段,也沒有很深入的研究算法原理和實現機制。實話說其實個人提升不大。

過了一段時間后,實驗室師兄拉我去幫他做事。師兄本身能力非常出眾,科研水平不容置疑,所以當時就過去了。之後在他手下做的事就很多變了,不只是軟件開發,還涉及到数字電路編程,PCB制板,系統仿真等。

那個時候還來實驗室沒多久,沒有仔細的想過自己之後的就業方向,只想着能夠在研究生期間多做些成果,多發文章。因此,跟着師兄搬磚的時候也沒有過多考慮對之後的求職是否有幫助,只是想把事情做好,做出有意義的成果。

不過幸運的是,在大四暑假前,也就是研究生入學之前。根據師兄的指導,將一個軟件開發的工作給擴充,投了一篇領域內的頂會。雖然覺得所作的工作沒什麼特別的,但是最終文章被錄還是非常開心的。也正因為這個事,之後更想着能夠多發文章多出成果。同時,師兄也比較會熬雞湯,各種說辭讓我放佛感覺到即將成為科研巨人,登上學校官網,走上人生巔峰。

綜合能力鍛煉

在實驗室,除了完成自己的科研任務,還有很重要的一部分,就是寫本子。也就是申請項目基金等等文字工作。

我估計有參与過的項目申請或結題工作不下十餘項,每次都會涉及到大量文字的整理、表達、排版等工作。一般的學生對於這樣的工作都是極度排斥的,覺得很煩又沒什麼用。當然我也是這樣覺得的。

只不過在寫了大量的本子後會發現,其實這也是綜合能力的鍛煉。我們導師常說,博士生一定要會寫項目,碩士生的話盡量寫。說明他把這也是當作一種能力的培養。在師兄手下,我也經常性的寫本子,甚至於超過了敲的代碼。那時候也沒有很明確的技術培養規劃,所以倒也覺得還行。

獨自前行

只不過好景不長。帶我的師兄由於過於優秀,將要離開實驗室去其它高校就職。這對當時的我來說,處境有些許尷尬。因為我的研究方向是與師兄的工作有交叉的,同時一些設備和材料也需要師兄的支持。

實驗室本身的氛圍是以博士帶頭,碩士輔助進行項目開展的,而我的研究方向是我自己獨立出來的一個題目。說白了就是實驗室就我一個人在搞這個,但是這個方向不是導師關注的方向。

因此,在這之後,我又回歸到了一個人做事的階段。大概是研一下開始,我就只能自己一個人去想自己的研究課題,偶爾會跟老師討論,但是也沒有很確定的工作路線。具體的工作內容也只能靠自己去思索。

在這段期間,我寫過verilog,調過FPGA,畫過板子,畫過工程圖,當然也寫過軟件做過算法。一個人做事的話,可以說是很自由,沒有老師管,也沒有報告的壓力。只是很多時候發現有一些想法沒辦法實現,也無法對前沿領域有很深的洞察。空有一腔發文章報效實驗室的熱血,卻發現像是站在水中的浮木上,搖搖欲墜。當然這其中,個人的問題佔有很大比重。

摸魚的日子

不敢說自己的有多麼自律,多麼出色。可能世上大多人都是芸芸眾生,我也不過一個普通人。在自己做研究的同時,每次到臨近頂會收集文章摘要之時,我都會很积極的熬個一兩個月,將自己的想法和結果寫成一到兩篇的會議文章。我自己也大概知道其實工作沒有太大的突破點,但也是希望能夠賭一把。

只不過兩年來,一共整理出數篇會議摘要,都沒有被同意投稿。每一次還是會有些灰心,也有些不服。會時刻回想起導師的反饋,而後想到一堆反駁的理由。只不過都沒用。

每一次之後,都會有一段時間的消沉。不太想去實驗室,不太想看論文,在實驗室可能也就是摸摸魚罷了。很多時候覺得鬥志滿滿,又會瞬間像泄了氣的皮球,沒有精力去完成任何事。

職業方向

到如今,算是大四的時光,在實驗室已經呆了快四年。平心而論,沒有做過什麼突出的成果,也沒有練就什麼出色的技術。研究生活高開低走,一度以為自己是什麼科研巨星,後來僅剩的一絲熱情也在導師的「也沒看出你有什麼天分」中黯然消逝。不過還好,在這之前,我找到了自己的職業方向。

我本身就是一個比較後知后覺的人,一方面是懶,另一方面又對自己有一種迷之自信。我真正意義上開始準備校招還是在去年六月底七月初的時候,也就是研二下學期快結束的時候吧。我們實驗室的碩士生出去基本就是兩個方向,軟件或者硬件。搞電路的基本都選擇去做硬件開發,其它的大部分都會選擇做軟件。我也沒啥可想的,雖然做過硬件,但是技術水平根本不敢出手。所以求職的方向直接就定在了軟件開發相關。

當時還不知道應聘互聯網還要刷題,還要複習,以為上去介紹下自己,講講自己做的跟互聯網沒有半毛錢關係的項目就可以顯得很有想法的樣子。後來真正開始準備的時候才發現,自己原來還差得有點遠。

老實說在研究生期間,主要使用C++Matlab,主要的項目就是用Qt寫了一個客戶端,裏面有網絡通信的模塊可以對數據做一些處理,並且能夠显示圖譜。用Matlab主要就是做了一些比較基礎的圖像處理和機器學習的算法。光這項目再加上非科班帶「生物」二字的專業,的確讓不清楚的人會十動然拒。

不過其實也沒得選擇。生醫專業對口的公司都是做醫療器械,比如聯影、邁瑞等。但是其實去這些公司也是做軟件開發工程師。相對來說,互聯網公司技術好,待遇好,發展好,自然就成為大部分的選擇去向。

說是跨行,但是其實也沒有別的更好的選擇。我們老師常說希望我們學生以後能夠在我們這個領域發光發熱。這倒是真的,國家的發展還是離不開這些能夠在某個領域深耕的人,而不是為了一昧追求熱點和高薪而忘記了初心。

四 求職經歷

講講我的秋招經歷吧。我的秋招雖然準備的晚,但是其實還是挺順利的。從19年8月開始接到面試,2個月內已經拿到了15+的offer,基本平均薪資都在30w+。包括抖音美團華為小米等等。那段時間真的是狀態上來了,就是佛擋殺佛,神擋殺神。

不過剛開始的時候,由於實驗室的原因,沒有辦法出去實習。甚至由於一些原因,一直拖到暑期前才開始準備複習。

那時已經快七月初了。急匆匆的登上各種學習網站,發現這也太多要看了的吧,還得刷題。關鍵一看面經,這都啥呀。還要手撕代碼的嗎?

這一看當時不要緊,關鍵晚上就焦慮的快睡不着了。每天都在想應該怎樣複習,怎麼寫簡歷,沒有項目該怎麼辦。

剛開始的時候連簡歷都不敢投,因為老覺得簡歷一過去就會安排面試。後來發現這完全就是多慮了。大概從七月初我就開始投簡歷,因為七八月是一些公司提前批招聘的階段。

許多非科班的學生,在投遞簡歷的時候才能發現自己的無助。我在簡歷投遞初期,基本沒有任何反饋。提前批階段,許多公司都會去爭奪更優秀的簡歷候選者,對於生物專業的學生真的沒有什麼優勢。

但是沒有關係,既然選擇了這條路,那麼就要堅持下去。投一家無人應答,那麼就投十家,投五十家。我在整個秋招階段,總共投遞過近一百家公司。許多在提前批沒有給予反饋的公司,後期大部分都有電話聯繫重新開啟面試流程。所以,就算認為自己的簡歷再不夠出色,也要相信總會有瞎了眼的HR(誤)。

我當時一直在堅持投簡歷,只要看到的招聘信息都會去投遞。還記得第一次做測評題的時候都非常緊張,以為這就是筆試題。非常認真的拿紙筆在計算,慌的不行。

後來直到八月初才收到第一份面試邀請,多益網絡。當時約了面試之後簡直怕死了,雖然說複習了一個月大部分的 C++ 基礎知識都看得差不多了,但是肯定是不夠的。面試的時候面試小哥全程就低頭照着題庫念,也不看我。我這邊的音頻信號也不太行,他那邊說話都聽不清楚。兩個人就在無數次的重複和確認。關鍵是面試完之後我自我感覺還非常良好。

最終結果還是掛了,說實話打擊挺大的,感覺因此對面試產生了恐懼。不過後來試着自己跟自己講解,慢慢的也習慣了面試的感覺。

隨後在八月底的時候,終於收穫了第一家 ihandy 的 offer。並且在進行總管面的時候,跟面試官進行了深入的交談。面完之後讓我有一種都不好意思拒絕這家公司的念頭。這也是第一次感受到了面試官的認可。

隨後,便一發不可收拾。這個時候,已經準備了大概兩個月,基本的技術知識我都看完了,劍指 offer 上的66題以及 leetcode 上基本的題也大概刷了兩次。同時獲得了一個 offer 之後,對自己的認可度非常高,使得面試的狀態非常好。

後來給了面試機會的公司基本都拿到了 offer。像字節跳動、美團、虎牙等等公司,面完的感覺就是基本穩了。不過可惜的是阿里簡歷面后,內推人開始說通過了,後來不知道什麼情況流程就拖到結束了。

騰訊也一直沒有撈過我面試。感覺如果在狀態頂峰的情況下能有面試機會的話,還是很有希望的。不過也說明簡歷依然不夠出色,非科班沒有實習經歷,項目也比較水,導致AT大廠連面試機會都沒給。這也是秋招比較遺憾的地方。

另外,記住面試過程,跟投遞簡歷一樣,一定要多面多總結。

如果你的表達能力不好,沒有別的好的辦法,只能多練。自己在面試前問自己問題,然後用自己的話陳述出來。甚至是錄音自己聽,感覺一下面試官聽到你的回答是做何感想。

心態要好。面試官也是人,不可能所有人都能夠絕對公平的跟你面試,所以遇到人品不好的面試官,做好自己就行了。

要善於總結。每一次的面試都可以做好記錄,錄音或者筆記都可以。面試完之後需要多回顧,發現自己的錯誤,感受面試官對你的引導,然後下次面試注意。我一般喜歡用印象筆記記錄東西,每一次的面試記錄我都記錄在印象筆記上。電腦手機都可以看,即使是出門現場面試也不怕。

最後就是一定要堅持下去。金九銀十,金三銀四。把握好機會,要善於規劃自己的成功。

五 複習準備

想進互聯網的技術崗,基本都是要提前準備的。當然某些大佬及大大佬除外。無論是校招還是社招,都需要針對自己的求職崗位進行必要的理論知識複習、項目經歷反思和算法能力訓練。只不過校招會偏向於基礎和算法能力,社招可能都會重點考察。

除項目經歷外,複習的階段主要分為語言基礎、數據結構和算法、計算機網絡、操作系統、數據庫以及算法刷題。

語言基礎:

以 C++ 為主。我不喜歡看又厚又重的語言書,因此複習全程是以博客、開發文檔和實踐相結合的方式進行技術點複習。C++ 的技術點相對於其它語言來說,不算多,也不算難。技術重點的篩選可以從面經中提取而來。當然每一屆都差不多,所以找找別人總結好的資料看就行。(想要我複習資料的,可關注公眾號後台回復秋招領取)

數據結構和算法:

這應該是編程的基礎,重要需要了解的數據結構不出10種。花點時間弄清楚它們的原理、結構和使用方法,常用的操作也需要掌握。最難不過紅黑樹。

這裏的算法指的是常用的算法,比如排序、遍歷,與數據結構相結合的數據操作方式。需要保證手寫才行。

計算機網絡:

網絡部分的內容其實可以算是最重要的,無論是前端後端都需要掌握網絡通信過程中的操作和機制。技術點可參考網絡服務器的請求和響應過程。將其中所有涉及到的協議、機制了解清楚,就可以掌握大部分了。

操作系統:

如果有Linux使用經歷和腳本編程基礎在面試中會很加分。對於操作系統的理解建議按照Linuxwindos系統的區分進行。

數據庫:

SQL基本操作必須要掌握,還包括一些關係型數據庫的基本原理和機制,內容不多,多看看就可以掌握。

Redis同樣也是加分項,有能力的可去研究下源碼。

算法刷題

這一部分不多說,普通人只能勤能補拙。無論是劍指offer66題還是leetcode都可以,劍指刷兩遍,leetcode兩百題,基本沒有問題了。刷題時不要死刷,可以根據類型刷,比如鏈表操作、二叉樹操作、動態規劃等。相同類型重複做,能夠更好的培養算法思想。

六 感想體會

說起求職的過程,其實真要我來評價的話,估計運氣是佔了一大部分,連我這樣都行,你們也可以的。不過最終能夠獲得一些互聯網公司認可的原因,我認為主要有以下幾點原因吧:

1.本科階段接觸互聯網行業比較早,對於這個行業有自己的見解。

2.本科階段有考過一些計算機水平的證書,大概系統的學了一下計算機相關的基礎知識。

3.用C++比較多,對語言基礎的理解比較好。

4.面試狀態比較好,比較會表達自己的想法。

5.學習能力還行,能夠在面試官的引導下找到他想問的技術點。

6.準備過程比較有規劃,能夠快速的掌握面試的重點。

7.人長得老實,比較容易獲得信任感。(這個你們可能學不了^_^)

8.比較幸運。

前天剛碩士答辯完,這两天也把學位申請的各種材料提交上去了。一不小心,七年的大學校園時光真的要結束了,兜兜轉轉感覺好像依舊是一無是處,一事無成。但是依舊希望以後能夠:

二龍騰飛、三羊開泰、四季平安、 五福臨門、六六大順、七星高照、八方來財、九九同心、十全十美、百事亨通、千事吉祥、萬事如意

人生無常,活在當下,且行且珍惜!

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

【其他文章推薦】

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

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

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

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

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

【C#】AutoMapper 使用手冊

目錄

  • 1 入門例子
  • 2 註冊
    • 2.1 Profile
  • 3 配置
    • 3.1 命名約定
    • 3.2 配置可見性
    • 3.3 全局屬性/字段過濾
    • 3.4 識別前綴和後綴
    • 3.5 替換字符
  • 4 調用構造函數
  • 5 數組和列表映射
    • 5.1 處理空集合
    • 5.2 集合中的多態
  • 6 方法到屬性映射
  • 7 自定義映射
  • 8 扁平化映射
    • 8.1 IncludeMembers
  • 9 嵌套映射

本文基於 AutoMapper 9.0.0

AutoMapper 是一個對象-對象映射器,可以將一個對象映射到另一個對象。

官網地址:http://automapper.org/

官方文檔:https://docs.automapper.org/en/latest/

1 入門例子

public class Foo
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class FooDto
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public void Map()
{
    var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());

    var mapper = config.CreateMapper();

    Foo foo = new Foo { ID = 1, Name = "Tom" };

    FooDto dto = mapper.Map<FooDto>(foo);
}

2 註冊

在使用 Map 方法之前,首先要告訴 AutoMapper 什麼類可以映射到什麼類。

var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());

每個 AppDomain 只能進行一次配置。這意味着放置配置代碼的最佳位置是在應用程序啟動中,例如 ASP.NET 應用程序的 Global.asax 文件。

從 9.0 開始 Mapper.Initialize 方法就不可用了。

2.1 Profile

Profile 是組織映射的另一種方式。新建一個類,繼承 Profile,並在構造函數中配置映射。

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<Employee, EmployeeDto>();
    }
}

var config = new MapperConfiguration(cfg =>
{
    cfg.AddProfile<EmployeeProfile>();
});

Profile 內部的配置僅適用於 Profile 內部的映射。應用於根配置的配置適用於所有創建的映射。

AutoMapper 也可以在指定的程序集中掃描從 Profile 繼承的類,並將其添加到配置中。

var config = new MapperConfiguration(cfg =>
{
    // 掃描當前程序集
    cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
    
    // 也可以傳程序集名稱(dll 名稱)
    cfg.AddMaps("LibCoreTest");
});

3 配置

3.1 命名約定

默認情況下,AutoMapper 基於相同的字段名映射,並且是 不區分大小寫 的。

但有時,我們需要處理一些特殊的情況。

  • SourceMemberNamingConvention 表示源類型命名規則
  • DestinationMemberNamingConvention 表示目標類型命名規則

LowerUnderscoreNamingConventionPascalCaseNamingConvention 是 AutoMapper 提供的兩個命名規則。前者命名是小寫並包含下劃線,後者就是帕斯卡命名規則(每個單詞的首字母大寫)。

我的理解,如果源類型和目標類型分別採用了 蛇形命名法駝峰命名法,那麼就需要指定命名規則,使其能正確映射。

public class Foo
{
    public int Id { get; set; }

    public string MyName { get; set; }
}

public class FooDto
{
    public int ID { get; set; }

    public string My_Name { get; set; }
}

public void Map()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Foo, FooDto>();

        cfg.SourceMemberNamingConvention = new PascalCaseNamingConvention();
        cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
    });

    var mapper = config.CreateMapper();

    Foo foo = new Foo { Id = 2, MyName = "Tom" };

    FooDto dto = mapper.Map<FooDto>(foo);
}

3.2 配置可見性

默認情況下,AutoMapper 僅映射 public 成員,但其實它是可以映射到 private 屬性的。

var config = new MapperConfiguration(cfg =>
{
    cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.SetMethod.IsPrivate;
    cfg.CreateMap<Source, Destination>();
});

需要注意的是,這裏屬性必須添加 private set,省略 set 是不行的。

3.3 全局屬性/字段過濾

默認情況下,AutoMapper 嘗試映射每個公共屬性/字段。以下配置將忽略字段映射。

var config = new MapperConfiguration(cfg =>
{
	cfg.ShouldMapField = fi => false;
});

3.4 識別前綴和後綴

var config = new MapperConfiguration(cfg =>
{
    cfg.RecognizePrefixes("My");
    cfg.RecognizePostfixes("My");
}

3.5 替換字符

var config = new MapperConfiguration(cfg =>
{
    cfg.ReplaceMemberName("Ä", "A");
});

這功能我們基本上用不上。

4 調用構造函數

有些類,屬性的 set 方法是私有的。

public class Commodity
{
    public string Name { get; set; }

    public int Price { get; set; }
}

public class CommodityDto
{
    public string Name { get; }

    public int Price { get; }

    public CommodityDto(string name, int price)
    {
        Name = name;
        Price = price * 2;
    }
}

AutoMapper 會自動找到相應的構造函數調用。如果在構造函數中對參數做一些改變的話,其改變會反應在映射結果中。如上例,映射后 Price 會乘 2。

禁用構造函數映射:

var config = new MapperConfiguration(cfg => cfg.DisableConstructorMapping());

禁用構造函數映射的話,目標類要有一個無參構造函數。

5 數組和列表映射

數組和列表的映射比較簡單,僅需配置元素類型,定義簡單類型如下:

public class Source
{
    public int Value { get; set; }
}

public class Destination
{
    public int Value { get; set; }
}

映射:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source, Destination>();
});
IMapper mapper = config.CreateMapper();

var sources = new[]
{
    new Source { Value = 5 },
    new Source { Value = 6 },
    new Source { Value = 7 }
};

IEnumerable<Destination> ienumerableDest = mapper.Map<Source[], IEnumerable<Destination>>(sources);
ICollection<Destination> icollectionDest = mapper.Map<Source[], ICollection<Destination>>(sources);
IList<Destination> ilistDest = mapper.Map<Source[], IList<Destination>>(sources);
List<Destination> listDest = mapper.Map<Source[], List<Destination>>(sources);
Destination[] arrayDest = mapper.Map<Source[], Destination[]>(sources);

具體來說,支持的源集合類型包括:

  • IEnumerable
  • IEnumerable
  • ICollection
  • ICollection
  • IList
  • IList
  • List
  • Arrays

映射到現有集合時,將首先清除目標集合。如果這不是你想要的,請查看AutoMapper.Collection。

5.1 處理空集合

映射集合屬性時,如果源值為 null,則 AutoMapper 會將目標字段映射為空集合,而不是 null。這與 Entity Framework 和 Framework Design Guidelines 的行為一致,認為 C# 引用,數組,List,Collection,Dictionary 和 IEnumerables 永遠不應該為 null

5.2 集合中的多態

這個官方的文檔不是很好理解。我重新舉個例子。實體類如下:

public class Employee
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class Employee2 : Employee
{
    public string DeptName { get; set; }
}

public class EmployeeDto
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class EmployeeDto2 : EmployeeDto
{
    public string DeptName { get; set; }
}

數組映射代碼如下:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Employee, EmployeeDto>().Include<Employee2, EmployeeDto2>();
    cfg.CreateMap<Employee2, EmployeeDto2>();
});
IMapper mapper = config.CreateMapper();

var employees = new[]
{
    new Employee { ID = 1, Name = "Tom" },
    new Employee2 { ID = 2, Name = "Jerry", DeptName = "R & D" }
};

var dto = mapper.Map<Employee[], EmployeeDto[]>(employees);

可以看到,映射后,dto 中兩個元素的類型,一個是 EmployeeDto,一個是 EmployeeDto2,即實現了父類映射到父類,子類映射到子類。

如果去掉 Include 方法,則映射后 dto 中兩個元素的類型均為 EmployeeDto

6 方法到屬性映射

AutoMapper 不僅能實現屬性到屬性映射,還可以實現方法到屬性的映射,並且不需要任何配置,方法名可以和屬性名一致,也可以帶有 Get 前綴。

例如下例的 Employee.GetFullName() 方法,可以映射到 EmployeeDto.FullName 屬性。

public class Employee
{
    public int ID { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string GetFullName()
    {
        return $"{FirstName} {LastName}";
    }
}

public class EmployeeDto
{
    public int ID { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string FullName { get; set; }
}

7 自定義映射

當源類型與目標類型名稱不一致時,或者需要對源數據做一些轉換時,可以用自定義映射。

public class Employee
{
    public int ID { get; set; }

    public string Name { get; set; }

    public DateTime JoinTime { get; set; }
}

public class EmployeeDto
{
    public int EmployeeID { get; set; }

    public string EmployeeName { get; set; }

    public int JoinYear { get; set; }
}

如上例,IDEmployeeID 屬性名不同,JoinTimeJoinYear 不僅屬性名不同,屬性類型也不同。

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Employee, EmployeeDto>()
        .ForMember("EmployeeID", opt => opt.MapFrom(src => src.ID))
        .ForMember(dest => dest.EmployeeName, opt => opt.MapFrom(src => src.Name))
        .ForMember(dest => dest.JoinYear, opt => opt.MapFrom(src => src.JoinTime.Year));
});

8 扁平化映射

對象-對象映射的常見用法之一是將複雜的對象模型並將其展平為更簡單的模型。

public class Employee
{
    public int ID { get; set; }

    public string Name { get; set; }

    public Department Department { get; set; }
}

public class Department
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class EmployeeDto
{
    public int ID { get; set; }

    public string Name { get; set; }

    public int DepartmentID { get; set; }

    public string DepartmentName { get; set; }
}

如果目標類型上的屬性,與源類型的屬性、方法都對應不上,則 AutoMapper 會將目標成員名按駝峰法拆解成單個單詞,再進行匹配。例如上例中,EmployeeDto.DepartmentID 就對應到了 Employee.Department.ID

8.1 IncludeMembers

如果屬性命名不符合上述的規則,而是像下面這樣:

public class Employee
{
    public int ID { get; set; }

    public string Name { get; set; }

    public Department Department { get; set; }
}

public class Department
{
    public int DepartmentID { get; set; }

    public string DepartmentName { get; set; }
}

public class EmployeeDto
{
    public int ID { get; set; }

    public string Name { get; set; }

    public int DepartmentID { get; set; }

    public string DepartmentName { get; set; }
}

Department 類中的屬性名,直接跟 EmployeeDto 類中的屬性名一致,則可以使用 IncludeMembers 方法指定。

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Employee, EmployeeDto>().IncludeMembers(e => e.Department);
    cfg.CreateMap<Department, EmployeeDto>();
});

9 嵌套映射

有時,我們可能不需要展平。看如下例子:

public class Employee
{
    public int ID { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    public Department Department { get; set; }
}

public class Department
{
    public int ID { get; set; }

    public string Name { get; set; }

    public string Heads { get; set; }
}

public class EmployeeDto
{
    public int ID { get; set; }

    public string Name { get; set; }

    public DepartmentDto Department { get; set; }
}

public class DepartmentDto
{
    public int ID { get; set; }

    public string Name { get; set; }
}

我們要將 Employee 映射到 EmployeeDto,並且將 Department 映射到 DepartmentDto

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Employee, EmployeeDto>();
    cfg.CreateMap<Department, DepartmentDto>();
});

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案