Kigg是一个很好的ASP.NET MVC范例项目,本着研究的目的,对Kigg进行解读。

  •  
    • ASP.NET MVC
    • Linq To SQL
    • MS Patterns & Practices – Enterprise Library (Logging & Caching)
    • MS Patterns & Practices - Unity
    • jQuery
    • xUnit.net
    • Moq
    • HtmlAgilityPack
    • DotNetOpenId
    • jQuery UI & Markitup
  • 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,如下图所示:

    clip_image001

    这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种后台任务:

    clip_image002

    这些后台任务的开启,是在实现了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:

    clip_image003

    在查看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,这就是发布吗?

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架