主頁 > .NET開發 > [Abp vNext 原始碼分析] - 9. 介面引數的驗證

[Abp vNext 原始碼分析] - 9. 介面引數的驗證

2020-09-22 09:12:57 .NET開發

一、簡要說明

ABP vNext 針對介面引數的校驗作業,分別由過濾器和攔截器兩步完成,過濾器內部使用的 ASP.NET Core MVC 所提供的 IModelStateValidator 進行處理,而攔截器使用的是 ABP vNext 自己提供的一套 IObjectValidator 進行校驗作業,

關于引數驗證相關的代碼,分布在以下三個專案當中:

  • Volo.Abp.AspNetCore.Mvc
  • Volo.Abp.Validation
  • Volo.Abp.FluentValidation

通過 MVC 的過濾器和 ABP vNext 提供的攔截器,我們能夠快速地對介面的引數、物件的屬性進行統一的驗證處理,而不會將這些代碼擴散到業務層當中,

文章資訊:

基于的 ABP vNext 版本:1.0.0

創作日期:2019 年 10 月 22 日晚

更新日期:暫無

二、原始碼分析

2.1 模型驗證過濾器

模型驗證過濾器是直接使用的 MVC 那一套模型驗證機制,基于資料注解的方式進行校驗,資料注解也就是存放在 System.ComponentModel.DataAnnotations 命名空間下面的一堆特性定義,例如我們經常在 DTO 上面使用的 [Required][StringLength] 特性等,如果想知道更多的資料注解用法,可以前往 MSDN 進行學習,

2.1.1 過濾器的注入

模型驗證過濾器 (AbpValidationActionFilter) 的定義存放在 Volo.Abp.AspNetCore.Mvc 專案內部,它是在模塊的 ConfigureService() 方法中被注入到 IoC 容器的,

AbpAspNetCoreMvcModule 里面的相關代碼:

namespace Volo.Abp.AspNetCore.Mvc
{
    [DependsOn(
        typeof(AbpAspNetCoreModule),
        typeof(AbpLocalizationModule),
        typeof(AbpApiVersioningAbstractionsModule),
        typeof(AbpAspNetCoreMvcContractsModule),
        typeof(AbpUiModule)
        )]
    public class AbpAspNetCoreMvcModule : AbpModule
    {
        //
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            // ...
            Configure<MvcOptions>(mvcOptions =>
            {
                mvcOptions.AddAbp(context.Services);
            });
        }
        // ...
    }
}

上述代碼是呼叫對 MvcOptions 撰寫的 AddAbp(this MvcOptions, IServiceCollection) 擴展方法,傳入了我們的 IoC 注冊容器(IServiceCollection),

AbpMvcOptionsExtensions 里面的相關代碼:

internal static class AbpMvcOptionsExtensions
{
    public static void AddAbp(this MvcOptions options, IServiceCollection services)
    {
        AddConventions(options, services);
        // 注冊過濾器,
        AddFilters(options);
        AddModelBinders(options);
        AddMetadataProviders(options, services);
    }

    // ...

    private static void AddFilters(MvcOptions options)
    {
        options.Filters.AddService(typeof(AbpAuditActionFilter));
        options.Filters.AddService(typeof(AbpFeatureActionFilter));
        // 我們的引數驗證過濾器,
        options.Filters.AddService(typeof(AbpValidationActionFilter));
        options.Filters.AddService(typeof(AbpUowActionFilter));
        options.Filters.AddService(typeof(AbpExceptionFilter));
    }

    // ...
}

到這一步,我們的 AbpValidationActionFilter 會被添加到 IoC 容器當中,以供 ASP.NET Core Mvc 框架進行使用,

2.1.2 過濾器的驗證流程

我們的驗證過濾器通過上述步驟,已經被注入到 IoC 容器當中了,以后我們每次的介面呼叫都會進入 AbpValidationActionFilterOnActionExecutionAsync() 方法內部,在這個過濾器的內部實作代碼中,我們看到 ABP 為我們注入了一個 IModelStateValidator 物件,

public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency
{
    private readonly IModelStateValidator _validator;

    public AbpValidationActionFilter(IModelStateValidator validator)
    {
        _validator = validator;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        //TODO: Configuration to disable validation for controllers..?
        //TODO: 是否應該增加一個配置項,以便開發人員禁用驗證功能 ?

        // 判斷當前請求是否是一個控制器行為,是則回傳 true,
        // 第二個條件會判斷當前的介面回傳值是 IActionResult、JsonResult、ObjectResult、NoContentResult 的一種,是則回傳 true,
        // 這里則會忽略不是控制器的方法,控制器型別不是上述型別任意一種也會被忽略,
        if (!context.ActionDescriptor.IsControllerAction() ||
            !context.ActionDescriptor.HasObjectResult())
        {
            await next();
            return;
        }

        // 呼叫驗證器進行驗證操作,
        _validator.Validate(context.ModelState);
        await next();
    }
}

過濾器的行為很簡單,判斷當前的 API 請求是否符合條件,不符合則不進行引數驗證,否則呼叫 IModelStateValidatorValidate 方法,將模型狀態傳遞給它進行處理,

這個介面從名字上看,應該是模型狀態驗證器,因為我們介面上面的引數,在 ASP.NET Core MVC 的使用當中,會進行模型系結,即建立物件到 Http 請求引數的映射,

public interface IModelStateValidator
{
    void Validate(ModelStateDictionary modelState);

    void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState);
}

ABP vNext 的默認實作是 ModelStateValidator ,它的內部實作也很簡單,就是遍歷 ModelStateDictionary 物件的錯誤資訊,將其添加到一個 AbpValidationResult 物件內部的 List 集合,這樣做的目的,是方便后面 ABP vNext 進行錯誤拋出,

public class ModelStateValidator : IModelStateValidator, ITransientDependency
{
    public virtual void Validate(ModelStateDictionary modelState)
    {
        var validationResult = new AbpValidationResult();

        AddErrors(validationResult, modelState);

        if (validationResult.Errors.Any())
        {
            throw new AbpValidationException(
                "ModelState is not valid! See ValidationErrors for details.",
                validationResult.Errors
            );
        }
    }

    public virtual void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState)
    {
        if (modelState.IsValid)
        {
            return;
        }

        foreach (var state in modelState)
        {
            foreach (var error in state.Value.Errors)
            {
                validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key }));
            }
        }
    }
}

2.1.3 結果的包裝

當過濾器拋出了 AbpValidationException 例外之后,ABP vNext 會在例外過濾器 (AbpExceptionFilter) 內部捕獲這個特定例外 (取決于例外繼承的 IHasValidationErrors 介面),并對其進行特殊的包裝,

[Serializable]
public class AbpValidationException : AbpException, 
    IHasLogLevel, 
    // 注意這個介面,
    IHasValidationErrors, 
    IExceptionWithSelfLogging
{
    // ...
}

2.1.4 資料注解的驗證

這一節相當于是一個擴展知識,幫助我們了解資料注解的作業機制,以及 ModelStateDictionary 是怎么被填充的,

擴展閱讀:

  • ASP.NET Core 模型驗證詳解

  • .NET Core 開發日志 -- Model Binding

2.2 物件驗證攔截器

ABP vNext 除了使用 ASP.NET Core MVC 提供的模型驗證功能,自己也提供了一個單獨的驗證模塊,我們先來看看模塊型別內部所執行的操作:

public class AbpValidationModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        // 添加攔截器注冊類,
        context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded);
        // 添加物件驗證攔截器的輔助物件,
        AutoAddObjectValidationContributors(context.Services);
    }

    private static void AutoAddObjectValidationContributors(IServiceCollection services)
    {
        var contributorTypes = new List<Type>();

        // 在型別注冊的時候,如果型別實作了 IObjectValidationContributor 介面,則認定是驗證器的輔助類,
        services.OnRegistred(context =>
        {
            if (typeof(IObjectValidationContributor).IsAssignableFrom(context.ImplementationType))
            {
                contributorTypes.Add(context.ImplementationType);
            }
        });

        // 最后向 Options 型別添加輔助類的型別定義,
        services.Configure<AbpValidationOptions>(options =>
        {
            options.ObjectValidationContributors.AddIfNotContains(contributorTypes);
        });
    }
}

模塊在啟動時進行了兩個操作,第一是為框架注冊物件驗證攔截器,第二則是添加 輔助型別(IObjectValidationContributor) 的定義到配置類中,方便后續進行使用,

2.2.1 攔截器的注入

攔截器的注入行為很簡單,主要注冊的型別實作了 IValidationEnabled 介面,就會為其注入攔截器,

public static class ValidationInterceptorRegistrar
{
    public static void RegisterIfNeeded(IOnServiceRegistredContext context)
    {
        if (typeof(IValidationEnabled).IsAssignableFrom(context.ImplementationType))
        {
            context.Interceptors.TryAdd<ValidationInterceptor>();
        }
    }
}

2.2.2 攔截器的行為

public class ValidationInterceptor : AbpInterceptor, ITransientDependency
{
    private readonly IMethodInvocationValidator _methodInvocationValidator;

    public ValidationInterceptor(IMethodInvocationValidator methodInvocationValidator)
    {
        _methodInvocationValidator = methodInvocationValidator;
    }

    public override void Intercept(IAbpMethodInvocation invocation)
    {
        Validate(invocation);
        invocation.Proceed();
    }

    public override async Task InterceptAsync(IAbpMethodInvocation invocation)
    {
        Validate(invocation);
        await invocation.ProceedAsync();
    }

    protected virtual void Validate(IAbpMethodInvocation invocation)
    {
        _methodInvocationValidator.Validate(
            new MethodInvocationValidationContext(
                invocation.TargetObject,
                invocation.Method,
                invocation.Arguments
            )
        );
    }
}

攔截器內部只會呼叫 IMethodInvocationValidator 物件提供的 Validate() 方法,在呼叫時會將方法的引數,方法型別等資料封裝到 MethodInvocationValidationContext

這個背景關系型別,本身就繼承了前面提到的 AbpValidationResult 型別,在其內部增加了存盤引數資訊的屬性,

public class MethodInvocationValidationContext : AbpValidationResult
{
    public object TargetObject { get; }

    // 方法的元資料資訊,
    public MethodInfo Method { get; }

    // 方法的具體引數值,
    public object[] ParameterValues { get; }

    // 方法的引數資訊,
    public ParameterInfo[] Parameters { get; }

    public MethodInvocationValidationContext(object targetObject, MethodInfo method, object[] parameterValues)
    {
        TargetObject = targetObject;
        Method = method;
        ParameterValues = parameterValues;
        Parameters = method.GetParameters();
    }
}

接下來我們看一下真正的 物件驗證器 ,也就是 IMethodInvocationValidator 的默認實作 MethodInvocationValidator 當中具體的操作,

// ...
public virtual void Validate(MethodInvocationValidationContext context)
{
    // ...

    AddMethodParameterValidationErrors(context);

    if (context.Errors.Any())
    {
        ThrowValidationError(context);
    }
}

// ...

protected virtual void AddMethodParameterValidationErrors(MethodInvocationValidationContext context)
{
    // 回圈呼叫 IObjectValidator 的 GetErrors 方法,捕獲引數的具體錯誤,
    for (var i = 0; i < context.Parameters.Length; i++)
    {
        AddMethodParameterValidationErrors(context, context.Parameters[i], context.ParameterValues[i]);
    }
}

protected virtual void AddMethodParameterValidationErrors(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue)
{
    var allowNulls = parameterInfo.IsOptional ||
                        parameterInfo.IsOut ||
                        TypeHelper.IsPrimitiveExtended(parameterInfo.ParameterType, includeEnums: true);

    // 添加錯誤資訊到 Errors 里面,方便后面拋出,
    context.Errors.AddRange(
        _objectValidator.GetErrors(
            parameterValue,
            parameterInfo.Name,
            allowNulls
        )
    );
}

2.2.3 “真正”的引數驗證器

我們看到,即便是在 IMethodInvocationValidator 內部,也沒有真正地進行引數驗證作業,而是呼叫了 IObjectValidator 進行物件驗證處理,其介面定義如下:

public interface IObjectValidator
{
    void Validate(
        object validatingObject,
        string name = null,
        bool allowNull = false
    );

    List<ValidationResult> GetErrors(
        object validatingObject, // 待驗證的值,
        string name = null,	// 引數的名字,
        bool allowNull = false	// 是否允許可空,
    );
}

它的默認實作代碼如下:

public class ObjectValidator : IObjectValidator, ITransientDependency
{
    protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
    protected AbpValidationOptions Options { get; }

    public ObjectValidator(IOptions<AbpValidationOptions> options, IHybridServiceScopeFactory serviceScopeFactory)
    {
        ServiceScopeFactory = serviceScopeFactory;
        Options = options.Value;
    }

    public virtual void Validate(object validatingObject, string name = null, bool allowNull = false)
    {
        var errors = GetErrors(validatingObject, name, allowNull);

        if (errors.Any())
        {
            throw new AbpValidationException(
                "Object state is not valid! See ValidationErrors for details.",
                errors
            );
        }
    }

    public virtual List<ValidationResult> GetErrors(object validatingObject, string name = null, bool allowNull = false)
    {
        // 如果待驗證的值為空,
        if (validatingObject == null)
        {
            // 如果引數本身是允許可空的,那么直接回傳,
            if (allowNull)
            {
                return new List<ValidationResult>(); //TODO: Returning an array would be more performent
            }
            else
            {
                // 否則在錯誤資訊里面加入不能為空的錯誤,
                return new List<ValidationResult>
                {
                    name == null
                        ? new ValidationResult("Given object is null!")
                        : new ValidationResult(name + " is null!", new[] {name})
                };
            }
        }

        // 構造一個新的背景關系,將其分派給輔助類進行驗證,
        var context = new ObjectValidationContext(validatingObject);

        using (var scope = ServiceScopeFactory.CreateScope())
        {
            // 遍歷之前模塊啟動的輔助型別,
            foreach (var contributorType in Options.ObjectValidationContributors)
            {
                // 通過 IoC 創建實體,
                var contributor = (IObjectValidationContributor) 
                    scope.ServiceProvider.GetRequiredService(contributorType);

                // 呼叫輔助型別進行具體認證,
                contributor.AddErrors(context);
            }
        }

        return context.Errors;
    }
}

所以我們的物件驗證,還沒有真正的進行驗證處理,所有的驗證操作都是由各個 驗證輔助型別 處理的,而這些輔助型別有兩種,第一是基于資料注解驗證輔助型別,第二種則是基于 FluentValidation 庫撰寫的一種驗證輔助類,

雖然 ABP vNext 套了三層,最終只是為了方便我們開發人員重寫各個階段的實作,也就更加地靈活可控,

2.2.4 默認的資料注解驗證

ABP vNext 為了降低我們的學習成本,本身也是支持 ASP.NET Core MVC 那一套資料注解校驗,你可以在某個非控制器型別的引數上,使用 [Required] 等資料注解特性,

它的默認實作我就不再多加贅述,基本就是通過反射得到引數物件上面的所有 ValidationAttribute 特性,顯式地呼叫 GetValidationResult() 方法,獲取到具體的錯誤資訊,然后添加到背景關系結果當中,

foreach (var attribute in validationAttributes)
{
    var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext);
    if (result != null)
    {
        errors.Add(result);
    }
}

另外注意,這個遞回驗證的深度是 8 級,在輔助型別的 MaxRecursiveParameterValidationDepth 常量中進行了定義,也就是說,你這個物件圖的邏輯層級不能超過 8 級,

public class A1
{
    [Required]
    public string Name { get; set;}
    
    public B2 B2 { get; set;}
}

public class B2
{
    [StringLength(8)]
    public string Name { get; set;}
}

如果你方法引數是 A1 型別的話,那么這就有 2 層了,

2.3 流暢驗證庫

回想上一節說的驗證輔助類,還有一個基于 FluentValidation 庫的型別,這里對于該庫的使用方法參考單元測驗即可,我這里只講解一下,這個輔助型別是如何進行驗證的,

public class FluentObjectValidationContributor : IObjectValidationContributor, ITransientDependency
{
    private readonly IServiceProvider _serviceProvider;

    public FluentObjectValidationContributor(
        IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void AddErrors(ObjectValidationContext context)
    {
        // 構造泛型型別,如果你對 Person 寫了個驗證器,那么驗證器型別就是 IValidator<Person>,
        var serviceType = typeof(IValidator<>).MakeGenericType(context.ValidatingObject.GetType());
        // 通過 IoC 獲得一個實體,
        var validator = _serviceProvider.GetService(serviceType) as IValidator;
        if (validator == null)
        {
            return;
        }

        // 呼叫驗證器的方法進行驗證,
        var result = validator.Validate(context.ValidatingObject);
        if (!result.IsValid)
        {
            // 獲得錯誤資料,將 FluentValidation 的錯誤轉換為標準的錯誤資訊,
            context.Errors.AddRange(
                result.Errors.Select(
                    error =>
                        new ValidationResult(error.ErrorMessage)
                )
            );
        }
    }
}

單元測驗當中的基本用法:

public class MyMethodInputValidator : AbstractValidator<MyMethodInput>
{
    public MyMethodInputValidator()
    {
        RuleFor(x => x.MyStringValue).Equal("aaa");
        RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb");
        RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3Validator());
    }
}

三、總結

總的來說 ABP vNext 為我們提供了多種引數驗證方法,一般來說使用 MVC 過濾器配合資料注解就夠了,如果你確實有一些特殊的需求,那也可以使用自己的方式對引數進行驗證,只需要實作 IObjectValidationContributor 介面就行,

需要看其他的 ABP vNext 相關文章?點擊我 即可跳轉到總目錄,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/103594.html

標籤:.NET Core

上一篇:04 .NET CORE 2.2 使用OCELOT -- identity認證授權

下一篇:.NetCore+WebUploader實作大檔案分片上傳

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more