總結一下 IEnumerable 的例子

本篇將圍繞 《》和《》給出的例子,總結一下對於 IEnumerable 接口的一些使用方法,希望讀者能夠從中獲得一些啟發。

框架類型的迭代

對於一個實現了 IEnumerable 接口的類型來說,開發中最常用的,就是把這個類型的對象放入到 foreach 等循環關鍵詞中進行迭代,遍歷其中的元素進行處理。

這種遍歷通常分為兩種目的:遍歷和查找。

IEnumerable 及其泛型版本 IEnumerable<T> 定義了一個類型的 “可迭代性”。這點很容易理解,系統中的很多集合類型都實現了該接口。

因此這些集合類型均可以採用 foreach 進行迭代遍歷。但是每個集合類型的迭代方式和結果是不完全相同的,這取決於集合本身的特性。例如:

  • List<>Stack<> 和 Queue<> 的迭代的順序不相同,因為數據結構本身要求是不同的
  • ConcurrentDictionary<,> 和 Dictionary<,> 在迭代時的線程安全性是不同的,因為針對線程安全的設計是不同的
  • BlockingCollection.GetConsumingEnumerable 方法返回一個會產生阻塞的消費者對象,

所以,即使都是丟進 foreach,但是效果也是不完全一樣的。使用這些,需要讀者對這些類型本身需要增進了解。

建議讀者在使用框架中實現了 IEnumerable 的類型時,一定要注意迭代的細節,可以通過 MSDN 上的文檔了解其特殊性。

Linq

Linq 是一個說小不小的話題,這裏只是說其中的 Linq To Object 部分內容。

通過 Linq 中提供的一些擴展方法,可以方便的控制對於一個 IEnumerable 對象的迭代方式。通過這些方法的應用,可以在很多時候避免複雜的條件和循環嵌套。

同時,Linq 中抽象的 Func 和 Action,也要求開發人員在平時的編寫過程中注意對於迭代本身的歸類和整理。Where(IsLeapYear) 會比 Where(x=>(x % 4 == 0 && x % 100 != 0) || x % 400 == 0) 來的更加容易閱讀。

設計複雜的數據結構及其迭代算法

除了基礎的數據結構,開發過程中有時需要自定義一些集合類型。這些集合類型需要自己實現一個迭代過程。例如:二叉樹及其遍歷,對列表進行分頁等等。

這些數據結構的迭代通常需要特定算法的支持。

在《》中關於樹的幾個例子便數據此類中。

本地函數

在 C#7.0 引入了之後, IEnumerable 結合本地函數,快速實現自定義迭代過程的奇怪操作也就跟着出現。

通過這種操作可以在一個函數內採用一些以前不容易實現的方式實現一些操作:

  • 將多重循環拉平
  • 將多級條件判斷變為循環判斷
  • 無需創建新的類就能快速生成一個上下文需要的特殊迭代算法

這相關的例子在《》中較多。

按照月老闆的名言:“業務複雜度是不會因為系統設計變化而減少的,它只是從一個地方轉移到了另外的地方。”,我們可以知道,這種寫法其實沒有使得原來就有的判斷和循環變少。只是改變了語法結構。

讀者可以將這種操作作為一種 “語法糖” 進行使用。如果是在團隊項目中,則需要尊重團隊成員的共同意見,因為這種操作並非所有人都願意接受。

當然,這種做法在一些地方會產生好處。例如在將本地函數、IEnumerable 和 Task 相結合的 T10 測試網絡連接 中。這種寫法就減少了傳統寫法中需要創建一個 List 或者 Array 的開銷。

總之,這種寫法,提供了一種新的思路。是否一定要使用,將取決於讀者團隊的接受程度。

異步迭代器

在 C# 8 和 .netcore 3.0 到來的版本中,我們迎接到了 IAsyncEnumerable 接口來實現異步迭代器的功能。

IEnumerable 是同步方法的迭代器,IAsyncEnumerable 可以看做是其異步版本。有了這個接口,那麼在迭代的過程中也可以充分利用 async/await 帶來的編程快感。

本系列中沒有添加這部分的示例,但是主體思路是一致的。

她的出現,只會使得開發者更容易應用以上總結的幾種主要場景。

詳細的例子,可以。

總結

本系列到此便結束了,希望讀者多在實踐中體會以上總結的幾種使用場景。

本系列中的例子已經全部使用  進行了重寫,讀者可以直接在本博客的頁面上運行這些示例。

如果無法正常的展示示例,讀者也可以。

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

【其他文章推薦】

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

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

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

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

Asp.net Core 系列之–4.事務、日誌及錯誤處理

ChuanGoing 2019-11-17

    這篇原本想把事務處理、日誌處理、錯誤處理、授權與鑒權一併介紹完的,授權和鑒權我想結合自定義權限來介紹,全部放到這裏篇幅可能太長,因此權限部分將會在下篇來介紹。先說下我接下來的打算把,下篇將介紹權限控制,結合Oauth2.0和OpenId(OIDC)以及自定義權限來介紹;完了後會結合之前所介紹的基礎來實現一個簡單的電商網站,當然是利用領域驅動設計來實現。我的這個系列的主題就是領域驅動設計,實現簡單電商網站時將會深入的講解下領域的劃分原則及領域服務的場景,中間可能會嘗試部分業務實現事件驅動。

本篇學習曲線:

1.日誌記錄

2.錯誤處理

3.事務處理

日誌記錄

    NLog是一個記錄日誌組件,和log4net一樣被廣泛使用,它可以將日誌保存到文本文件、CSV、控制台、VS調試窗口、數據庫等。在之前例子中的WebApi項目中添加NLog.Web.AspNetCore的Nuget包,並添加如下配置:

 

 

   簡單介紹下配置信息,“targets”配置每個輸出配置,我這裡有3個輸出:database、allfile、ownfile,分別表示輸出到數據庫和對應路徑的日誌文件下。

“rules”規則配置了4條:

1.將Debug以上級別(含)信息輸出到allfile

2.忽略Microsoft.*開頭的信息(對應的輸出沒有配置到任何文件),此配置一般忽略即可

3.將Debug以上級別(含)信息輸出到ownfile(注意這裏配置和allfile一樣,一般配置級別高點的日誌信息)

4.將Warn以上級別(含)信息輸出到數據庫

完了后,在Program.cs Main方法裏面註冊NLog:

var logger = NLogBuilder.ConfigureNLog($"Nlog.config").GetCurrentClassLogger();
            try
            {
                CreateWebHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Stopped program because of exception");
                throw ex;
            }
            finally
            {
                NLog.LogManager.Shutdown();
            }

注意不要忘了啟用NLog組件使之生效

 

在OrderController的Add方法中加入以下代碼:

 

用postman簡單測試下,我們可以看到執行目錄中多出來了日誌信息

 

 

錯誤處理

  這裏一般我們關心的錯誤大概有兩類:

1.內部錯誤,即通過框架(Mvc)管道準確的傳入到內部系統中併發生錯誤的此類信息

2.框架(Mvc)執行管道的某些中間件時發生的錯誤或被中間件禁止繼續訪問的請求

  因此,定義如下3個類:

public class InnerException : Exception
    {
        /// <summary>
        /// 內部錯誤代碼
        /// </summary>
        public int? ErrorCode { get; }

        public InnerException(int errorCode) : base()
        {
            ErrorCode = errorCode;
        }

        public InnerException(int errorCode, string message) : base(message)
        {
            ErrorCode = errorCode;
        }

        public InnerException(int code, string message, Exception exception) : base(message, exception)
        {
            ErrorCode = code;
        }
    }

InnerException

public class MessageCodes
    {
        #region 公用

        /// <summary>
        /// 成功
        /// </summary>
        public const int Success = 20101000;
        /// <summary>
        /// 警告
        /// </summary>
        public const int Warning = 20102000;
        /// <summary>
        /// 錯誤
        /// </summary>
        public const int Error = 20103000;
        /// <summary>
        /// 數據驗證錯誤
        /// </summary>
        public const int DataValidationError = 20104000;
        /// <summary>
        /// 數據不存在
        /// </summary>
        public const int DataNotFound = 20105000;
        /// <summary>
        /// 非法的數據狀態
        /// </summary>
        public const int IllegalState = 20106000;
        /// <summary>
        /// 參數無效
        /// </summary>
        public const int InvalidParams = 20107000;
        /// <summary>
        /// 輸入非法
        /// </summary>
        public const int IllegalInput = 20108000;
        /// <summary>
        /// 鑒權成功
        /// </summary>
        public const int AuthSuccess = 20109000;

        #endregion

    }

MessageCodes

 public class WebException: InnerException
    {
        public HttpStatusCode HttpStatus { get; set; }

        public HttpRequest Request { get; private set; }

        public WebException(HttpStatusCode httpStatus, int errorCode, string message)
             : base(errorCode, message)
        {
            HttpStatus = httpStatus;
        }

        public WebException(HttpStatusCode httpStatus, int errorCode, string message, HttpRequest request)
            : this(httpStatus, errorCode, message)
        {
            Request = request;
        }

        public WebException(int errorCode, string message)
            : base(errorCode, message)
        {
            HttpStatus = HttpStatusCode.BadRequest;
        }
    }

WebException

通過Aop,很方便就可以實現錯誤信息的處理:

public class ExceptionFilter : IExceptionFilter
    {
        private readonly ILogger<ExceptionFilter> _logger;

        public ExceptionFilter(ILogger<ExceptionFilter> logger)
        {
            _logger = logger;
        }

        public void OnException(ExceptionContext context)
        {
            _logger.LogError(context.Exception, context.Exception.Message);

            #region Ioc/automapper等中間件對錯誤信息進行了包裝,需要解包

            //web錯誤:驗證/鑒權等
            var webException = GetException<Base.Exceptions.WebException>(context.Exception);
            if (webException != null)
            {
                context.Result = new JsonResult(new
                {
                    ErrorCode = webException.ErrorCode ?? MessageCodes.Error,
                    webException.Message
                })
                {
                    StatusCode = (int)webException.HttpStatus
                };
                return;
            }
            //內部錯誤
            var exception = GetException<InnerException>(context.Exception);
            if (exception != null)
            {
                context.Result = new JsonResult(new
                {
                    ErrorCode = exception.ErrorCode ?? MessageCodes.Error,
                    exception.Message
                })
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError
                };
                return;
            }

            #endregion
        }

        private TException GetException<TException>(Exception exception)
          where TException : Exception
        {
            if (exception == null)
            {
                return null;
            }
            if (exception is TException tException)
            {
                return tException;
            }
            else
            {
                return GetException<TException>(exception.InnerException);
            }
        }
    }

ExceptionFilter

同時,Startup.cs的ConfigureServices中註冊一下:

services.AddMvc(mvcOptions =>
                {
                    mvcOptions.Filters.Add<ExceptionFilter>();
                })

即完成了錯誤信息並且錯誤信息會寫入相應配置的輸出中。

事務處理

  UnitOfWork又稱工作單元,為了保證數據操作完整性,我們將處理數據的的操作統一放在一個事務中,我們這裏利用UnitOfWork來實現事務處理。

首先定義IUnitOfWork及UnitOfWork實現:

 public interface IUnitOfWork
    {
        void Begin(IsolationLevel level = IsolationLevel.Unspecified);
        void SaveChanges();
        void Failed();
    }

View Code

public class UnitOfWork : IUnitOfWork
    {
        private ITransactionRepository _repository;

        public UnitOfWork(ITransactionRepository repository)
        {
            _repository = repository;
        }

        public virtual void Begin(IsolationLevel level = IsolationLevel.Unspecified)
        {
            _repository.BeginTransaction(level);
        }

        public virtual void SaveChanges()
        {
            _repository.Commit();
        }

        public virtual void Failed()
        {
            _repository.Rollback();
        }
    }

View Code

其中,UnitOfWork依賴於ITransactionRepository的實現:

public interface ITransactionRepository
    {
        /// <summary>
        /// 打開事務
        /// </summary>
        /// <param name="level"></param>
        void BeginTransaction(IsolationLevel level = IsolationLevel.Unspecified);
        /// <summary>
        /// 提交事務
        /// </summary>
        void Commit();
        /// <summary>
        /// 事務回滾
        /// </summary>
        void Rollback();
    }

ITransactionRepository

利用DapperRepository繼承ITransactionRepository並實現:

public virtual void BeginTransaction(IsolationLevel level = IsolationLevel.Unspecified)
        {
            DbContext.BeginTransaction(level);
        }

        public virtual void Commit()
        {
            DbContext.Commit();
        }

        public virtual void Rollback()
        {
            DbContext.RollBack();
        }

View Code

基本功能實現后,如何使用呢?這裏還是需要利用Aop:

public class UnitOfWorkAttribute : AbstractInterceptorAttribute
    {
        public override Task Invoke(AspectContext context, AspectDelegate next)
        {
            if (context.Implementation is IApplicationService applicationService)
            {
                var uow = applicationService.UnitOfWork;
                uow.Begin();
                var aspectDelegate = next(context);
                if (aspectDelegate.Exception != null)
                {
                    uow.Failed();
                    throw aspectDelegate.Exception;
                }
                else
                {
                    uow.SaveChanges();
                    return aspectDelegate;
                }
            }
            else
            {
                return next(context);
            }
        }
    }

UnitOfWorkAttribute

因此,我們還需要在Application項目中添加如下代碼:

 public class ServiceBase<TEntity, TPrimaryKey> : IApplicationService
        where TEntity : class, IEntity<TPrimaryKey>
    {
        protected IMapper Mapper { get; private set; }
        public virtual IUnitOfWork UnitOfWork { get; private set; }

        public ServiceBase(IComponentContext container, ICommandRepository<TEntity, TPrimaryKey> repository)
        {
            Mapper = container.Resolve<IMapper>();
            UnitOfWork = container.Resolve<IUnitOfWork>(new TypedParameter(typeof(ITransactionRepository), repository));
        }
    }

ServiceBase

Application中的每個服務去繼承上面的ServiceBase,因此每個Application服務都具有了事務處理能力

 public interface IOrderService : IScopeInstance
    {
        [UnitOfWork]
        void Add(OrderViewModel order);
        OrderViewResult Get(string sn);
    }

 

 程序運行時,Add方法前後形成切面,如下圖所示,next(context)這裏執行的就是Add方法,執行前開啟事務,執行后提交

 

 利用Aop特性切面實現事務的無感注入(Ioc/DI小節中引入了AspectCore動態代理),底層還是依賴IDbConnection的事務相關接口,完整的事務處理大概就是這樣了。

詳細代碼在Github的 的Domain分支可以找到。

 

 

    

 

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!