PIXNET Logo登入

互聯網 - 大數據

跳到主文

本部落格為互聯網熱門頭條訊息管理中心

部落格全站分類:生活綜合

  • 相簿
  • 部落格
  • 留言
  • 名片
  • 3月 09 週四 201720:38
  • ASP.NET Core 之 Identity 入門(三)

文章出處
前言
在上一篇文章中,我們學習了 CookieAuthentication 中間件,本篇的話主要看一下 Identity 本身。
最早2005年 ASP.NET 2.0 的時候開始, Web 應用程序在處理身份驗證和授權有了很多的變化,多了比如手機端,平板等,所以那個時候為了適應這種變化就引入了ASP.NET Membership,但是隨著時間的發展一些社交網站或者程序聚集了大量的用戶,比如Facebook,Twitter,QQ等,這個時候用戶希望能夠使用他們在這些社交站點身份來登陸當前網站,這樣可以免除注冊這些瑣碎而又必要的操作,用戶也不必記住大量的賬戶密碼。
又隨著互聯網的發展,越來越多的開發者不只是關注具體業務代碼的編寫,轉變為開始關注應用程序代碼的單元測試,這已經是開發者關注的核心。所以在2008年,ASP.NET 團隊引入了 MVC 框架,這樣來幫助開發者很方便的構建單元測試,同時開發者希望他們的 Membership 系統也能夠做到這一點。
基于以上,ASP.NET Identity 應運而生。
Identity 要解決的問題
很多開發人員說他們不愿意使用Identity,自己實現要方便的多,OK,那么需求來了?以下就是我針對此次任務給你提出來的需求。
身份系統
  • 可以同時被所有的ASP.NET 框架使用(Web MVC,Web Forms,Web Api,SignalR)

  • 可以應用于構建 Web, 手機,存儲,或者混合應用。

  • 能夠對用戶資料(User Profile)很方便的擴展
  • 可以針對用戶資料進行擴展。

  • 持久化
  • 默認把用戶信息存儲在數據庫中,可以支持使用EF進行持久化。(可以看到,EF 其實只是Identity的一個功能點而已)

  • 可以控制數據庫架構,更改表名或者主鍵的數據類型(int,string)

  • 可以使用不同的存儲機制(如 NoSQL,DB2等)

  • 單元測試
  • 使WEB 應用程序可以進行單元測試,可以針對ASP.NET Identity編寫單元測試

  • 角色機制
  • 提供角色機制,可以使用不同的角色來進行不同權限的限制,可以輕松的創建角色,向用戶添加角色等。

  • 要支持基于Claims
  • 需要支持基于 Claims 的身份驗證機制,其中用戶身份是一組Claims,一組Claims可以比角色擁有更強的表現力,而角色僅僅是一個bool值來表示是不是會員而已。

  • 第三方社交登陸
  • 可以很方便的使用第三方登入,比如 Microsoft 賬戶,Facebook, Twitter,Google等,并且存儲用戶特定的數據。

  • 封裝為中間件
  • 基于中間件實現,不要對具體項目產生依賴

  • 基于 Authorzation 中間件實現,而不是使用 FormsAuthentication 來存儲cookie。

  • NuGet包提供
  • 發布為 Nuget 包,這樣可以容易的進行迭代和bug修復,可以靈活的提供給使用者。

  • 以上,就是我提出來的需求,如果讓你來封裝這樣一個用戶身份認證組件,你會不是想到以上的這些功能點,那針對于這些功能點你又會怎么樣來設計呢?
    下面來看一下 Identity 怎么樣設計的吧。
    Getting Started
    抽絲剝繭,我們先從入口看一下其使用方式。 首先我們打開 Startup.cs 文件,然后添加如下代碼:
    public class Startup
    {
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
    services.AddIdentity<ApplicationUser, IdentityRole>(options => {
    options.Cookies.ApplicationCookie.AuthenticationScheme = "ApplicationCookie";
    options.Cookies.ApplicationCookie.CookieName = "Interop";
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();
    }
    public void Configure(IApplicationBuilder app)
    {
    // 使用了 CookieAuthentication 中間件做身份認證
    app.UseIdentity();
    }
    }

    在 ConfigureServices 中,先是注冊了數據庫上下文,然后又 services.AddIdentity() 我們看一下里面都注冊了哪些服務呢?
    public static IdentityBuilder AddIdentity<TUser, TRole>(
    this IServiceCollection services,
    Action<IdentityOptions> setupAction)
    where TUser : class
    where TRole : class
    {
    // 這個就是被 Identity 使用的
    services.AddAuthentication(options =>
    {
    // This is the Default value for ExternalCookieAuthenticationScheme
    options.SignInScheme = new IdentityCookieOptions().ExternalCookieAuthenticationScheme;
    });
    // 注冊 IHttpContextAccessor ,會用到
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    // Identity services
    services.TryAddSingleton<IdentityMarkerService>();
    services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
    services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
    services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
    services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
    services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
    // 錯誤描述信息
    services.TryAddScoped<IdentityErrorDescriber>();
    services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
    //身份當事人工廠
    services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
    //三大對象
    services.TryAddScoped<UserManager<TUser>, UserManager<TUser>>();
    services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
    services.TryAddScoped<RoleManager<TRole>, RoleManager<TRole>>();
    if (setupAction != null)
    {
    services.Configure(setupAction);
    }
    return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
    }

    看了以上代碼后,基本上知道了 Identity 他的設計的一個架構了,通過此結構我們也能夠學習到我們自己封裝一個中間件的時候,該怎么樣來組織我們的代碼結構,怎么樣的利用 ASP.NET Core 給我們提供的依賴注入來更好的解耦,下面我們來看一下通過以上代碼我們能夠學到什么東西:
    1、 在 public static IdentityBuilder AddIdentity<TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction) 這個擴展方法中,提供了一個參數 Action<IdentityOptions>,這個是干什么用的呢? 這個是我們在設計一個中間件的時候,有需要外部提供的參數,就會設計一個 Options 的類用來接受外部的參數,然后封裝為一個Action委托的方式提供。在使用到的地方就可以以 IOption xxx 的形式注入進來使用了。


    2、services.TryAddScoped<Interface,Implement>(),這個注冊方式就是說,如果檢測到DI容器里面已經有了當前要注冊的Interface或者Service,就不會再次注冊,沒有才會注冊進去。 那么為什么此處要這樣用呢? 這是因為如果用戶已經實現了此接口并且已經注冊的容器當中的話,就使用用戶注冊的,而不是中間件自身的。用戶就能很好的對中間件提供的功能進行自定義了,這就是OO中的多態性,這就是里氏替換原則。


    3、如果你能理解第2條的話,那么你應該知道為什么會在服務中注冊 錯誤描述信息 那個IdentityErrorDescriber 了吧,也能夠解決你想提示賬號密碼錯誤,無奈Identity輸出是英文問題的提示。


    4、三大對象,這個是 Identity 的核心了,所以學習 Identity 的話,在看完博客 ASP.NET Core 之 Identity 入門(一,二)之后,學這三個對象就夠了。


    SignInManager: 主要處理注冊登錄相關業務邏輯。


    UserManager: 處理用戶相關添加刪除,修改密碼,添加刪除角色等。


    RoleManager:角色相關添加刪除更新等。


    有些同學可能很好奇,都沒有依賴具體的數據庫或者是EF,是怎么樣做到的增刪改查的呢?


    這個時候,就需要幾個 Store 接口派上用場了。以下是Identity中定義的Store接口:



    • IQueryableRoleStore

    • IQueryableUserStore

    • IRoleClaimStore

    • IRoleStore

    • IUserAuthenticationTokenStore

    • IUserClaimStore

    • IUserEmailStore

    • IUserLockoutStore

    • IUserLoginStore

    • IUserPasswordStore

    • IUserPhoneNumberStore

    • IUserRoleStore

    • IUserSecurityStampStore

    • IUserStore

    • IUserTwoFactorStore


    有了這些接口之后,是不是豁然開朗了,原來 Identity 是通過這種方式實現的持久化機制,依賴抽象接口而不是依賴具體的細節實現,這就是面向對象中的依賴倒置原則呀。


    Identity 和 EntityFramework


    Identity 和 EntityFramework的關系,相信上個章節看懂了之后,就很容易明白了,對的,EF 只是針對于上述 Store 接口的實現,不信你看截圖的源碼:



    Identity 打頭的那些類文件都是定義的需要持久化的Entity對象,Store結尾的那些就是接口的實現啦。


    第三方的 Identity 實現


    除了 EF 是官方默認提供的持久化庫之外,還有一些第三方的庫,當然你也可以自己使用 ADO.NET 或者 Drapper 實現。


    MangoDb 針對于 Identity 提供的實現: https://github.com/tugberkugurlu/AspNetCore.Identity.MongoDB


    LinqToDB 針對于 Identity 提供的實現:https://github.com/linq2db/LinqToDB.Identity


    總結


    這篇博文寫了蠻久的時間的,一方面是因為在構思怎么樣的思路來讓大家更好的理解,而不僅僅是使用。因為有太多的文章介紹Identity 的使用方式以及代碼了,但是最后大家還是不會用。后來想到如果讓別人想要理解你的庫也好代碼也好,讓其知道誕生的背景是很重要的,因為這才是設計的初衷。另一方面是因為Connect() 2016 大會上,.NET Core 發布了 1.1 版本,除了把項目升級到1.1之外,也在學習1.1新的一些東西,以便更好給大家分享。


    授人以魚不如授人以漁, 一篇好的博文確實要花費作者更多的心血和時間。如果你覺得這篇博文對您有幫助,感謝您的【推薦】。


    同時,ASP.NET Core 之 Identity 系列也結束了,感謝支持的朋友們。如果你對 .NET Core 感興趣可以關注我,我會定期在博客分享關于 .NET Core 的學習心得。





    本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接



    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(609)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • .NET Core 首例 Office 開源跨平臺組件(NPOI Core)

    image
    文章出處
    前言
    最近項目中,需要使用到 Excel 導出,找了一圈發現沒有適用于 .NET Core的,不依賴Office和操作系統限制的 Office 組件,于是萌生了把 NPOI 適配并移植到 .NET Core 的想法。
    NPOI 的介紹不多說了,不了解的可以看一下 NPOI百度百科 的介紹,在此感謝瞿總和他的團隊的貢獻。
    NPOI 的移植之路并非想象的那么容易,因為其依賴了 System.Drawing 和 System.Window.Forms 兩個組件,還有一個第三方的 SharpZipLib 庫,在 GitHub 克隆了最新的代碼并且轉換為 NetStandrad 1.6 編譯之后,出現了數不清的錯誤,應該有上千個吧,在經過一天的努力之后(包括刪除,修改,重寫),錯誤數量已經減少到了100多個,50多個,20多個,編譯通過。
    在移植的過程中可以真切感受到當初NPOI的作者在寫這些代碼時候的辛苦努力,因為NPOI最初是基于 .Net Framework 1.1 框架寫的,那個時候沒有泛型,沒有var,沒有很多的現成的類庫,全都是靠最基礎的一些數據結構來實現,雖然里面的很多種寫法在目前看來可以很大程序的精簡,但是在當時的條件下 真的是不容易。
    在通過編譯之后,心里想著應該問題不大了,于是測試了一下,不幸的是,各種問題,又經過半天的調整之后,打算放棄了。 于是又去 github 上面搜索看看有沒有其他什么解決方案之類的,無意間搜索到了一個 NPOI.Core 的一個項目,是一個老外移植的 NPOI 到Core平臺,原來已經有人做了Core的移植了,克隆下來之后發現編譯不過,又進去看了一下代碼,這個庫目前依賴于Windows平臺,而我們項目是運行在CentOS的,其并不能在Linux上運行,看來還是空歡喜一場。
    怎么辦? 于是,又一次重構開始了,有了前一次的重構經驗之后,這一次可謂是輕車熟路了,NPOI Core 庫 里面使用了很多.NET Core netstandrad 標準不支持的 Hashtable 和 ArrayList 等數據結構,這些已經被新的泛型 Directory 和 List 替代了,還有依賴的 SharpZipLib 等壓縮組件也都替換成了 NetStandrad 的實現,當然還有其他很多雜七雜八的就不細說了,最后,終于 netstandrad 1.6 下編譯通過。
    通過之后,本地 visual studio 下 新建了一個項目,簡單測試了導出 Excel 的功能,沒問題,也沒有報錯,心里很開心...。 這個時候我在想,最關鍵的就是能不能在Linux上正常運行了,其實這個時候我心里想我已經把依賴于.NET Framework 的各種類都換成了net standrad了,應該問題不大了。
    然后在一頓 dotnet publish 之后,把部署包傳到了 Linux 下進行測試,果然,運行通過,并沒有拋出任何異常,而且Excel也生成了,把Excel傳輸到windows上使用office打開,完美...
    然后緊接著就是繼續各種測試了,在測試到 Word 的導出功能時候,出問題了。因為NPOI 天生對Word的功能支持的并不是很完善,心想是不是.NET Framewok下導出也不行啊,于是又新建的一個.NET Framework的項目,使用NPOI Team提供的 dll 測試,發現可以導出。然后就開始跟代碼,各種測試,重構,最終Word的導出功能也沒有問題了。緊接著又發到了Linux上測試,沒有問題,心里的石頭落地了。
    在通過測試之后,本想著把這次調整的代碼以 PR 的形式推送給原作者的時候,發現好像原作者已經不維護這個項目了,無奈,只能自己發布NuGet了。
    于是我就把它重新發布到 NuGet 了,如果使用的過程中有問題,大家可以去我的github下面提交 issue。
    GitHub : https://github.com/yuleyule66/Npoi.Core
    NuGet :
    Getting Started
    導出 Excel
    本示例代碼包含:
  • 多個 Sheet

  • 合并單元格

  • 自動調整列寬

  • 填充背景色

  • var newFile = @"newbook.core.xlsx";
    using (var fs = new FileStream(newFile, FileMode.Create, FileAccess.Write)) {
    IWorkbook workbook = new XSSFWorkbook();
    ISheet sheet1 = workbook.CreateSheet("Sheet1");
    sheet1.AddMergedRegion(new CellRangeAddress(0, 0, 0, 10));
    var rowIndex = 0;
    IRow row = sheet1.CreateRow(rowIndex);
    row.Height = 30 * 80;
    row.CreateCell(0).SetCellValue("這是單元格內容,可以設置很長,看能不能自動調整列寬");
    sheet1.AutoSizeColumn(0);
    rowIndex++;
    var sheet2 = workbook.CreateSheet("Sheet2");
    var style1 = workbook.CreateCellStyle();
    style1.FillForegroundColor = HSSFColor.Blue.Index2;
    style1.FillPattern = FillPattern.SolidForeground;
    var style2 = workbook.CreateCellStyle();
    style2.FillForegroundColor = HSSFColor.Yellow.Index2;
    style2.FillPattern = FillPattern.SolidForeground;
    var cell2 = sheet2.CreateRow(0).CreateCell(0);
    cell2.CellStyle = style1;
    cell2.SetCellValue(0);
    cell2 = sheet2.CreateRow(1).CreateCell(0);
    cell2.CellStyle = style2;
    cell2.SetCellValue(1);
    cell2 = sheet2.CreateRow(2).CreateCell(0);
    cell2.CellStyle = style1;
    cell2.SetCellValue(2);
    cell2 = sheet2.CreateRow(3).CreateCell(0);
    cell2.CellStyle = style2;
    cell2.SetCellValue(3);
    cell2 = sheet2.CreateRow(4).CreateCell(0);
    cell2.CellStyle = style1;
    cell2.SetCellValue(4);
    workbook.Write(fs);
    }

    導出 Word
    本示例代碼包含:
  • 設置段落對其方式

  • 設置段落字體

  • 設置段落縮進

  • 這是字體大小


  • var newFile2 = @"newbook.core.docx";
    using (var fs = new FileStream(newFile2, FileMode.Create, FileAccess.Write)) {
    XWPFDocument doc = new XWPFDocument();
    var p0 = doc.CreateParagraph();
    p0.Alignment = ParagraphAlignment.CENTER;
    XWPFRun r0 = p0.CreateRun();
    r0.FontFamily = "microsoft yahei";
    r0.FontSize = 18;
    r0.IsBold = true;
    r0.SetText("這里是標題");
    var p1 = doc.CreateParagraph();
    p1.Alignment = ParagraphAlignment.LEFT;
    p1.IndentationFirstLine = 500;
    XWPFRun r1 = p1.CreateRun();
    r1.FontFamily = "仿宋";
    r1.FontSize = 12;
    r1.IsBold = true;
    r1.SetText("這里是正文,這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文這里是正文");
    doc.Write(fs);
    }

    總結
    據不完全統計,這應該是 .NET Core 首例 Office 跨平臺 組件了,大家如果在使用的過程中有什么問題,可以在 Github 上提交Issue(盡量使用英文)。當然更希望你能夠提交 PR 來幫助一起完善 NPOI。
    如果你覺得本篇博客對您有幫助的話,感謝您的【推薦】。
    如果你對 .NET Core 感興趣可以關注我,我會定期在博客分享關于 .NET Core 的學習心得。


    本文地址:http://www.cnblogs.com/savorboard/p/netcore-npoi.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(315)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • ASP.NET Core 中間件之壓縮、緩存

    image
    文章出處
    前言
    今天給大家介紹一下在 ASP.NET Core 日常開發中用的比較多的兩個中間件,它們都是出自于微軟的 ASP.NET 團隊,他們分別是
    Microsoft.AspNetCore.ResponseCompression 和 Microsoft.AspNetCore.ResponseCaching , 下面讓我們一起看看的功能以及如何去使用吧。
    Getting Started
    Microsoft.AspNetCore.ResponseCompression
    Microsoft.AspNetCore.ResponseCompression 這個中間件是 .NET Core 1.1 版本中新增加的,看名字應該知道,它主要是負責對輸出的內容進行壓縮, 那么在我們WEB開發中主要就是 GZip 壓縮了。
    Gzip 壓縮是我們在 WEB 中經常會使用的一項性能優化技術,它可以對頁面輸出的內容使用壓縮算法(GZip)進行體積的壓縮, 那在以前的時候,我們可以使用 IIS 來做這項工作,但是現在我們的程序脫離 IIS了,就必須有一個中間件來幫我們做這件事情了,它就是我們要介紹的這個中間件。
    1、添加 Microsoft.AspNetCore.ResponseCompression 包
    你可以使用 Visual Studio 打開 NuGet 包管理器控制臺輸入一下命令安裝
    Install-Package Microsoft.AspNetCore.ResponseCompression
    也可以使用 NuGet包管理器UI界面安裝。
    添加完成之后,你就可以在 project.json 中看到你添加的包了。注意目前版本是 1.0.0.
    2、更新 Startup.cs 文件
    修改 Startup , 在ConfigureServices 和Configure 兩個方法中添加如下代碼:
    public class Startup
    {
    ...
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddResponseCompression();
    ...
    }
    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
    app.UseResponseCompression();
    ...
    }
    }

    現在你就可以測試一下輸入的 Http Response 是否被壓縮了。
    前:
    后:
    通過 前 后 對比,可以看出來,在 Response Headers 里面多了一個 Content-Encoding:gzip 的頭部信息,說明我們的中間件生效了。
    Microsoft.AspNetCore.ResponseCaching
    Microsoft.AspNetCore.ResponseCaching 這個中間件也是 .NET Core 1.1 版本中新增加的,同樣看名字應該知道,它主要是負責對輸出的內容進行緩存設置。在以前我們可以同樣在 IIS 中設置這些東西,但是粒度可能并沒有這么細。
    我之前寫過一篇關于 ASP.NET Core 緩存的文章,里面介紹了 ASP.NET Core MVC 中的 Response 緩存,它是通過一個 ResponseCacheAttribute 來實現的設置緩存頭信息:
    [ResponseCache(VaryByHeader ="Accept-Encoding", Location = ResponseCacheLocation.Any, Duration = 10)]
    public IActionResult About()
    {
    }

    那,除了 MVC 提供的 ResponseCacheAttribute 外,還有另外一種方式設置緩存頭信息,如下:
    public IActionResult About()
    {
    Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
    {
    Public = true,
    MaxAge = TimeSpan.FromSeconds(10)
    };
    Response.Headers[HeaderNames.Vary] = new string[] { "Accept-Encoding" };
    }

    這兩種方式,最終的效果是一致的。
    有了這些頭信息之后,我們就可以在服務端里面這個中間件干些什么事情了。所以,該中間件將會在適當的時候讀取這些頭信息,然后緩存到本地緩存里面,當再有請求進來的時候會直接跳過action,讀取緩存信息進行返回。
    下面,我們一起來看看怎么樣添加到我們的項目中吧,很簡單。
    1、添加 Microsoft.AspNetCore.ResponseCaching 包
    你可以使用 Visual Studio 打開 NuGet 包管理器控制臺輸入一下命令安裝
    Install-Package Microsoft.AspNetCore.ResponseCaching
    2、更新 Startup.cs 文件
    修改 Startup , 在ConfigureServices 和Configure 兩個方法中添加如下代碼:
    public class Startup
    {
    ...
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddResponseCaching();
    ...
    }
    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
    app.UseResponseCaching();
    ...
    }
    }

    輸入的頭信息如下:
    詳細示例可以看這里.

    注意:上面的示例在 Chrome 瀏覽器中,當你按 F5 或者右鍵刷新頁面時候, Http Response Header 中的 Control-Cache: max-age 有時候可能會不生效,這是因為 Chrome 瀏覽器有很智能的算法來猜測你當前的行為是真的想刷新還是取緩存。 所以你可以試著把你的地址放入到一個HTML的Link中或者新開一個選項卡鍵入地址嘗試。而Edge和IE瀏覽器行為是符合預期的。


    總結
    以上是這兩個中間件的功能及使用方法,很簡單,就不多說了,如果你覺得對你有幫助,點個贊就是對我最大的鼓勵。
    如果你對 .NET Core 感興趣可以關注我,我會定期在博客分享關于 .NET Core 的學習心得。


    本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-response-compression-caching.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(18)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • 權衡微服務

    文章出處

    原文地址:http://martinfowler.com/articles/microservice-trade-offs.html
    作者:Martin Fowler


    很多開發團隊已經認識到 微服務架構比單體架構更優越。但是也有其他團隊感覺到這是一種消弱生產力的負擔。就像任何軟件架構,微服務架構同樣有利弊。為了能做出一個明智的選擇,你必須了解這些應用并將它們運用到你特定的環境中。
    微服務的優勢
  • 具有邊界的健壯模塊:有邊界的模塊化業務,可以隨著團隊規模的擴大而陸續增加。
              


  • 獨立部署:服務的部署更加簡單容易,另外,因為每個微服務是獨立的,所以其中一個出問題不會影響到整個系統。
              


  • 技術的多樣性:在微服務中,可以有一種以上的語言,開發框架,以及數據庫存儲相結合。


  • 微服務的附加成本
  • 分布式:分布式系統編程相對困難,因為遠程調用慢,所以可能總是會存在失敗的風險。
              


  • 最終一致性:在分布式系統中,保持強一致性是非常困難的,所以必須要處理最終一致性。
              


  • 操作復雜性:你需要一個經驗豐富的運營團隊來管理很多需要定時重新部署的服務。


  • 具有邊界的健壯模塊
    微服務最大的好處是對功能模塊的劃分。這是個既重要又奇怪的好處,因為理論上沒有理由來解釋為何微服務比單體架構有更強大的模塊劃分。
    那到底什么是我說的模塊邊界。我想大多數人同意,將軟件劃分成多個模塊,非常好:彼此就可以分離解耦。如果我需要改變系統的一小部分,大多數的時候我只需要弄清楚這一小塊,然后做些改動,我們會發現小變動真的很容易。對于任何程序而言,好的模塊化結構非常有用,特別是當軟件的規模變大,模塊化會顯得格外重要。也許,它會變得更重要,因為團隊的規模也在增加。
    提倡微服務,需要快速介紹一下 康威定律,該定律認為一個軟件系統的結構反映了團隊的溝通結構。對于較大的團隊,特別對于那些分散在各地的團隊,對軟件實施結構化顯得尤為重要,跨團隊之間的溝通與一個團隊內部溝通相比,將會變得不那么頻繁且更加正式。微服務允許每個團隊用類似的溝通模式來照看相對獨立的模塊單元。
    正如我前面所說,沒有理由可以解釋為何一個單體系統不應該擁有一個良好的模塊化結構[1]。但是很多人都觀察到這似乎很少見,因此大雜燴成了最常見的架構模式。事實上單體架構這種固有的困惑,驅動一些團隊開始使用微服務架構。模塊的解耦能夠起作用,是因為模塊的邊界就是模塊之間引用的障礙。麻煩的是,一個單體系統,通常都很容易繞過障礙。這樣做,可能是構建功能時有用的技術捷徑,可是呢,如果廣泛運用這種方法,將削弱模塊化的結構和降低團隊的生產力。將模塊放入獨立的服務,這樣做使模塊的邊界更為牢固,使這種自殺式的解決方案更難實現。
    耦合的重要部分是持久化的數據。微服務的關鍵特征是 去中心的數據管理,也就是說每個服務管理它自己的數據庫,其它的服務必須通過該服務的API來交互。這就會消除 集成數據庫,這是大系統非常討厭耦合的主要原因。
    值得強調的是在一個單體系統中,使用模塊化完全可能,但是它需要紀律來保證。同樣地,你也可能得到微服務的大雜燴模式,但是對于本身已是微服務的架構,要犯這樣的錯還是不太容易的。在我看來,使用微服務,可以幫助你實現更好的模塊化。如果你對團隊紀律很有信心,那么可能會消除這一優勢,不過,隨著團隊的規模增加,保持紀律會變得越來越,這也就意味著維護模塊邊界將顯得更為重要性。
    如果你不好好管理模塊的邊界,這個優勢就會變成障礙。這就是 單體優先策略的兩個主要原因之一,也是為何我更傾向于 較早使用微服務,你只能在一個領域了解得非常透徹。
    但我現在不會在這方面做出警告。只有經過時間的洗禮,你才能真正講出要如何才能把一個系統的模塊化維護好。所以只有我們看到微服務系統已經運行好多年,才可以評估它是否會帶來更好的模塊化特性。而且,早期接納者更有才華,也就是說在我們能夠評估一般團隊構建的微服務系統的模塊化優勢之前,還有好幾年的延遲。所以與其和高水平的團隊比較成果,還不如比較那些已經使用單體架構的軟件帶來的變化,這是一個棘手的違背事實的評估。
    所有我現在能做的,就是聽聽那些我認識的人,他們使用微服務架構的早期經驗。他們的判斷就是微服務能讓他們更容易去維護功能模塊。
    有一個案例特別有意思。有一個團隊做了一個錯誤的決定,在一個不那么復雜的系統上面 使用微服務。后來這個項目碰到了問題,需要幫助,然后很多人就跑了進來。這方面,微服務還是有所幫助的,因為這個系統能夠吸引開發者的快速涌入,和單體架構相比,團隊更容易擴張。最終這個項目的進度大大超過使用單體架構的預期,使整個團隊趕上進度。當然也有負面的,和單體架構相比,微服務的方式耗費了更多的人力,但是微服務的架構能夠支持未來更大的規模。
    如果你想要知道更多有關于如何構建微服務系統,Sam Newman的 這本書是很重要的資料。
    獨立部署
    微服務是DevOps革命后第一新的軟件架構。-- Neal Ford
    模塊化和分布式系統的復雜性一直伴隨著我整個職業生涯中。但是有一件事情發生了明顯的變化,就是在過去的十年中,有關生產環境的發布。在二十世紀,生產環境的發布幾乎總是痛苦且罕見的事情,周末的白天或晚上部署一些能用的軟件。但是現在呢,熟練的團隊頻繁發布到生產環境,許多團隊實行 持續交付,使他們一天能夠在生產環境發布很多次。
    這種轉變已經對軟件產業產生了深遠的影響,并與微服務運動深深交織在一起。當部分的小變化可能導致整個部署失敗,微服務終于被單體架構的部署難題所激發出來。微服務的一個關鍵原則是, 每個服務都是系統的一個組件,均可獨立部署。所以現在當你做出改變時,你只需要測試和部署一個小服務。如果你把它搞砸了,你不會把整個系統都搞砸。畢竟,事先對故障進行了設計,即使失敗了,你的組件也不應該停止其他部分的系統工作,盡管功能上有些退化。
    這種關系是雙向的。由于許多微服務需要頻繁部署,統一部署行為顯得尤為重要。這就是為什么應用的快速部署和快速配置是 微服務的先決條件。對于任何以此為基礎的服務,你都需要做持續的交付。
    持續交付的好處是減少了由想法變成軟件的時間。那么,團隊可以快速響應市場變化,并快于競爭對手先引入新的功能。
    盡管許多人認為持續交付是使用微服務的一個原因,但值得注意的是,即使單體架構也可以持續交付。Facebook和Etsy是兩個最好的例子。還有一些嘗試微服務的例子,因為多個服務需要認真協調才能發布,因此無法獨立部署[2]。同時我也聽到很多人認為使用微服務能更容易做持續交付,我是不太相信模塊化可以使持續發布更容易,盡管它可以大大提高交付的速度。
    技術的多樣性
    由于每個微服務都是一個獨立的部署單元,你有相當的自由選擇需要的技術。微服務可以用不同的語言,使用不同的庫,并使用不同的數據存儲方式。這使得團隊可以選擇合適的工具來工作,有些語言和庫更適合某些類型的問題。
    技術多樣性通常以最佳的工具為中心進行討論,但往往微服務最大的好處確是更令人頭疼的版本問題。在單體架構中你可以只使用一個單一的版本庫,這種情況經常導致升級出現問題。系統的一部分可能需要升級,來實現使用它的新功能,但不能因為升級而中斷系統的另一部分。處理庫的版本問題是其中的一個難題,因為隨著代碼庫的增大,難度會呈指數級增長。
    這里有一個風險,有這么多的技術多樣性,開發團隊會被壓倒。我所認識的大多數組織都鼓勵有限的一組技術。這種鼓勵是通過提供共同的工具來支持監測,使它更容易將服務穩定在一個小的通用環境中。
    不要低估了支持使用新技術的價值。用單體架構系統,早期對語言和框架的決定是很難逆轉的。經過十年左右,這些的決定可能會限制團隊并使團隊陷入尷尬的技術境地。微服務讓團隊嘗試新工具,并逐步一次遷移系統的一個服務,使新老技術有所關聯。
    分布式
    微服務采用分布式系統來提高模塊化。但是分布式軟件有一個主要的缺陷,就是分布式系統本身。一旦你開始玩分布式系統,你就會碰到一堆復雜的問題。 我不認為微服務社區對分布式系統所帶來的成本沒有概念,但是復雜性也確實存在。
    首先是性能。這時候,你不得不以一種不常見的方式,看著進程內的函數調用轉變成性能的瓶頸,遠程調用是很慢的。如果你的服務調用了很多遠程服務,這些遠程服務本身也要調用了另外一些遠程服務,這些響應時間加起來,就會帶來很恐怖的延遲特征。
    當然你有辦法減少延遲。首先,你可以增加調用的粒度,減少調用的數目。這會使你的編程模式變得復雜,使得你必須想清楚如何批量處理跨服務交互。由于你必須調用至少一次這些所有合作的服務,因此到目前為止,你能做的就這么多了。
    第二種方法就是使用異步通信。如果六個服務異步并行調用,延遲只會是那個最慢的調用,而不是所有調用延遲的總和。這大大改善了性能,但也帶來了認知成本。異步編程很難,你很難用好它,而且很難調試。但我聽到的大多數微服務的例子,都需要異步來獲得預期的性能。
    速度之后就是可靠性的問題。你期望in-process函數調用能夠成功,可是一個遠程調用可能在任何時間失敗。在很多的微服務中,甚至有很多的潛在的故障點。明智的開發人員知道這些, 為可能發生的故障事先設計。你為異步協作做的方案,也適應于故障處理,還可提高服務的彈性。然而,這些補償仍然不夠,仍然有著額外的復雜性,你需要弄明白每一個遠程調用失敗的后果。
    這就是分布式計算最主要的 兩個難題。
    對于這個問題,還有一些要注意的地方。首先,很多這類的問題出現在單體架構規模擴充的時候。很少有單體架構可以真正獨立運行,通常是跟其他系統一起,這些系統大部分是遺留系統。和這些系統通過網絡進行交互,同樣會碰到這些類似的問題。這就是為何很多人都傾向于更快轉移到微服務架構,來處理遠程系統的交互問題。這個問題,同樣也和經驗有關系,更熟練的團隊更有能力處理分布式特性所帶來的問題。
    當然,分布式特性永遠都是一個成本。我總是不太愿意打分布式這張牌,因為想到很多很多人,因低估這些難題而太快地引入了分布式系統。
    最終一致性
    我想你應該知道,網站的更新著實需要一點耐心。更新某一個東西之后刷新屏幕,可是更新的東西還沒有出現。你等了一兩分鐘后,它出現了。
    這是一個非常惱人的可用性問題,幾乎可以肯定是由于最終的一致性造成的。你的更新被節點P收到,可是呢,你的請求卻被另一個節點G處理。直到節點G從節點P那兒得到更新之前,你一直處于數據不一致的狀態。最終,它會變成一致的,但在這之前,你會疑惑是不是有什么東西弄錯了。
    像這樣的不一致是令人惱火的,但他們可以更嚴重。業務邏輯會停滯在對不一致的信息上做出決策,當這種事發生時,在不一致的窗口關閉之前,也難以診斷出到底什么出了問題。
    微服務帶來了最終一致性的問題,是因為他們堅持對去中心的數據管理,這種堅持值得稱贊。單體架構,你可以一次更新一堆東西。微服務則需要多個資源的更新和分布式的處理,這確實頭疼。所以現在,開發者需要意識到一致性問題,在寫任何代碼之前,弄明白如何檢測數據的不一致。
    單體架構在這些問題上同樣不能全身而退。隨著系統的增長,更需要使用緩存來提高性能,緩存失效是 另一個困難的問題。大多數應用程序需要脫機鎖,以避免長期的數據庫事務處理。外部系統需要更新,不能與事務管理器協調。商業流程往往更具寬容的不一致性,因為企業對可用性要求更高(業務流程,一直以來對 CAP理論都是一種直覺上的理解)。
    和其他分布式問題一樣,單體架構也不能完全避免非一致性問題,但它們受到的困擾不多,特別當它們規模很小時。
    操作復雜性
    能夠迅速部署獨立的小單位,是項目開發的一大福音,但由于幾十個應用現在轉變為幾百個小的微服務,給運維額外增加了負擔。許多組織會發現,處理這樣一個迅速變化的工具,有一種令人望而卻步的難度。
    這加強了持續交付的重要性。而持續交付是單體架構一項有價值的技能,也是一個微服務所必須的。如果沒有自動化協作,持續交付也無法處理那么多的服務。運維的復雜性也是由管理這些服務和監控的需求的增加而增加。運維的復雜性對單體架構的應用也有所幫助,不過對微服務來說,這是必然的。
    微服務的支持者指出,由于每個服務更小,所以更容易理解。但風險在于,復雜性并沒有消除,它只是轉移到服務之間的相互聯系中。這會增加運維的復雜性,比如調試、跨服務的難度。良好的服務邊界的選擇將減少這個問題,但如果用錯地方,則更糟糕。
    處理這種運維復雜度,需要一個新的技能和工具——重點是技能。工具仍然是不成熟的,但我的直覺告訴我,即使有更好的工具,微服務還得靠高技能。
    然而,對技能和工具的需求不是解決運維復雜性最難的部分。想要高效地解決,你還需要引入一個DevOps文化:開發者和運維之間的緊密合作,每個人都參與軟件交付。文化變革是困難的,尤其是在更大和更老的組織中。如果你不改變這個技能和文化,單體架構的應用只會受到阻礙,而微服務應用則會受到創傷。
    其他因素
    在我看來,上面的因素是作為首要權衡指標來考慮的。這里要討論的一些事情,我認為就沒那么重要了。
    微服務的支持者們經常說服務更容易擴展:一個服務得到大量的負載,你就可以擴展,而不用對整個應用進行擴展。然而,我努力回憶起一個過往的經驗報告,它使我相信,和 千篇一律的應用復制相比,它實際上是更有效地進行選擇性擴展。
    微服務允許你隔離敏感數據以及給數據增加安全性。此外,在保證微服務間交互安全的前提下,微服務難以被攻入。安全問題越來越重要,這可以成為使用微服務的主要考慮因素。即使不用微服務,對于單體架構的系統創建隔離服務來處理敏感數據,也是很常見的。
    批評者說微服務應用的測試要比單體架構的應用難度更大。雖然這個真正的困難部分來自于分布式應用程序的復雜性,但還是有 好的方法可以測試微服務。在這里,最重要的是要有紀律,要認真測試,單體架構和微服務應用的測試方法的區別只是第二位的。
    總結
    任何有關架構的綜述文章都會受到 一般性建議的限制。所以讀一篇這樣的文章不能幫助你做決定,但這樣的文章可以幫助你確保考慮到你應該考慮的各種因素。這里對不同的系統,成本和收益做了不同的權重,甚至成本收益會被顛倒過來(在更復雜的系統中強模塊化更好,但對于一個簡單的系統,這就增加了障礙),你所做的任何決定取決于環境中使用條件,評估哪些因素最重要,以及它們如何影響你的特定評估條件。此外,我們對微服務架構經驗相對有限。在一個系統成熟后,你通常只會在這個體系中決定相應的架構。我們還沒有很多關于長期運行的微服務架構的實例。
    單體架構和微服務并不是簡單的二選一。兩者都是模糊的定義,意味著大多數系統都將在一個模糊的邊界區域。當然也還有其他系統,不適合這兩個類別的。大多數人,包括我自己,討論微服務時用單體架構作對比,這是因為它們更常見,但我們必須記住,還是有系統不屬于這兩類的。我認為,單體架構和微服務是架構設計領域重要的兩部分。它們值得被討論,因為它們存在有趣的特性,以及有用的討論,但是沒有合適的架構師可以對它們在架構設計領域進行一個全面的區分。
    總結起來,微服務的好處:微服務提高了生產效率,但同時也帶來了復雜性。所以如果你可以用單體架構管理好你的系統,那么就無需微服務。
    對微服務的討論不應該讓我們忘記了更重要的問題,驅動軟件項目成功和失敗的重要因素。軟因素如團隊中人的素質,以及他們如何彼此合作,與領域專家的溝通能力,這都會對是否使用微服務有更加的影響。在純技術層面上來講,更重要的是把重點放在干凈的代碼、完善的測試,并持續關注架構的演化進步。
    腳注
    [1] 有些人認為"單體架構"是一種侮辱,總是意味著很糟糕的模塊化結構。在微服務世界,大多數人不這樣做,他們將單體架構定義為將整個應用構建為一個獨立的單元。當然,微服務倡導者相信大多數單體架構最終會變成大雜燴,但我不知道有誰會認為建立一個結構良好的單體架構毫無可能。
    [2] 能獨立部署服務是 微服務定義的一部分。所以這么說還是有道理的,即必須在其部署時協調服務的架構不是微服務架構。也可以這么說,很多團隊嘗試微服務架構而陷入麻煩是因為他們最終不得不協調多個服務來部署。
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(11)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • WinWordControl控件的使用注意


    文章出處
      最近在研究WinWordControl這個控件,因為上級要求在系統里,嵌入Word,然后用C#控制Word格式,在網上下載了WinWordControl.dll(在DLL之家下載的),因為本人的系統環境識win7+office07,導入到visual studio之后,vs報錯,因為WinWordControl控件只支持office03,悲劇。
      后來在電腦里裝了office03,現在是office03跟office07并存,幸好沒有沖突報錯,現在沒有報錯了,在引用里加入office 11 object library 、office 12 object library 、Excel 11.0 object library 、Excel 12.0 object library、Word 11.0 object library跟Word 12.0 object library就可以了,導入之后就可以正常使用了。謝天謝地。

    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(12)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • C# 利用Excel直接讀取方法讀取Excel進DataGridView


    文章出處
    在winform里拖入一個datagridview控件,跟一個openfiledialog控件,這個例子的功能只是讀取excel文件,其他的功能本人還在研究當中
    獻上代碼

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using Microsoft.Office.Core;
    using Excel=Microsoft.Office.Interop.Excel;
    using System.Windows.Forms;
    using System.Reflection;
    namespace Excelproject
    {
    public partial class ExcelForm : Form
    {
    private ExcelOperate eo;
    private string excelpath;
    private Excel.Application excel1;
    private Excel.Workbooks wbs = null;
    private Excel.Workbook wb = null;
    private Excel.Sheets wss;
    private Excel.Worksheet ws = null;
    private Excel.Range range1 = null;
    public ExcelForm()
    {
    InitializeComponent();
    this.excel1 = new Excel.Application();
    if (excel1 == null)
    {
    MessageBox.Show("error");
    System.Windows.Forms.Application.Exit();
    }
    excel1.Visible = true;
    }
    #region excel文件打開關閉操作
    private void 打開_Click(object sender, EventArgs e)
    {
    openFileDialog1 = new OpenFileDialog();
    openFileDialog1.Title = "打開excel文件";
    openFileDialog1.Filter = "excel03文件(*.xls)|*.xls|excel07文件(*.xlsx)|*.xlsx";
    openFileDialog1.InitialDirectory = @"C:\Users\Administrator\Desktop";
    openFileDialog1.RestoreDirectory = true;
    if (openFileDialog1.ShowDialog() == DialogResult.OK)
    {
    //打開文件對話框選擇的文件
    excelpath = openFileDialog1.FileName;
    eo = new ExcelOperate();
    readExcel(excelpath);
    }
    }
    void readExcel(string path)
    {
    object miss = System.Reflection.Missing.Value;
    excel1.UserControl = true;
    excel1.DisplayAlerts = false;
    excel1.Application.Workbooks.Open(excelpath, miss, miss, miss, miss,
    miss, miss, miss, miss,
    miss, miss, miss, miss,
    miss, miss);
    wbs = excel1.Workbooks;
    wss = wbs[1].Worksheets;
    ws = (Excel.Worksheet) wss.get_Item(1);
    int rowNum = ws.UsedRange.Cells.Rows.Count;
    int colNum = ws.UsedRange.Cells.Columns.Count;
    string cellStr = null;
    char ch = 'A';
    for (int i = 0; i < colNum; i++)
    {
    dataGridView1.Columns.Add(i.ToString(), ch.ToString());
    dataGridView1.Rows.Add(rowNum);
    for (int j = 0; j <rowNum; j++)
    {
    cellStr = ch.ToString() + (j + 1).ToString();
    dataGridView1[i, j].Value = ws.UsedRange.Cells.get_Range(cellStr, miss).Text.ToString();
    }
    ch++;
    }
    }
    #endregion
    }
    }


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(8,035)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • 對做C#自定義控件的一點心得


    文章出處
    近期在做DSOFramer這個控件,打算自己弄一個自定義控件來封裝這個COM組件,中間遇到很多曲折,研究了一個星期,終于完成了
    下面總結一下我做DSOFramer這個自定義控件的注意地方:
    1、在創建一個Windows窗體控件庫的時候,那個項目路徑里不能有中文,比如:D:\C#練習\WindowsFormsDsoframer ,這樣在你弄好之后運行控件的時候會報錯,說什么不能引用程序集,用戶控件測試容器不能啟動,實際上不是沒有添加程序集,是因為路徑中有中文,VS2008不能識別。
    2、最好注冊一下那個COM組件,然后放到工具箱里,直接拖到窗體設計器里,不要手工寫代碼比如:this.m_Panel_Control.Controls.Add(m_axFramerControl),如果手工寫的話,還要自己手工添加引用程序集,如果直接拖到窗體設計器里的話,VS2008會自動添加程序集,手工添加程序集是不行的,因為AxDSOFramer.dll  跟DSOFramer.dll 文件是你把DSOFramer控件拖到窗體設計器里VS自動生成的。
    比如,你做了兩個自定義控件,第一次你是用直接把DSOFramer控件拖到窗體設計器中,然后你第二次做自定義控件,但是你這次是手工寫代碼,在引用里手工添加你第一次做控件時VS自動生成的AxDSOFramer.dll  跟DSOFramer.dll 文件,那么你運行控件的時候VS就會報錯。
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(38)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • 打印出datagrideview里面的內容,用到少量GDI+的知識

    文章出處
     

     1 using System;
    2 using System.Data;
    3 using System.Drawing;
    4 using System.Drawing.Printing;
    5 using System.Windows.Forms;
    6
    7
    8 namespace InvoiceSystem{
    9
    10 public partial class Printer : Form
    11 {
    12 private DataGridView invoiceGrid;
    13 private int rowCount = 0; //datagridview行數
    14 private int colCount = 0; //datagridview列數
    15 private int x = 0; //一個datagridview單元格寬度
    16 private int y = 0; //當前行行間距
    17 int i = 0; //判斷行數for循環的起始值
    18 private int rowGap = 60; //行間距
    19 private int leftMargin = 50; //正文到左面的左邊距
    20 private Font font = new Font("Arial", 10); //正文字體
    21 private Font headingFont = new Font("Arial", 11, FontStyle.Underline); //標題字體
    22 private Font captionFont = new Font("Arial", 10, FontStyle.Bold);
    23 private Brush brush = new SolidBrush(Color.Black);
    24 private string cellValue = string.Empty; //保存datagridview一個單元格里的內容
    25
    26
    27 public Printer(DataGridView invoiceGrid)
    28 {
    29 InitializeComponent();
    30 this.invoiceGrid = invoiceGrid; //從外部傳入一個datagridview引用
    31
    32 }
    33
    34
    35 /// <summary>
    36 /// 打印按鈕
    37 /// </summary>
    38 /// <param name="sender"></param>
    39 /// <param name="e"></param>
    40 private void button1_Click(object sender, EventArgs e)
    41 {
    42
    43 printDocument1.DocumentName = "發票列表"; //打印的文檔文件名
    44 PaperSize ps=new PaperSize("16開",724,1024); //自定義紙張大小
    45 //ps.RawKind = 9; //設置為A4
    46 printDocument1.DefaultPageSettings.PaperSize = ps;
    47 printDocument1.DefaultPageSettings.Landscape = true; //使用橫向打印
    48
    49 printDocument1.PrintPage += new PrintPageEventHandler(printDocument1_PrintPage); //設置函數printDocument1_PrintPage為打印事件處理函數
    50 printDialog1.Document = printDocument1;
    51 printPreviewDialog1.Document = printDocument1;
    52
    53 if (printDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK && printPreviewDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    54 {
    55
    56 printDocument1.Print();
    57
    58
    59 }
    60 }
    61
    62
    63 private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    64 {
    65
    66 rowCount = invoiceGrid.Rows.Count - 1;
    67 colCount = invoiceGrid.ColumnCount;
    68
    69 //打印標題
    70 y += rowGap;
    71 x = leftMargin;
    72 for (int j = 0; j < colCount; j++)
    73 {
    74 if (invoiceGrid.Columns[j].Width>0)
    75 {
    76 cellValue = invoiceGrid.Columns[j].HeaderText;
    77 e.Graphics.FillRectangle(new SolidBrush(Color.LightGray), x, y, invoiceGrid.Columns[j].Width, rowGap); //將單元格的背景設置為灰色
    78 e.Graphics.DrawRectangle(Pens.Black, x, y, invoiceGrid.Columns[j].Width, rowGap); //畫出一個單元格
    79 e.Graphics.DrawString(cellValue, headingFont, brush, x, y); //將datagridview標題里的單元格里的內容填入剛才畫出的單元格
    80 x += invoiceGrid.Columns[j].Width; //寬度向后移一個datagridview單元格寬度的單位
    81 }
    82 }
    83
    84 //打印所有行
    85 for (; i < rowCount; i++)
    86 {
    87 y += rowGap;
    88 x = leftMargin;
    89
    90 for (int j = 0; j < colCount; j++)
    91 {
    92 if (invoiceGrid.Columns[j].Width>0)
    93 {
    94 cellValue = invoiceGrid.Rows[i].Cells[j].Value.ToString();
    95 e.Graphics.DrawRectangle(Pens.Black, x, y, invoiceGrid.Columns[j].Width, rowGap);
    96 e.Graphics.DrawString(cellValue, font, brush, x, y);
    97 x += invoiceGrid.Columns[j].Width;
    98 }
    99 }
    100
    101 if (y > e.PageBounds.Height) //e.PageBounds.Height表示頁邊距的高度
    102 {
    103 //允許多頁打印
    104 y = 0;
    105 e.HasMorePages = true; //如果還有沒有打印的就再執行一次print()函數把余下的打印出來
    106 i++;
    107 return;
    108
    109 }
    110
    111 }
    112 y += rowGap;
    113
    114
    115 //底部填充空格
    116 for (int j = 0; j < colCount; j++)
    117 {
    118 e.Graphics.DrawString(" ", font, brush, x, y);
    119 }
    120 i = 0;
    121 e.HasMorePages = false;
    122 }
    123 }
    124 }

      效果就是貼出的圖片的樣子
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(19)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • 數據庫中的two phase locking

    文章出處
    數據庫中的two phase locking
    兩段鎖協議是指每個事務的執行可以分為兩個階段:生長階段(加鎖階段)和衰退階段(解鎖階段)。
    加鎖階段:在該階段可以進行加鎖操作。在對任何數據進行讀操作之前要申請并獲得S鎖,在進行寫操作之前要申請并獲得X鎖。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
    解鎖階段:當事務釋放了一個封鎖以后,事務進入解鎖階段,在該階段只能進行解鎖操作不能再進行加鎖操作。
    兩段封鎖法可以這樣來實現:事務開始后就處于加鎖階段,一直到執行ROLLBACK和COMMIT之前都是加鎖階段。ROLLBACK和COMMIT使事務進入解鎖階段,即在ROLLBACK和COMMIT模塊中DBMS釋放所有封鎖。
     
    概念


    封鎖序列


     

     

    兩段鎖協議是指所有事務必須分兩個階段對數據項加鎖和解鎖:

    1. 在對任何數據進行讀、寫操作之前,要申請并獲得對該數據的封鎖。

    2. 每個事務中,所有的封鎖請求先于所有的解鎖請求。

    例如事務T1遵守兩段鎖協議,其封鎖序列是:

    Lock A, Read A, A:=A+100, Write A, Lock B, Unlock A, Read B, Unlock B, Commit;[1] 

    可以證明,若并發執行的所有事務均遵守兩段鎖協議,則對這些事務的任何并發調度策略都是可串行化的。

    另外要注意兩段鎖協議和防止死鎖的一次封鎖法的異同之處。一次封鎖法要求每個事務必須一次將所有要使用的數據全部加鎖,否則就不能繼續執行,因此一次封鎖法遵守兩段鎖協議;但是兩段鎖協議并不要求事務必須一次將所有要使用的數據全部加鎖,因此遵守兩段鎖協議的事務可能發生死鎖。

     

    SERIALIZABLE隔離級別可以一次將所有數據加鎖

     

     

    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(732)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:38
    • SQL Server2016 原生支持JSON


    文章出處
    SQL Server2016 原生支持JSON
     
    SQL Server 2005 開始支持 XML 數據類型,提供原生的 XML數據類型、XML 索引以及各種管理 XML 或輸出 XML 格式的函數。
    在 SQL Server 時隔 4 個主要版本之后,終于在 Microsoft Ignite 2015 大會上正式宣布,新一代的 SQL Server 2016 正式支持現在最流行的數據交換格式— JSON(JavaScript Object Notation)。
     
    SQL Server 2016 對JSON的支持并不是增加一個JSON數據類型,而是提供一個更輕便的框架,幫助用戶在數據庫里處理JSON格式數據。
    用戶不需要更變現有的表結構,因為SQL Server使用NVARCHAR 數據類型來存儲JSON文件,并且跟現有技術相互兼容,比如全文搜索、列存儲索引、in-memory OLTP,應用程序不需要做任何修改
     
    不需要使用JSON.Net這類工具分析和處理JSON數據,利用SQL Server內置函數就可以處理JSON數據,輕松將查詢結構輸出為JSON格式,或者搜索JSON文件內容。
     


    使用 JSON AUTO 輸出JSON 格式
    要將select語句的結果以JSON輸出,最簡單的方法是在后面加上 FOR JSON AUTO
    測試版本
    Microsoft SQL Server 2016 (CTP2.2) - 13.0.407.1 (X64) Jul 22 2015 21:19:11 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor) 

    SELECT * FROM [dbo].[Client] 
    GO
    SELECT * FROM [dbo].[Client] FOR JSON AUTO
    GO

     
     
    我們可以把每列中顯示的最大字符數 設置為8192
     


    加上Root Key
    如果想為FOR JSON 加上Root Key,可以使用ROOT選項來指定 Root Key 名稱
    SELECT * FROM [dbo].[Client] FOR JSON AUTO,ROOT('SUSU')
    GO

     


    使用JSON PATH 輸出JSON格式
    當想要自定義輸出JSON格式結構的時候,必須用JSON PATH描述,若SELECT 的字段名稱相同,必須用別名方式來重新命名字段名這樣才可以繼續查詢
    另外,如果字段的默認值為NULL,那么輸出JSON時,JSON會忽略null的只。如果要顯示null值,可以加上INCLUDE_NULL_VALUES 選項(同樣適用于JSON AUTO字句)
    select * from [dbo].[Client]
    --FOR JSON PATH
    SELECT * FROM [dbo].[Client] WHERE ClientID =2
    FOR JSON PATH
    [{"ClientID":2,"Firstname":"Peter","Lastname":"Nielsen","Birthdate":"1998-05-19T00:00:00","Email":"Peter@126.com","PhoneNumber":"+86-16326269674","Birthplace":"Stockholm","SocialSecurityNumber":"1901531234"}]
    --FOR JSON PATH
    SELECT * FROM [dbo].[Client] WHERE ClientID =4
    FOR JSON PATH,INCLUDE_NULL_VALUES
    [{"ClientID":4,"Firstname":"kade","Lastname":null,"Birthdate":"1980-01-06T00:00:00","Email":"Lotte@SOHU.com","PhoneNumber":"+86-16326269674","Birthplace":"Aalborg","SocialSecurityNumber":"1061234"}]

     
     
    更多關于JSON的功能
    目前SQL Server 2016 CTP2 對于JSON的功能支持還是比較有限,例如內置處理JSON格式化的函數,
    ISJSON(判斷是否是JSON格式)、JSON_VALUE(分析JSON文件并提取出值) 、OPENJSON(將JSON文件轉換為普通數據表)
    這些功能要等到CTP3才能陸續推出
     
    更多SQL Server2016好用的功能,敬請期待o(∩_∩)o 
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(7)

    • 個人分類:生活學習
    ▲top
    «1...101112230»

    pop-under

    參觀人氣

    • 本日人氣:
    • 累積人氣:

    線上人數

    Marquee

    最新文章

    • 文章列表
    • jvm系列(四):jvm調優-命令大全(jps jstat jmap jhat jstack jinfo)
    • spring boot(一):入門篇
    • jvm系列(一):java類的加載機制
    • jvm系列(三):java GC算法 垃圾收集器
    • spring boot 實戰:我們的第一款開源軟件
    • jvm系列(六):jvm調優-從eclipse開始
    • 混合應用技術選型
    • jvm系列(二):JVM內存結構
    • spring boot(五):spring data jpa的使用

    熱門文章

    • (1,763)jQuery之前端國際化jQuery.i18n.properties
    • (1,001)Oracle Hint
    • (630)技術筆記:Indy控件發送郵件
    • (515)linux下安裝sqlite3
    • (501)學習筆記: Delphi之線程類TThread
    • (242)VC單選按鈕控件(Radio Button)用法(轉)
    • (104)單條件和多條件查詢
    • (51)淺談config文件的使用
    • (22)基于 Asp.Net的 Comet 技術解析
    • (15)Java中的抽象類

    文章分類

    • 生活學習 (2,296)
    • 未分類文章 (1)

    最新留言

    • [20/04/24] 我是女生想約炮 有男生願意給我溫暖的嗎?我賴是woyou58 於文章「(1)從底層設計,探討插件式GIS框架的...」留言:
      我叫黎兒女生最近內心掙扎著要不要約炮我的line:woy...

    文章搜尋

    文章精選

    誰來我家

    Live Traffic Feed