顯示具有 .NET-ASP.NET MVC 標籤的文章。 顯示所有文章
顯示具有 .NET-ASP.NET MVC 標籤的文章。 顯示所有文章

2020年4月18日 星期六

[MVC/Pattern] Unit of Work與Repository模式紀錄

前言:
上一次用到Unif Of Work,已經快3年了,有機會就來記錄一下吧。

先上個圖:


Repository部分讓商業邏輯跟資料存取可以拆開,也是一種pattern,
IRepository定義了常用的CRUD操作,使用泛型處理不同的資料實體,
建立Repository instance時,注入搭配的UnitOfWork instance。

而Unit Of Work負責處理資料交易的部分,確保資料與db是一致的。
當然,多了這些抽象,寫測試也就水到渠成了。

實際程式碼如下:
public interface IUnitOfWork : IDisposable
{ 

	DbContext DbContext { get; set; }
    
	void Commit();

}

public class UnitOfWork : IUnitOfWork
{

        public DbContext DbContext { get; set; }

        public UnitOfWork()
        {
            DbContext = new ApplicationDbContext();
        }

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

        public void Dispose()
        {
            DbContext.Dispose();
        }
        
}

public interface IRepository<T> where T : class
{

        IUnitOfWork UnitOfWork { get; set; }

        void Create(T entity);

        IQueryable<T> ReadAll();

        IQueryable<T> Read(Expression<Func<T, bool>> filter);

        void Delete(T entity);

        void Save();

}

public class Repository<T> : IRepository<T> where T : class
{

	private DbSet _entity;
    
	public Repository(IUnitOfWork unitOfWork)
	{
	    UnitOfWork = unitOfWork;
	}

	public IUnitOfWork UnitOfWork { get; set; }

	public void Create(T entity)
        {
            Entity.Add(entity);
        }

        public IQueryable ReadAll()
        {
            return Entity;
        }

        public IQueryable Read(Expression> filter)
        {
            return Entity.Where(filter);
        }

        public void Delete(T entity);
        {
            Entity.Remove(entity);
        }

        public void Save()
        {
            UnitOfWork.Commit();
        }

}


參考資料:
https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
https://web.csulb.edu/~pnguyen/cecs475/pdf/repository%20pattern.pdf

2018年11月13日 星期二

[ASP.Net MVC] Github OAuth外部驗證無法使用(要求已經中止: 無法建立 SSL/TLS 的安全通道/loginInfo為null)

前言:
OAuth是一種外部驗證的標準,可讓第三方應用程式,存取資源擁有者的資源。
通過驗證後,第三方應用程式會從OAuth Server拿到token,
即可使用token存取資源伺服器上的資源了。

.Net MVC 5預設有提供外部驗證(Google、FB、Microsoft與Twitter等),
如要使用Github OAuth驗證,NuGet上也有package可直接使用:
裝好package後,打開Startup.Auth.cs,
輸入app.UseGitHubAuthentication("clientID", "clientSecret");
基本上就可使用了。

需啟用https,才可以進行OAuth驗證。

不過在本機使用.Net framework 4.5.2+MVC 5.2.3驗證卻無法登入:
按下外部登入的GitHub按鈕,狀態列顯示瀏覽器在動作,但過了一會,
發現畫面依然停留在登入畫面,WTF??

取得Owin.Security.Providers.GitHub的source code來trace,
發現在發request要token這裡,
出現Exception:「要求已經中止: 無法建立 SSL/TLS 的安全通道」,
(The request was aborted: Could not create SSL/TLS secure channel.)
導致ExternalLoginCallback中loginInfo為null,又回到Login頁面。

那為何出現該Exceptioin? 參考黑大與其他文章,
原來是.Net 4.5預設https的protocol是TLS 1.0,但TLS 1.0因安全性問題,
已被大家捨棄,使用TLS 1.2以上是現行的做法。

解決辦法:
● 因.Net 4.6預設使用TLS 1.2,新專案可直接使用framework 4.6以上,試過ok,直接升級framework試過不行。
● 若是參考source code,可以在handler要token前的request加入:
 System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolTypeExtensions.Tls12;

● 修改registry機碼,將環境改為TLS 1.2,可參考下圖。



參考資料:
https://tools.ietf.org/html/rfc6749#section-1.3.1
https://blog.darkthread.net/blog/disable-tls-1-0-issues
https://support.microsoft.com/en-us/help/3154520/support-for-tls-system-default-versions-included-in-the-net-framework






2018年8月22日 星期三

[.Net MVC] Items could not be selected(null) in Multiple Select(ListBox)

前言:
網頁中常用Multi Select或稱ListBox供使用者多選Items,如:
<select multiple>
<option value="Taiwan">Taiwan</option>
<option value="USA">USA</option>
</select>
 或
@Html.ListBox("country", null, new {multiple = multiple})

當option items render後,又沒有被點選,會發生回傳item為null的狀況,
該如何處理?


作法:
● 在form post前,將option加上selected的屬性。
$('form').submit(function(){
   $('#target option').attr("selected", "selected");
});
若上述做法行不通,可使用下面方法:
● append option value到某一hidden element,再post到後端。



參考資料:
  

2017年11月1日 星期三

[.Net MVC] 使用MVC BundleConfig的{version} wildcard的注意事項


使用ASP.Net MVC預設範本的BundleConfig,
會使用{version}來bundle jQuery套件,如:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js"));


之前看微軟文件, 使用{version}萬用字元的好處其一是:
  • debug組態:會自動bundle full debug版本。
  • release組態:就bundle min版本。
看起來也很直覺。

但有一次在release組態,我將full debug(jquery-x.x.x.js)的版本拿掉,
發現jquery沒有正確載入,才發現MVC預設會忽略min版本(in the bundles.IgnoreList),
所以應是使用full debug版本bundle與minify,
再加入HttpRuntime.Cache,如下圖紅框處。



因此,在Scripts資料夾下放full debug版本的js或css應該就沒問題了。
to be continued...


參考資料:
https://stackoverflow.com/questions/29254181/bundling-not-working-in-mvc5-when-i-turn-on-release-mode
https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification
https://stackoverflow.com/questions/21270834/asp-net-mvc-bundle-not-rendering-script-files-on-staging-server-it-works-on-dev
http://www.mytecbits.com/microsoft/dot-net/asp-net-mvc-bundle-rendering
http://demo.tc/post/779

2017年10月17日 星期二

[.Net MVC] 使用ValidationAttribute與IClientValidatable自訂前後端驗證 (Input Date isn't greater than today)

說明:
之前提過Remote驗證預設沒有後端驗證,
雖然可以自行實作後端驗證。
但MVC提供了自訂驗證(Custom validation),
可自訂驗證邏輯,並含括前後端驗證,
既然是預設的,就來試看看吧。

實作:
首先,新增一類別做驗證,並宣告為sealed,
因該類別單純用來做驗證,
再繼承ValidationAttribute,並override IsValid(object value)方法,
方法內,編寫驗證邏輯。

P.S. 帶ValidationContext的isValid方法,
可存取ViewModel該驗證屬性的資訊。



掛上剛新增的類別,記得要using類別所在namespace。


但沒有觸發前端驗證??


因為繼承ValidationAttribute只有後端驗證,
所以要補上前端驗證,在驗證類別再加上實作IClientValidatable介面,
也就是實作GetClientValidationRules方法。
方法內,需建構ModelClientValidationRule instance,
其中ValidationType傳入字串需要小寫,用來連結前端的html attribute與js方法。


這個rule會render到該html element的attribute。


最後,透過實作jQuery.validator的自訂驗證方法。


前端驗證也觸發了。


參考資料:
http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/enabling-client-side-validation-on-custom-data-annotations-w/
http://demo.tc/post/687
https://jqueryvalidation.org/jQuery.validator.addMethod/

2017年9月20日 星期三

[.Net MVC/jQuery] add TextBox Numeric/Spinner

說明:
如果想讓TextBox(input element)可以用選的,如下圖:




該怎麼做呢?

作法:
HTML 5有支援Number input type按此可知那些瀏覽器有支援,
在input element內設定type="number",chrome與firefox就可以用選的(spinner)。
但是...,IE 11Edge雖然有支援numeric,但沒有實作spinner XD。

如果不想自己寫css跟js,可以使用jQuery.UI提供的spinner,demo按此
或是Number polyfill

這裡使用jQuery.UI的spinner來試試。
首先使用NuGet或自行下載jQuery.UI package,目前最新版為1.12.1,有相依於jQuery。
再include jquery-ui.css與jquery-ui-1.12.1.js。


這裡遵照unobtrusive的原則,將HTML跟JS分開。

MVC 5.1後,EditorFor可以加HTML attribute的object了。
也可以建立專用的EditorTemplates,類似需求直接掛屬性即可。

執行看看,IE跟Edge都有spinner了。


參考資料:
http://html5please.com/#number
https://docs.microsoft.com/en-us/aspnet/mvc/overview/releases/mvc51-release-notes#new-features

2017年8月28日 星期一

[.Net MVC] Remote validation example (Input Date isn't greater than today)

說明:
Remote驗證是一種假前端驗證,背後是用ajax呼叫後端Action,
方便開發人員在後端編寫驗證邏輯,不過預設只能在前端觸發。

範例:
首先新增一Controller做驗證,注意Action傳入參數名稱要與ViewModel對應的屬性名稱相同(Model binding)。


在ViewModel該屬性上掛上RemoteAttribute class。
第一個參數是Action name,第二個參數是Controller name。


View部分,使用HtmlHelper產生html element,
需再include jquery、jquery-validate與jquery.validate.unobtrusive.js。

執行網頁後,檢視HTML發現多了remote相關屬性:

切到Network tab,當觸發該欄位時,會送出get request,後端傳回true或false。

實際畫面如下。

結論:
● Remote驗證還是前端驗證,因此不保險,重要驗證還是須通過後端驗證。
● 若觸發欄位無變動,不會送出request,多欄位比較驗證要注意(例:兩個日期比較)。


參考資料:
https://msdn.microsoft.com/en-us/library/system.web.mvc.remoteattribute(v=vs.118).aspx