[C#]使用rx.net來幫助完成主動通知和訂閱的功能
前言
rxjs已經算是在前端很紅的一個主題了,不過相對來說rx.net則比較少看到有人討論,其實rx提供了很多類似functional programming的概念,甚至還提供了類似event emit的想法,也有更多處理多個非同步的強大operator,而今天想要來筆記一下,如何使用rx來完成推播和訂閱的需求,而這邊的主題最主要會使用到的就是subject這個名詞。
導覽
首先我們需要在visual studio安裝rx.net,開起nuget並搜尋reactive的關鍵字,然後如下圖下載
此舉會同時幫你安裝core和interface的部份,而我們會使用到的很多operator都是包裝在Linq裡面,同時也會看到很多已經熟悉的lambda語法,而如果爬文比較多看到的都是rxjs的介紹,就連官方對rx.net的說明也少得可憐,所以如果需要使用到rx.net的話,必須覺悟可能得花很多時間自行摸索,而如果對很多觀念不懂的話,如果對前端熟悉的話,可以由rxjs來了解觀念。
試想一下我們傳統在做快取資料的時候,都是用時間性來達成,但如果我們有新資料想要通知快取更新的話,目前有另一種redis的做法,預設就有主動通知的api可以使用,但redis快取比較適合在很多系統的共享資料情境,如果我們只是想要在各別系統快取的話,還是放在各系統的ap上比較適合,而這時候我們其實就可以使用rx.net來達成即時通知快取更新資料的做法。
首先我來摸擬一個會員登入資料,當有其他會員登入資料的時候,api會即時傳給前端總共有哪些會員登入,先來實做一個Member.cs的dto
public class MemberModel
{
public int Id { get; set; }
public string Name { get; set; }
}
接著我們先做兩筆假資料,然後使用memory cache的方式來做快取,程式碼如下
public class CacheService
{
ObjectCache cache = MemoryCache.Default;
public void Init()
{
if (cache.Get("Members") == null)
{
var Members = new List<MemberModel>
{
new MemberModel {Id=1,Name="kin" },
new MemberModel {Id=2,Name="anson" },
};
var policy = new CacheItemPolicy { Priority = CacheItemPriority.NotRemovable };
cache.Add("Members", Members, policy);
}
}
public List<MemberModel> GetMembers()
{
return cache.Get("Members") as List<MemberModel>;
}
}
因為我打算在系統一開始的時候,就先把快取寫進去了,所以我選擇在Global.asax裡面去Init快取資料
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
CacheService service = new CacheService();
service.Init();
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
接著我是使用swagger直接測試,以確保資料是有寫進memory裡面
public class ValuesController : ApiController
{
CacheService _service = new CacheService();
// GET: api/Values
public IHttpActionResult Get()
{
return Ok(_service.GetMembers());
}
}
結果如下圖
接著我們需要把寫快取的時機點,改成使用訂閱的方式來實現,也就是我們去訂閱Subject,而何時要去快取的機制則改成用rx來實現,首先來看一下我們如何實做這個部份,新增一支EventSubjects.cs
public static class EventSubjects
{
public static Subject<MemberModel> SubjectMembers { get; set; } = new Subject<MemberModel>();
}
接著我希望訂閱的時候一樣在系統一開始就去執行,所以一樣在Global.asax動手腳
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
CacheService service = new CacheService();
service.Init();
service.SubscribeSubjects(); //新增訂閱subject
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
然後替CacheService新增訂閱的方法
public class CacheService
{
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy { Priority = CacheItemPriority.NotRemovable };
public void Init()
{
if (cache.Get("Members") == null)
{
var Members = new List<MemberModel>
{
new MemberModel {Id=1,Name="kin" },
new MemberModel {Id=2,Name="anson" },
};
cache.Add("Members", Members, policy);
}
}
/// <summary>
/// 訂閱的方法
/// </summary>
public void SubscribeSubjects()
{
EventSubjects.SubjectMembers.Subscribe(x =>
{
var result = GetMembers();
var member=result.FirstOrDefault(r => r.Id == x.Id);
if (member == null) result.Add(x);
cache.Set("Members", result, policy);
});
}
public List<MemberModel> GetMembers()
{
return cache.Get("Members") as List<MemberModel>;
}
}
大功告成了,自此之後什麼時候重寫快取就是用訂閱的方式來實現了
這邊則是關鍵點了,當我們打web api的post時候,假設成功就去觸發subject去做主動通知
public class ValuesController : ApiController
{
CacheService _service = new CacheService();
// GET: api/Values
public IHttpActionResult Get()
{
return Ok(_service.GetMembers());
}
public void Post(MemberModel model)
{
EventSubjects.SubjectMembers.OnNext(model);
}
}
整個完成之後我們就可以測試看看,是否會如預期的當我新增資料的時候,資料是否有順利寫進快取。
一開始的資料
接著打post
重新再Get一次去拉快取資料
確認我們是使用主動通知的方式,去通知寫進快取資料了。
研究rx.net的時候,文件確實很缺乏,這就造成要研究這個技術確實不是那麼容易入門和學習,畢竟文件不全相關文章也不多,但相對性的來說rxjs則是有很多相關應用,而rxjs光是operator的相關應用就博大精深了,所以有興趣的筆者就請至rxjs30天這系列文章去做一些深入了解(https://ithelp.ithome.com.tw/articles/10186104),而這個連結更是用圖示的方式讓你理解各種operator的應用(http://rxmarbles.com/),此篇沒什麼用到operator的部份,主要是使用到subject的特性,如果對此篇文章有任何疑問或意見,再請回覆囉。