前天刚发过文章介绍了KudyStudio.Web.Activating,今天再次作了修改,并提供源码给大家。
KudyStudio.Web.Activating下提供了两个属性分别是ActivationAttribute、ActivationMethodAttribute,利用它们可以灵活地随时在你的程序集中注册一个或多个Appilcation_Start()前/后触发和Appilcation_End()前触发的处理事件。 KudyStudio文章目录
下载源码KudyStudio.Web.Activating.rar(.Net4.0)
可触发的函数目标定义如下:
/// <summary>
/// Specifies the targets to use for invoking activation methods.
/// </summary>
[Serializable]
public enum ActivationMethodTarget
{
/// <summary>
/// Provides expanded support for ASP.NET application pre-start.
/// </summary>
PreApplicationStart = 0x0,
/// <summary>
/// Provides expanded support for ASP.NET application post-start.
/// </summary>
PostApplicationStart,
/// <summary>
/// Provides expanded support for before ASP.NET application shutdown.
/// </summary>
PreApplicationEnd
}
先介绍ActivationMethodAttribute,这个属性允许你以静态方法方式来向程序集注册一个或多个触发函数,并指定触发函数的目标与执行顺序,它的定义如下:
/// <summary>
/// Provides expanded support for application activation.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public sealed class ActivationMethodAttribute : OrderableAttribute
{
#region Constructors
/// <summary>
/// Initializes a new instance of the ActivationMethodAttribute class.
/// </summary>
/// <param name="type">An object that describes the type of the activation method.</param>
/// <param name="methodName">An empty parameter signature that has no return value.</param>
public ActivationMethodAttribute(Type type, string methodName)
:this(type, methodName, ActivationMethodTarget.PreApplicationStart)
{
}
/// <summary>
/// Initializes a new instance of the ActivationMethodAttribute class.
/// </summary>
/// <param name="type">An object that describes the type of the activation method.</param>
/// <param name="methodName">An empty parameter signature that has no return value.</param>
/// <param name="methodTarget">The method target for the associated activation method</param>
public ActivationMethodAttribute(Type type, string methodName, ActivationMethodTarget methodTarget)
{
type.ThrowsIfNull("type");
methodName.ThrowsIfNullOrEmpty("methodName");
if (!Enum.IsDefined(typeof(ActivationMethodTarget), methodTarget))
{
throw new ArgumentException("The methodTarget is undefined.");
}
this.Type = type;
this.MethodName = methodName;
this.MethodTarget = methodTarget;
}
#endregion
#region Properties
/// <summary>
/// Gets the type that is returned by the associated activation method.
/// </summary>
public Type Type
{
get;
private set;
}
/// <summary>
/// Gets the associated activation method.
/// </summary>
public string MethodName
{
get;
private set;
}
/// <summary>
/// Gets or sets the method target for the associated activation method.
/// </summary>
public ActivationMethodTarget MethodTarget
{
get;
set;
}
#endregion
}
有了ActivationMethodAttribute属性其实可以满足基本需求了,但是ActivationAttribute属性能让你的代码组织更清晰,怎么说?因为注册触发函数时不需要传入函数名称,而且方便每个程式元件建立自己的实现类(实现IPreApplicationStart、IPostApplicationStart、IPreApplicationEnd中的一个或多个接口)而不和其它程式元件的注册事务混在一起。ActivationAttribute属性这个允许你以实现接口方式来向程序集注册一个或多个触发函数,并指定触发函数的执行顺序,定义如下:
/// <summary>
/// Provides expanded support for application activation(s).
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public sealed class ActivationAttribute : OrderableAttribute
{
#region Constructors
/// <summary>
/// Initializes a new instance of the ActivationAttribute class.
/// </summary>
/// <param name="type">
/// An object that implements any activation interface (<see cref="IPreApplicationStart"/>,
/// <see cref="IPostApplicationStart"/>, <see cref="IPreApplicationEnd"/>).
/// </param>
public ActivationAttribute(Type type)
{
type.ThrowsIfNull("type");
this.Type = type;
}
#endregion
#region Properties
/// <summary>
/// Gets the type that implements any activation interface.
/// </summary>
public Type Type
{
get;
private set;
}
#endregion
}
重点的实现就是ActivationManager类的InvokeActivationMethods方法,代码如下:
private static void InvokeActivationMethods(ActivationMethodTarget target)
{
List<OrderableAttribute> allAttributes = new List<OrderableAttribute>();
// get all attributes
foreach (Assembly assembly in AppAssemblies.Concat(GetCodeAssemblies(target)))
{
allAttributes.AddRange(GetAttributes<ActivationMethodAttribute>(assembly).Cast<OrderableAttribute>());
allAttributes.AddRange(GetAttributes<ActivationAttribute>(assembly).Cast<OrderableAttribute>());
}
// handle all ordered attributes
foreach (OrderableAttribute attribute in allAttributes.OrderBy(attr => attr.Order))
{
// invokes static method activations
ActivationMethodAttribute methodAttribute = attribute as ActivationMethodAttribute;
if (methodAttribute != null && methodAttribute.MethodTarget == target)
{
MethodInfo method = methodAttribute.Type.GetMethod(methodAttribute.MethodName, StaticMethodBindingFlags);
if (method != null)
{
method.Invoke(null, null);
continue; // continue next target
}
// method not found
throw new ApplicationException(string.Format("The type {0} doesn't have a static method named {1}.", methodAttribute.Type, methodAttribute.MethodName));
}
// try next case:
// invokes activations of activation classes that implements any activation interface
ActivationAttribute classAttribute = attribute as ActivationAttribute;
if (classAttribute != null)
{
Type type = classAttribute.Type;
object activation = null;
if (IsValidActivationType(type))
{
try
{
activation = Activator.CreateInstance(type);
}
catch(Exception ex)
{
throw new ApplicationException(string.Format("Fail to create instance of type {0}.", methodAttribute.Type), ex);
}
}
else
{
// invalid activation class
throw new ApplicationException(string.Format("The type {0} is not a valid activation class.", type.FullName));
}
if (activation != null)
{
if (target == ActivationMethodTarget.PreApplicationStart && (activation is IPreApplicationStart))
{
(activation as IPreApplicationStart).PreApplicationStart();
}
else if (target == ActivationMethodTarget.PostApplicationStart && (activation is IPostApplicationStart))
{
(activation as IPostApplicationStart).PostApplicationStart();
}
else if (target == ActivationMethodTarget.PreApplicationEnd && (activation is IPreApplicationEnd))
{
(activation as IPreApplicationEnd).PreApplicationEnd();
}
}
}
}
}
.NET 4.0版本新增了一个 PreApplicationStartMethodAttribute 类只提供了一个开始的前触发支持,那怎么实现开始后触发和结束前触发呢?.Net4.0中提供了一个叫DynamicModuleUtility的类(位于Microsoft.Web.Infrastructure.dll程序集),里面只有一个方法RegisterModule(Type moduleType),利用它可以动态的添加HttpModule。WebActivator的作者就是巧妙的利用了动态注册HttpModule。在第一个处理触发事件的HttpModule初始化时触发PostApplicationStart,最后一个HttpModule销毁时触发PreApplicationEnd,核心原理就这么两点,比较简单,KudyStudio.Web.Activating里的源码并没有作优化,因为在应用程序周期只触发一次各个事件,没必要刻意的去代码加载速度什么的。另外提醒一下,WebActivator1.5版本里的触发顺序只在每个独立程序集有效(不知道是不是作者故意的),KudyStudio.Web.Activating的触发顺序是在整个应用程序的全部程序集都有效的(有效是指排序的范围),感兴趣的朋友请下载源码分析。
下载KudyStudio.Web.Activating.rar(.Net4.0)
在此再附上测试实例说明吧,ActivatingWeb网站项目中ActivatingTest.cs里写了触发函数的实现与注册,Global.asax是用于辅助测试的。运行网站的结果如下:
所有触发函数都按指定的顺序执行了,打开Web.config后点击保存,查看网站根目录下的ActivatingTest.txt内容为:
可以看到PreApplicationEnd触发函数也正常的按顺序执行了。
文章到此结束了,如果本文对你有帮助请点击推荐表示支持,谢谢,转载时请务必写明出处。