Keywords: Task implement, ASP.NET 2.0 Provider Model
Web應用中有很多耗時的操作比如發送email、產生報表、處理上傳圖片等等,這些操作是不適合放到頁面中的。比如回複一個文章後email通知所有訂閱了該文章更新的使用者,如果在回複操作中處理,那麼使用者回複文章時大部分的時間就會浪費在發送email上了。在CommunityServer裡,類似的耗時的操作都被獨立出來,作為獨立的任務運行於服務端同一程式域中的一個或多個線程中。本文通過email notification的實現,來具體瞭解一下這種實現模式。涉及的知識點包括:.NET configuration API, Provider Model, ASP.NET 多線程, 抽象原廠模式等。
描述:CS Email Notifcation 包括兩部分產生email和發送email。產生email時,針對每個發送地址產生了一個具體的.NET Mail Mesage對象,序列化後存入資料庫。發送時還原序列化每個Mail Message對象,調用.NET SMTP API發送。發送email可以作為服務獨立出來,而產生email是和具體的業務相關的,不能作為獨立的服務。但這也是個耗時的操作,涉及到序列化,有時也會有大量email的產生。CS採用非同步事件調用,模仿Http Module,定義全域事件,以plug-in的方式載入module,在module中hook全域事件,在事件中處理具體的操作,如產生email。通過事件,使主程式之外的操作非同步進行。以Provider Model的方式來hook事件,增強了程式的可擴充性,實現了隨插即用。
代碼:
部分代碼下載
先看一下設定檔,CS中並沒有使用.net 設定檔(即web.config檔案)和標準API,本文將其修改為標準的,並採用ASP.NET 2.0中標準的Provider Model。
<configSections>
<section name="poConfiguration" type="PO.Component.Configuration.POConfigurationSection, PO.Component"/>
</configSections>
<poConfiguration>
<modules>
<add name="EmailNotificationModule" type="PO.Component.Module.EmailNotificationModule, PO.Component" />
</modules>
<providers>
<add name="CommonDataProvider" type="PO.Component.Provider.SqlCommonDataProvider, PO.Component" connectionStringName="ShippingWisePortalConnectionString" />
<add name="EmailTemplateProvider" type="PO.Component.Provider.XmlEmailTemplateProvider, PO.Component" />
<add name="NotificationProvider" type="PO.Component.Provider.EmailNotificationProvider, PO.Component" />
</providers>
<tasks>
<modules>
</modules>
<threads>
<thread minutes="1">
<add name="Emails" type="PO.Component.Task.EmailJob, PO.Component" enabled="true" enableShutDown="false" failureInterval="1" numberOfTries="10" />
</thread>
</threads>
</tasks>
</poConfiguration>
設定檔的操作:
整個自訂節poConifguration
namespace PO.Component.Configuration
{
public class POConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("modules")]
public ProviderSettingsCollection Modules
{
get { return (ProviderSettingsCollection)base["modules"]; }
}
[ConfigurationProperty("providers")]
public ProviderSettingsCollection Providers
{
get { return (ProviderSettingsCollection)base["providers"]; }
}
[ConfigurationProperty("tasks")]
public TasksElement Tasks
{
get { return (TasksElement)base["tasks"]; }
}
}
}
tasks子節
namespace PO.Component.Configuration
{
public sealed class TasksElement : ConfigurationElement
{
[ConfigurationProperty("modules")]
public ProviderSettingsCollection Modules
{
get { return (ProviderSettingsCollection)base["modules"]; }
}
[ConfigurationProperty("threads")]
public ThreadSettingsCollection Threads
{
get { return (ThreadSettingsCollection)base["threads"]; }
}
}
}
tasks子節中的threads節
namespace PO.Component.Configuration
{
[ConfigurationCollection(typeof(TaskSettingsCollection))]
public sealed class ThreadSettingsCollection : ConfigurationElementCollection
{
public TaskSettingsCollection this[int index]
{
get
{
return (TaskSettingsCollection)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
public new TaskSettingsCollection this[string key]
{
get
{
return (TaskSettingsCollection)base.BaseGet(key);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new TaskSettingsCollection();
}
protected override object GetElementKey(ConfigurationElement element)
{
return string.Empty;
}
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.BasicMap;
}
}
protected override string ElementName
{
get
{
return "thread";
}
}
}
}
threads節中的thread節
namespace PO.Component.Configuration
{
[ConfigurationCollection(typeof(ProviderSettings))]
public sealed class TaskSettingsCollection : ConfigurationElementCollection
{
[ConfigurationProperty("minutes", IsRequired = false, DefaultValue = 15)]
public int Minutes
{
get { return (int)base["minutes"]; }
set { base["minutes"] = value; }
}
public ProviderSettings this[int index]
{
get
{
return (ProviderSettings)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
public new ProviderSettings this[string key]
{
get
{
return (ProviderSettings)base.BaseGet(key);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new ProviderSettings();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ProviderSettings)element).Name;
}
}
}