ASP.NET MVC 开源项目Kigg解读(1)
Kigg是一个很好的ASP.NET MVC范例项目,本着研究的目的,对Kigg进行解读。
Kigg介绍: KiGG 是一个微软技术支持部门开发的Web 2.0 风格的社会新闻软件,采用如下的开发组件:
可以从http://kigg.codeplex.com/ 下载全部源代码 示例站点: KiGG v2.6 Beta 一、启动篇 就如一个操作系统,开机时需要boot,于是kigg也从boot开始! 在Kigg.Core中,定义了IBootstrapperTask接口 public interface IBootstrapperTask { void Execute(); } 纵观Kigg的源代码,我们可以发现共有4个Boot Task,如下图所示: 这4个task分别是创建默认用户,注册Controller工厂,注册路由,和启动后台任务(Background Tasks) 怎么在系统启动的时候调用IBootstrapperTask?Kigg专门建立了一个静态类Bootstrapper: 1: public static class Bootstrapper 2: {
3: static Bootstrapper() 4: {
5: try 6: {
7: IoC.InitializeWith(new DependencyResolverFactory()); 8: }
9: catch (ArgumentException) 10: {
11: // Config file is Missing 12: }
13: }
14: ?
15: public static void Run() 16: {
17: IoC.ResolveAll<IBootstrapperTask>().ForEach(t => t.Execute());
18: }
19: }
在该类的静态构造函数里,进行的是IOC(这里用的是Unity)的初始化工作。同时,Bootstrapper类还有个Run方法,该方法调用IOC Resolve所有实现了IBootstrapperTask接口的任务,然后ForEach(一个扩展方法,遍历集合)每个任务并Execute。 于是,我们在Kigg的GlobalApplication里看到了华丽丽的Bootstrapper.Run(); 1: public class GlobalApplication : HttpApplication 2: {
3: public static void OnStart() 4: {
5: Bootstrapper.Run();
6: Log.Info("Application Started"); 7: }
8: }
二、后台任务 其实分析IBootstrapperTask的初衷是对Kigg的后台任务(BackgroundTask)感兴趣: 1: public interface IBackgroundTask 2: {
3: bool IsRunning 4: {
5: get;
6: }
7: ?
8: void Start(); 9: ?
10: void Stop(); 11: }
在Kigg中,共有5种后台任务: 这些后台任务的开启,是在实现了IBootstrapperTask接口的StartBackgroundTasks中开启的: 1: public class StartBackgroundTasks : IBootstrapperTask 2: {
3: private readonly IBackgroundTask[] _tasks; 4: ?
5: public StartBackgroundTasks(IBackgroundTask[] tasks) 6: {
7: Check.Argument.IsNotEmpty(tasks,"tasks"); 8: ?
9: _tasks = tasks;
10: }
11: ?
12: public void Execute() 13: {
14: _tasks.ForEach(t => t.Start());
15: }
16: }
StartBackgroundTasks 类的构造函数参数是通过IOC搞定的(后面会单独介绍IOC) 三、事件聚合器IEventAggregator 在分析BackgroundTask的代码时,发现所有的BackgroundTask都继承于BaseBackgroundTask: 在查看BaseBackgroundTask时,发现了令人惊喜的东西——IEventAggregator EventAggregator是何许玩意呢?按字面意思,事件聚合器?姑且这么叫吧!这个接口只有一个方法: public interface IEventAggregator { TEventType GetEvent<TEventType>() where TEventType : BaseEvent; } 作用是获取一个继承BaseEvent的事件(Event). 为了弄清楚EventAggregator到底有什么用,我们先来看看与BaseEvent相关的几个类: 首先是一个事情订阅接口,包含一个订阅Token,一个获取可执行函数的方法。 1: public interface IEventSubscription 2: {
3: SubscriptionToken SubscriptionToken
4: {
5: get;
6: set;
7: }
8: ?
9: Action<object[]> GetExecutionStrategy(); 10: }
订阅Token,实现了IEquatable接口,可以进行比较,这里的Token没什么特别的作用,仅仅用来标识一个订阅,这样在移除订阅的时候通过Token能方便的找到并移除 1: public class SubscriptionToken : IEquatable<SubscriptionToken> 2: {
3: private readonly Guid _token = Guid.NewGuid(); 4: ?
5: [DebuggerStepThrough]
6: public bool Equals(SubscriptionToken other) 7: {
8: return (other != null) && Equals(_token,other._token); 9: }
10: ?
11: [DebuggerStepThrough]
12: public override bool Equals(object obj) 13: {
14: return ReferenceEquals(this,obj) || Equals(obj as SubscriptionToken); 15: }
16: ?
17: [DebuggerStepThrough]
18: public override int GetHashCode() 19: {
20: return _token.GetHashCode(); 21: }
22: ?
23: [DebuggerStepThrough]
24: public override string ToString() 25: {
26: return _token.ToString(); 27: }
28: }
再来看最为关键的BaseEvent 1: /// <summary> 2: /// 事件基类 3: /// </summary> 4: public abstract class BaseEvent 5: {
6: private readonly List<IEventSubscription> _subscriptions = new List<IEventSubscription>(); 7: ?
8: ?
9: /// <summary> 10: /// 订阅者 11: /// </summary> 12: protected ICollection<IEventSubscription> Subscriptions 13: {
14: [DebuggerStepThrough]
15: get
16: {
17: return _subscriptions; 18: }
19: }
20: ?
21: /// <summary> 22: /// 订阅 23: /// </summary> 24: /// <param name="eventSubscription"></param> 25: /// <returns></returns> 26: protected virtual SubscriptionToken Subscribe(IEventSubscription eventSubscription) 27: {
28: eventSubscription.SubscriptionToken = new SubscriptionToken(); 29: ?
30: lock (_subscriptions) 31: {
32: _subscriptions.Add(eventSubscription);
33: }
34: ?
35: return eventSubscription.SubscriptionToken; 36: }
37: ?
38: protected virtual void Publish(params object[] arguments) 39: {
40: List<Action<object[]>> executionStrategies = PruneAndReturnStrategies(); 41: ?
42: foreach (var executionStrategy in executionStrategies) 43: {
44: executionStrategy(arguments);
45: }
46: }
47: ?
48: public virtual void Unsubscribe(SubscriptionToken token) 49: {
50: lock (_subscriptions) 51: {
52: IEventSubscription subscription = _subscriptions.FirstOrDefault(evt => evt.SubscriptionToken == token);
53: ?
54: if (subscription != null) 55: {
56: _subscriptions.Remove(subscription);
57: }
58: }
59: }
60: ?
61: public virtual bool Contains(SubscriptionToken token) 62: {
63: lock (_subscriptions) 64: {
65: IEventSubscription subscription = _subscriptions.FirstOrDefault(evt => evt.SubscriptionToken == token);
66: ?
67: return (subscription != null); 68: }
69: }
70: ?
71: private List<Action<object[]>> PruneAndReturnStrategies() 72: {
73: List<Action<object[]>> returnList = new List<Action<object[]>>(); 74: ?
75: lock (_subscriptions) 76: {
77: for (int i = _subscriptions.Count - 1; i >= 0; i--) 78: {
79: Action<object[]> subscriptionAction = _subscriptions[i].GetExecutionStrategy(); 80: ?
81: if (subscriptionAction == null) 82: {
83: _subscriptions.RemoveAt(i);
84: }
85: else 86: {
87: returnList.Add(subscriptionAction);
88: }
89: }
90: }
91: ?
92: return returnList; 93: }
94: }
BaseEvent包含了Subscribe,Publish这两个对事件进行处理的关键方法: Subscribe时会添加一个IEventSubscription,Publish时会执行所有IEventSubscription中的方法。 回过头来,再来看BaseBackgroundTask: 1: public abstract class BaseBackgroundTask : IBackgroundTask 2: {
3: private readonly IEventAggregator _eventAggregator; 4: ?
5: protected BaseBackgroundTask(IEventAggregator eventAggregator) 6: {
7: Check.Argument.IsNotNull(eventAggregator,"eventAggregator"); 8: ?
9: _eventAggregator = eventAggregator;
10: }
11: ?
12: public bool IsRunning 13: {
14: get;
15: private set; 16: }
17: ?
18: protected internal IEventAggregator EventAggregator 19: {
20: [DebuggerStepThrough]
21: get
22: {
23: return _eventAggregator; 24: }
25: }
26: ?
27: public void Start() 28: {
29: OnStart();
30: IsRunning = true; 31: }
32: ?
33: public void Stop() 34: {
35: OnStop();
36: IsRunning = false; 37: }
38: ?
39: protected abstract void OnStart(); 40: ?
41: protected abstract void OnStop(); 42: ?
43: protected internal SubscriptionToken Subscribe<TEvent,TEventArgs>(Action<TEventArgs> action) where TEvent : BaseEvent<TEventArgs> where TEventArgs : class 44: {
45: return _eventAggregator.GetEvent<TEvent>().Subscribe(action,true); 46: }
47: ?
48: protected internal void Unsubscribe<TEvent>(SubscriptionToken token) where TEvent : BaseEvent 49: {
50: _eventAggregator.GetEvent<TEvent>().Unsubscribe(token);
51: }
52: }
注意下 Subscribe和Unsubscribe方法,这两个方法通过eventAggregator获取特定的TEvent,实现事件的定订阅和解除订阅。 然后再来看一个具体的Task,比如PingServer: PingServer继承BaseBackgroundTask ,需要实现OnStart和OnStop,PingServer的作用是在发布一篇story的时候通知ping服务器,我更新了,你可以派你的爬虫过来了……因此,在OnStart方法中,Subscribe了story提交事件--StorySubmitEvent,并指定用StorySubmitted方法来处理这个事件,因此StorySubmitted方法只需要实现发送ping的代码就可以了。 1: ?
2: protected override void OnStart() 3: {
4: if (!IsRunning) 5: {
6: _storySubmitToken = Subscribe<StorySubmitEvent,StorySubmitEventArgs>(StorySubmitted);
7: _storyApproveToken = Subscribe<StoryApproveEvent,StoryApproveEventArgs>(StoryApproved);
8: }
9: }
10: ?
11: protected override void OnStop() 12: {
13: if (IsRunning) 14: {
15: Unsubscribe<StorySubmitEvent>(_storySubmitToken);
16: Unsubscribe<StoryApproveEvent>(_storyApproveToken);
17: }
18: }
19: ?
20: internal void StorySubmitted(StorySubmitEventArgs eventArgs) 21: {
22: SendPing();
23: }
24: ?
25: internal void StoryApproved(StoryApproveEventArgs eventArgs) 26: {
27: SendPing();
28: }
光有订阅是不行的,同学们,还需要有发布才行!关于发布,看看StoryService就可以了,在这个service的Create函数中有这么一段代码: 1: _eventAggregator.GetEvent<StorySubmitEvent>().Publish(new StorySubmitEventArgs(story,detailUrl)); ? OMG,这就是发布吗? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc-3 – Url.RouteUrl返回空
- asp.net-mvc – @ Html.ValidationSummary是否适用于MVC3或
- VS2017中建立ASP.NET MVC 4.0项目
- 给ASP.NET Core Web发布包做减法
- asp.net – 有没有一种简单的方法可以向DataBound DropDown
- asp.net-mvc-4 – 自定义错误在我的MVC 4应用程序中无效
- asp.net-mvc – 如何使用Moq模拟存储库Single(Expression>
- asp.net – Web配置转换不工作
- 在ASP.NET网站中使用SQL Server Compact Edition
- ASP.NET从URL获取物理文件路径
- 在Asp.Net中使用Office365 SMTP时出错
- asp.net下经典数据库记录分页代码
- 如何在ASP.NET中使用多个授权方案发布相应的承载
- 在ASP.NET Web窗体中收集输入值的推荐方法是什么
- ASP.NET EF错误:11007未映射实体类型
- asp.net-mvc – 在ASP.NET MVC3中有一个无会话控
- 如何在ASP.NET Core WebAPI中使用Newtonsoft:Js
- asp.net-web-api – 如何从WEB API检索邮件?
- asp.net-mvc-3 – ASP.Net MVC 3 JSON模型绑定和
- asp.net-mvc-2 – 来自下拉列表的id值的Html.Act