Asp.net Core 使用 In Memory Caching

Cache 主要目的就是為了減少資料庫存取,加快應用程式速度

但系統的架構設計是,必須要測試是否資料存在 in memory cache,否則就要跟底層的資料庫取得,因為 in memory cache 不保證資料的存在期(也就是他有可能會消失)。

此外,如果是 web farm (代表有多個 web server)就必須要使用 分散式 Cached 機制,確保每台都可以讀取。

 

使用方式:

加入 package 連結:

"Microsoft.Extensions.Caching.Memory":
"1.0.0-rc2-final",

因為 Cache 是一種 Service,因此要透過 DI 加入:

接下來就可以使用 Constructor injection 呼叫:

Cache 使用方式很簡單,用 Get 取出資料(無 = null),或者用 TryGet 使用 out 取出資料,會回傳 true/false。

用 set 設定資料,並且可以用  MemoryCacheEntryOptions 指定 cache 的有效期間;範例如下:

請注意:預設 MemoryCache 會自動調整 Cache 內容,如果記憶體過大,會自動移除部份內容。可以使用 CacheItemPriority.NeverRemove 讓它不要移除:

如果要移除,就用以下命令:

cache.Remove(cacheKey);

其他內容包含:CancellationTokenSource、Cache Dependencies and
Callbacks
主要用途為控制跟後端資料提供者同步,因使用機會不大,若有需要請自行參考。

 

如何讓 TagHelper 取出 Model 屬性的特性(Attribute)

客製化 TagHelper 是 Asp.net Core 一個很重要的擴充,允許我們非常方便的建立各種彈性的運用。另外一方面在 Model 中如何定義好屬性(Property),也有相當一部分要透過 Attribute 擴充我們的定義。

以下定義一個 Attribute 用途只為了標註在特定屬性中,可以對應的代碼:

[AttributeUsage(AttributeTargets.Property)]
public class DropdownAttribute : Attribute
{
    public string CodeMap { get; set; }

    public DropdownAttribute(string code)
    {
        this.CodeMap = code;
    }
}

使用方式很簡單,直接在物件中的屬性加入此 Attribute 就可以指定代碼:

public class DailyReportItem
{
    public int Visitors { get; set; }
    
    [Required]
    [Dropdown("DailyProgress")]
    public string Progress { get; set; }
}

現在我們希望可以自訂 TagHelper,用來處理 Required attribute 的內容(上面範例的 DailyProgress)。在轉寫自訂義的 TagHelper,只有一點需要注意:HtmlAttributeName 要使用 ModelExpression 型態,不可以用 String。這裡最大差別在於如果指定 String 則 razor 會傳入值,而非物件本身;但我們需要物件本身用來解析屬性值。

程式碼如下:

public class CustomerTagHelper: TagHelper
{
    [HtmlAttributeName("asp-for")]
    public ModelExpression Source { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "p";
        output.TagMode = TagMode.StartTagAndEndTag;

        var contents = $@"
            Model name: {Source.Metadata.ContainerType.FullName}<br/>
            Property name: {Source.Name}<br/>
            Current Value: {Source.Model}<br/> 
            Is Required: {Source.Metadata.IsRequired}";

        output.Content.SetHtmlContent(new HtmlString(contents));
    }
}

其中 IsRequired 就是代表是否有宣告 required attribute。使用方式跟一般宣告一樣:

<customer asp-for="Name"></customer>

如果需要處理 Dropdown attributte,可以用 MetaData 取出客製化的屬性:

var dropdown = model.Metadata.ContainerType.GetProperty(code)
                    .GetCustomAttributes()
                    .OfType<DropdownAttribute>().FirstOrDefault();

Asp.net core Identity 如何使用既有的 AspnetUser 密碼驗證?

主要重點在於密碼的加密機制,因為 .net framework (MVC5) 使用 IdentityV2,其 hash 的加密方式比較不嚴謹,新版的 dotnet core 使用 IdentityV3 更好的 hash 機制,因此兩者不相容。

修改方案如下:

You just have to add this line in ConfigureServices in Startup.cs:

services.Configure<PasswordHasherOptions>(options =>
    options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2
);

來自 <https://stackoverflow.com/questions/47850720/signinmanager-passwordsigninasync-always-return-failure>

補充說明:

_signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false)

這段寫法第一個雖然是用 EMAIL,但實際上 PASSWORD checking 是用 UserName,因此,如果 UserName = Email 就會正確,否則就必須要先讀出 UserName 才可以正確比對。

此外,新的 IdentityV3 一定要使用 NormalizedUserName property 作為登入的 User name(但 UserName 仍然有使用,兩者最大的差異在於 Normalized 一律轉成大寫)。