主頁 > .NET開發 > [Abp vNext 原始碼分析] - 11. 用戶的自定義引數與配置

[Abp vNext 原始碼分析] - 11. 用戶的自定義引數與配置

2020-09-22 04:55:05 .NET開發

一、簡要說明

文章資訊:

基于的 ABP vNext 版本:1.0.0

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

更新日期:2019 年 10 月 24 日

ABP vNext 針對用戶可編輯的配置,提供了單獨的 Volo.Abp.Settings 模塊,本篇文章的后面都將這種用戶可變更的配置,叫做 引數,所謂可編輯的配置,就是我們在系統頁面上,用戶可以動態更改的引數值,

例如你做的系統是一個門戶網站,那么前端頁面上展示的 Title ,你可以在后臺進行配置,這個時候你就可以將網站這種全域配置作為一個引數,在程式代碼中進行定義,通過 GlobalSettingValueProvider(后面會講) 作為這個引數的值提供者,用戶就可以隨時對 Title 進行更改,又或者是某些通知的開關,你也可以定義一堆引數,讓用戶可以動態的進行變更,

二、原始碼分析

模塊啟動流程

AbpSettingsModule 模塊干的事情只有兩件,第一是掃描所有 ISettingDefinitionProvider (引數定義提供者),第二則是往配置引數添加一堆引數值提供者(ISettingValueProvider),

public class AbpSettingsModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        // 自動掃描所有實作了 ISettingDefinitionProvider 的型別,
        AutoAddDefinitionProviders(context.Services);
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        // 配置默認的一堆引數值提供者,
        Configure<AbpSettingOptions>(options =>
        {
            options.ValueProviders.Add<DefaultValueSettingValueProvider>();
            options.ValueProviders.Add<GlobalSettingValueProvider>();
            options.ValueProviders.Add<TenantSettingValueProvider>();
            options.ValueProviders.Add<UserSettingValueProvider>();
        });
    }

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

        services.OnRegistred(context =>
        {
            if (typeof(ISettingDefinitionProvider).IsAssignableFrom(context.ImplementationType))
            {
                definitionProviders.Add(context.ImplementationType);
            }
        });

        // 將掃描到的資料添加到 Options 中,
        services.Configure<AbpSettingOptions>(options =>
        {
            options.DefinitionProviders.AddIfNotContains(definitionProviders);
        });
    }
}

引數的定義

引數的基本定義

ABP vNext 關于引數的定義在型別 SettingDefinition 可以找到,內部的結構與 PermissionDefine 類似,,開發人員需要先定義有哪些可配置的引數,然后 ABP vNext 會自動進行管理,在網站運行期間,用戶、租戶可以根據自己的需要隨時變更引數值,

public class SettingDefinition
{
    /// <summary>
    /// 引數的唯一標識,
    /// </summary>
    [NotNull]
    public string Name { get; }

    // 引數的顯示名稱,是一個多語言字串,
    [NotNull]
    public ILocalizableString DisplayName
    {
        get => _displayName;
        set => _displayName = Check.NotNull(value, nameof(value));
    }
    private ILocalizableString _displayName;

    // 引數的描述資訊,也是一個多語言字串,
    [CanBeNull]
    public ILocalizableString Description { get; set; }

    /// <summary>
    /// 引數的默認值,
    /// </summary>
    [CanBeNull]
    public string DefaultValue { get; set; }

    /// <summary>
    /// 指定引數與其引數的值,是否能夠在客戶端進行顯示,對于某些密鑰設定來說是很危險的,默認值為 Fasle,
    /// </summary>
    public bool IsVisibleToClients { get; set; }

    /// <summary>
    /// 允許更改本引數的值提供者,為空則允許所有提供者提供引數值,
    /// </summary>
    public List<string> Providers { get; } //TODO: 考慮重命名為 AllowedProviders,

    /// <summary>
    /// 當前引數是否能夠繼承父類的 Scope 資訊,默認值為 True,
    /// </summary>
    public bool IsInherited { get; set; }

    /// <summary>
    /// 引數相關連的一些擴展屬性,通過一個字典進行存盤,
    /// </summary>
    [NotNull]
    public Dictionary<string, object> Properties { get; }

    /// <summary>
    /// 引數的值是否以加密的形式存盤,默認值為 False,
    /// </summary>
    public bool IsEncrypted { get; set; }

    public SettingDefinition(
        string name,
        string defaultValue = https://www.cnblogs.com/myzony/p/null,
        ILocalizableString displayName = null,
        ILocalizableString description = null,
        bool isVisibleToClients = false,
        bool isInherited = true,
        bool isEncrypted = false)
    {
        Name = name;
        DefaultValue = defaultValue;
        IsVisibleToClients = isVisibleToClients;
        DisplayName = displayName ?? new FixedLocalizableString(name);
        Description = description;
        IsInherited = isInherited;
        IsEncrypted = isEncrypted;

        Properties = new Dictionary();
        Providers = new List();
    }

    // 設定附加資料值,
    public virtual SettingDefinition WithProperty(string key, object value)
    {
        Properties[key] = value;
        return this;
    }

    // 設定 Provider 屬性的值,
    public virtual SettingDefinition WithProviders(params string[] providers)
    {
        if (!providers.IsNullOrEmpty())
        {
            Providers.AddRange(providers);
        }

        return this;
    }
}

上面的引數定義值得注意的就是 DefaultValueIsVisibleToClientsIsEncrypted 這三個屬性,默認值一般適用于某些系統配置,例如當前系統的默認語言,后面兩個屬性則更加注重于 安全問題,因為某些引數存盤的是一些重要資訊,這個時候就需要進行特殊處理了,

如果引數值是加密的,那么在獲取引數值的時候就會進行解密操作,例如下面的代碼,

SettingProvider 類中的相關代碼:

// ...
public class SettingProvider : ISettingProvider, ITransientDependency
{
    // ...
    public virtual async Task<string> GetOrNullAsync(string name)
    {
        // ...
        var value = https://www.cnblogs.com/myzony/p/await GetOrNullValueFromProvidersAsync(providers, setting);
        // 對值進行解密處理,
        if (setting.IsEncrypted)
        {
            value = SettingEncryptionService.Decrypt(setting, value);
        }

        return value;
    }

    // ...
}

引數不對客戶端可見的話,在默認的 AbpApplicationConfigurationAppService 服務類中,獲取引數值的時候就會跳過,

private async Task<ApplicationSettingConfigurationDto> GetSettingConfigAsync()
{
    var result = new ApplicationSettingConfigurationDto
    {
        Values = new Dictionary<string, string>()
    };

    foreach (var settingDefinition in _settingDefinitionManager.GetAll())
    {
        // 不會展示這些屬性為 False 的引數,
        if (!settingDefinition.IsVisibleToClients)
        {
            continue;
        }

        result.Values[settingDefinition.Name] = await _settingProvider.GetOrNullAsync(settingDefinition.Name);
    }

    return result;
}

引數定義的掃描

跟權限定義類似,所有的引數定義都被放在了 SettingDefinitionProvider 里面,如果你需要定義一堆引數,只需要繼承并實作 Define(ISettingDefinitionContext) 抽象方法就可以了,

public class TestSettingDefinitionProvider : SettingDefinitionProvider
{
    public override void Define(ISettingDefinitionContext context)
    {
        context.Add(
            new SettingDefinition(TestSettingNames.TestSettingWithoutDefaultValue),
            new SettingDefinition(TestSettingNames.TestSettingWithDefaultValue, "default-value"),
            new SettingDefinition(TestSettingNames.TestSettingEncrypted, isEncrypted: true)
        );
    }
}

因為我們的 SettingDefinitionProvider 實作了 ISettingDefinitionProviderITransientDependency 介面,所以這些 Provider 都會在組件注冊的時候(模塊里面有定義),添加到對應的 AbpSettingOptions 內部,方便后續進行呼叫,

引數定義的管理

我們的 引數定義提供者引數值提供者 都賦值給 AbpSettingOptions 了,首先看有哪些地方使用到了 引數定義提供者

第二個我們已經看過,是在模塊啟動時有用到,第一個則是有一個 SettingDefinitionManager ,顧名思義就是管理所有的 SettingDefinition 的管理器,這個管理器提供了三個方法,都是針對 SettingDefinition 的查詢功能,

public interface ISettingDefinitionManager
{
    // 根據引數定義的標識查詢,不存在則拋出 AbpException 例外,
    [NotNull]
    SettingDefinition Get([NotNull] string name);

    // 獲得所有的引數定義,
    IReadOnlyList<SettingDefinition> GetAll();

    // 根據引數定義的標識查詢,如果不存在則回傳 null,
    SettingDefinition GetOrNull(string name);
}

接下來我們看一下它的默認實作 SettingDefinitionManager ,它的內部沒什么說的,只是注意 SettingDefinitions 的填充方式,這里使用了執行緒安全的 懶加載模式,只有當用到的時候,才會呼叫 CreateSettingDefinitions() 方法填充資料,

public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency
{
    protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; }

    protected AbpSettingOptions Options { get; }

    protected IServiceProvider ServiceProvider { get; }

    public SettingDefinitionManager(
        IOptions<AbpSettingOptions> options,
        IServiceProvider serviceProvider)
    {
        ServiceProvider = serviceProvider;
        Options = options.Value;

        // 填充的時候,呼叫 CreateSettingDefinitions 方法進行填充,
        SettingDefinitions = new Lazy<IDictionary<string, SettingDefinition>>(CreateSettingDefinitions, true);
    }

    // ...

    protected virtual IDictionary<string, SettingDefinition> CreateSettingDefinitions()
    {
        var settings = new Dictionary<string, SettingDefinition>();

        using (var scope = ServiceProvider.CreateScope())
        {
            // 從 Options 中得到型別,然后通過 IoC 進行實體化,
            var providers = Options
                .DefinitionProviders
                .Select(p => scope.ServiceProvider.GetRequiredService(p) as ISettingDefinitionProvider)
                .ToList();

            // 執行每個 Provider 的 Define 方法填充資料,
            foreach (var provider in providers)
            {
                provider.Define(new SettingDefinitionContext(settings));
            }
        }

        return settings;
    }
}

引數值的管理

當我們構建好引數的定義之后,我們要設定某個引數的值,或者說獲取某個引數的值應該怎么操作呢?查看相關的單元測驗,看到了 ABP vNext 自身是注入 ISettingProvider ,呼叫它的 GetOrNullAsync() 獲取引數值,

private readonly ISettingProvider _settingProvider;

var settingValue = https://www.cnblogs.com/myzony/p/await _settingProvider.GetOrNullAsync("WebSite.Title")

跳轉到介面,發現它有兩個實作,這里我們只講解一下 SettingProvider 類的實作,

獲取引數值

直奔主題,來看一下 ISettingProvider.GetOrNullAsync(string) 方法是怎么來獲取引數值的,

public class SettingProvider : ISettingProvider, ITransientDependency
{
    protected ISettingDefinitionManager SettingDefinitionManager { get; }
    protected ISettingEncryptionService SettingEncryptionService { get; }
    protected ISettingValueProviderManager SettingValueProviderManager { get; }

    public SettingProvider(
        ISettingDefinitionManager settingDefinitionManager,
        ISettingEncryptionService settingEncryptionService,
        ISettingValueProviderManager settingValueProviderManager)
    {
        SettingDefinitionManager = settingDefinitionManager;
        SettingEncryptionService = settingEncryptionService;
        SettingValueProviderManager = settingValueProviderManager;
    }

    public virtual async Task<string> GetOrNullAsync(string name)
    {
        // 根據名稱獲取引數定義,
        var setting = SettingDefinitionManager.Get(name);

        // 從引數值提供者管理器,獲得一堆引數值提供者,
        var providers = Enumerable
            .Reverse(SettingValueProviderManager.Providers);

        // 過濾符合引數定義的提供者,這里就是用到了之前引數定義的 List<string> Providers 屬性,
        if (setting.Providers.Any())
        {
            providers = providers.Where(p => setting.Providers.Contains(p.Name));
        }

        //TODO: How to implement setting.IsInherited?
        //TODO: 如何實作 setting.IsInherited 功能?

        var value = https://www.cnblogs.com/myzony/p/await GetOrNullValueFromProvidersAsync(providers, setting);
        // 如果引數是加密的,則需要進行解密操作,
        if (setting.IsEncrypted)
        {
            value = SettingEncryptionService.Decrypt(setting, value);
        }

        return value;
    }

    protected virtual async Task GetOrNullValueFromProvidersAsync(IEnumerable providers,
    SettingDefinition setting)
    {
        // 只要從任意 Provider 中,讀取到了引數值,就直接進行回傳,
        foreach (var provider in providers)
        {
            var value = await provider.GetOrNullAsync(setting);
            if (value != null)
            {
                return value;
            }
        }

        return null;
    }

    // ...
}

所以真正干活的還是 ISettingValueProviderManager 里面存放的一堆 ISettingValueProvider ,這個 引數值管理器 的介面很簡單,只提供了一個 List<ISettingValueProvider> Providers { get; } 的定義,

它會從模塊配置的 ValueProviders 屬性內部,通過 IoC 實體化對應的引數值提供者,

_lazyProviders = new Lazy<List<ISettingValueProvider>>(
    () => Options
        .ValueProviders
        .Select(type => serviceProvider.GetRequiredService(type) as ISettingValueProvider)
        .ToList(),
    true

引數值提供者

引數值提供者的介面定義是 ISettingValueProvider,它定義了一個名稱和 GetOrNullAsync(SettingDefinition) 方法,后者可以通過引數定義獲取存盤的值,

public interface ISettingValueProvider
{
    string Name { get; }

    Task<string> GetOrNullAsync([NotNull] SettingDefinition setting);
}

注意這里的回傳值是 Task<string> ,也就是說我們的引數值型別必須是 string 型別的,如果需要存盤其他的型別可能就需要從 string 進行型別轉換了,

在這里的 SettingValueProvider 其實類似于我們之前講過的 權限提供者,因為 ABP vNext 考慮到了多種情況,我們的引數值有可能是根據用戶獲取的,同時也有可能是根據不同的租戶進行獲取的,所以 ABP vNext 為我們預先定義了四種引數值提供器,他們分別是 DefaultValueSettingValueProviderGlobalSettingValueProviderTenantSettingValueProviderUserSettingValueProvider

下面我們就來講講這幾個不同的引數提供者有啥不一樣,

DefaultValueSettingValueProvider

顧名思義,默認值引數提供者就是使用的引數定義里面的 DefaultValue 屬性,當你查詢某個引數值的時候,就直接回傳了,

public override Task<string> GetOrNullAsync(SettingDefinition setting)
{
    return Task.FromResult(setting.DefaultValue);
}

GlobalSettingValueProvider

這是一種全域的提供者,它沒有對應的 Key,也就是說如果資料庫能查到 ProviderNameG 的記錄,就直接回傳它的值了,

public class GlobalSettingValueProvider : SettingValueProvider
{
    public const string ProviderName = "G";

    public override string Name => ProviderName;

    public GlobalSettingValueProvider(ISettingStore settingStore) 
        : base(settingStore)
    {
    }

    public override Task<string> GetOrNullAsync(SettingDefinition setting)
    {
        return SettingStore.GetOrNullAsync(setting.Name, Name, null);
    }
}

TenantSettingValueProvider

租戶提供者,則是會將當前登錄租戶的 Id 結合 T 進行查詢,也就是引數值是按照不同的租戶進行隔離的,

public class TenantSettingValueProvider : SettingValueProvider
{
    public const string ProviderName = "T";

    public override string Name => ProviderName;

    protected ICurrentTenant CurrentTenant { get; }
    
    public TenantSettingValueProvider(ISettingStore settingStore, ICurrentTenant currentTenant)
        : base(settingStore)
    {
        CurrentTenant = currentTenant;
    }

    public override async Task<string> GetOrNullAsync(SettingDefinition setting)
    {
        return await SettingStore.GetOrNullAsync(setting.Name, Name, CurrentTenant.Id?.ToString());
    }
}

UserSettingValueProvider

用戶提供者,則是會將當前用戶的 Id 作為查詢條件,結合 U 在資料庫進行查詢匹配的引數值,引數值是根據不同的用戶進行隔離的,

public class UserSettingValueProvider : SettingValueProvider
{
    public const string ProviderName = "U";

    public override string Name => ProviderName;

    protected ICurrentUser CurrentUser { get; }

    public UserSettingValueProvider(ISettingStore settingStore, ICurrentUser currentUser)
        : base(settingStore)
    {
        CurrentUser = currentUser;
    }

    public override async Task<string> GetOrNullAsync(SettingDefinition setting)
    {
        if (CurrentUser.Id == null)
        {
            return null;
        }

        return await SettingStore.GetOrNullAsync(setting.Name, Name, CurrentUser.Id.ToString());
    }
}

引數值的存盤

除了 DefaultValueSettingValueProvider 是直接從引數定義獲取值以外,其他的引數值提供者都是通過 ISettingStore 讀取引數值的,在該模塊的默認實作當中,是直接回傳 null 的,只有當你使用了 Volo.Abp.SettingManagement 模塊,你的引數值才是存盤到資料庫當中的,

我這里不再詳細決議 Volo.Abp.SettingManagement 模塊的其他實作,只說一下 ISettingStore 在它內部的實作 SettingStore

public class SettingStore : ISettingStore, ITransientDependency
{
    protected ISettingManagementStore ManagementStore { get; }

    public SettingStore(ISettingManagementStore managementStore)
    {
        ManagementStore = managementStore;
    }

    public Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
    {
        return ManagementStore.GetOrNullAsync(name, providerName, providerKey);
    }
}

我們可以看到它也只是個包裝,真正的操作型別是 ISettingManagementStore

引數值的設定

在 ABP vNext 的核心模塊當中,是沒有提供對引數值的變更的,只有在 Volo.Abp.SettingManagement 模塊內部,它提供了 ISettingManager 管理器,可以進行引數值的變更,原理很簡單,就是對資料庫對應的表進行修改而已,

public async Task SetAsync(string name, string value, string providerName, string providerKey)
{
    // 操作倉儲,查詢記錄,
    var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
    
    // 新增或者更新記錄,
    if (setting == null)
    {
        setting = new Setting(GuidGenerator.Create(), name, value, providerName, providerKey);
        await SettingRepository.InsertAsync(setting);
    }
    else
    {
        setting.Value = https://www.cnblogs.com/myzony/p/value;
        await SettingRepository.UpdateAsync(setting);
    }
}

三、總結

ABP vNext 提供了多種引數值提供者,我們可以根據自己的需要靈活選擇,如果不能夠滿足你的需求,你也可以自己實作一個引數值提供者,我建議對于用戶在界面可更改的引數,都可以使用 SettingDefinition 定義成引數,可以根據不同的情況進行配置讀取,

ABP vNext 其他模塊用到的許多引數,也都是使用的 SettingDefinition 進行定義,例如 Identity 模塊用到的密碼驗證規則,就是通過 ISettingProvider 進行讀取的,還有當前程式的默認語言,

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

下面附上 E2Home 的總結,很詳細:

  1. 在各個模塊中定義設定資料源的類來設定配置鍵值對, 該類只需要繼承介面 ISettingDefinitionProvider 或者 SettingDefinitionProvider 實作類
    ABP 會自動尋找被注冊,最后會將配置鍵值對都匯總到 SettingProvider 類中,如果是存盤在資料庫中的,則需要重寫 ISettingStore
    當然建議依賴 Volo.Abp.SettingManagement.Domain 這個模塊,如果資料表是用自定義的,則建議重寫 ISettingRepository 介面即可,

  2. ConfigureServices() 方法中注冊添加 ISettingValueProvider,比如:值是 json 格式的,就可以定義一個設定值 Provider 來決議,

  3. ISettingValueProvider 可以有多個,并且按倒序進行執行,只要能獲取到值就回傳,不再繼續往下執行,一般自定義的 ISettingValueProvider 放在后面,

  4. 如果將敏感資料保存到設定管理,則建議采用加密的方式,只需要重寫 ISettingEncryptionService 即可, 引數定義:IsEncrypted = true

  5. Volo.Abp.SettingManagement.Domain 是采用資料庫加快取的方式來讀寫設定的,
    通過 SettingCacheItemInvalidator 來注冊 Setting 物體的 EntityChanged 事件,從而達到快取能跟物體同步更新,

  6. 為啥 ABP 還需要設定管理,而不用 .NET Core 自帶的配置(Configuration)?
    因為 ABP 設定管理可以做到三個層級,用戶,租戶和全域(系統級),同時 ABP 的設定管理只是做了一層封裝,
    具體的資料源可以是 .NET Core 自帶的配置(Configuration),也可以是分布式配置,只不過需要我們自己去寫擴展,

  7. 另外建議大家對引數進行打包,比如郵件相關的引數可以封裝在一個 EmailConfig 類中,郵件 Host,用戶名和密碼都是該類的屬性,而具體取值同時通過 ISettingValueProvider 來獲取的,建議加入分布式快取,

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

標籤:.NET Core

上一篇:NServiceBus+Saga開發分布式應用

下一篇:.Net Core 3.0 IdentityServer4 快速入門

標籤雲
其他(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