惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Engineering at Meta
Engineering at Meta
T
Tenable Blog
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Secure Thoughts
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
H
Hacker News: Front Page
I
InfoQ
L
LangChain Blog
Security Latest
Security Latest
The Cloudflare Blog
Forbes - Security
Forbes - Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Stack Overflow Blog
Stack Overflow Blog
TaoSecurity Blog
TaoSecurity Blog
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
Scott Helme
Scott Helme
爱范儿
爱范儿
A
Arctic Wolf
F
Full Disclosure
酷 壳 – CoolShell
酷 壳 – CoolShell
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 最新话题
V2EX - 技术
V2EX - 技术
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
The Hacker News
The Hacker News
Hugging Face - Blog
Hugging Face - Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
Application and Cybersecurity Blog
Application and Cybersecurity Blog

博客园 - 阿福

TypeScript forms authentication failed the ticket supplied was invalid错误 (Windows Server 2008 + IIS 7.5) jQuery Mobile 1.1.1 RC1发布 HashSet<T> vs List<T> 不要用把无序GUID既作为主键又作为聚集索引 WCF Data Services 5.0 RTM发布 EF Power Tools Beta 2发布 Entity Framework 5性能方面的注意事项 jQuery Mobile 1.1.0 RC2发布 源码+幻灯片:学习HTML5/jQuery/ASP.NET MVC/EF Code First的绝佳资源Account at a Glance项目 使用Autofac在ASP.NET Web API上实现依赖注入 在Windows Azure上开发ASP.NET程序与在Windows Sever上有何不同 充分利用缓存来提高网站性能 ASP.NET MVC 4, ASP.NET Web API, ASP.NET Web Pages v2 (Razor)全部开源,并接受来自社区的贡献(contributions) EF5 beta2通过NuGet发布 Getting Started with HTML5 开发HTML5应用你需要了解的 WCF入门资源 jquery history plugin, url hash ASP.NET 2.0: 生成Excel报表 VS 2008 hot-fix终于出来了 Images; How to create an HTTP handler to dynamically resize images and change quality. ASP.NET 2.0: Add build-in paging feature to repeater/为repeater添加内置分页功能 Tip/Trick ASP.NET 2.0: DropDownList DataBind ASP.NET 2.0: Forms Authentication Across domains Ajax类库、框架、工具包完全列表 怀旧啊 Search Box ASP.NET 2.0: Site Maps Checking All CheckBoxes in a GridView Efficient Data Paging and Sorting with ASP.NET 2.0 and SQL 2005 JavaScript Calendar Cool MSDN ASP.NET 2.0 GridView Control Article Do I have to cry for you Google无法访问 扯淡 狗*! Atlas Control Toolkit and Source Code for the Build-in Asp.Net 2.0 Providers Great New Advanced Article on ASP.NET 2.0 Master Pages ASP.NET 2.0:通过SqlDataSource绑定数据到普通控件 通过客户端扩展实现固定GridView表头功能 不使用ISAPI或IIS wildcard实现不带扩展名URL的转向 VS 2005 Web Application Project & Atlas Control Toolkit & CSS Control Adapter ASP.NET 2.0 Language Swithcer and Theme Swicher 多语言转换和多样式主题转换 How to use virtual path providers to dynamically load and compile content from virtual paths in 
Run Tasks in an ASP.NET Application Domain
阿福 · 2010-01-01 · via 博客园 - 阿福

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;
        }


    }
}