最近用到了App_Code文件夹,想要实现动态编译文件的方式,即替换文件夹中的类文件从而达到实时修改代码的效果,类似web.config,网上查到的资料基本都是把文件夹中的类文件修改属性为"编译",这跟我想要的效果不一样,但是不这么做的话在VS中无法调用,想来想去只能使用反射+Expression+缓存的方式来实现了.
研究了几个小时后搞了一个App_Code帮助类
假设App_Code文件夹中有这么一个类
namespace WebTest.App_Code { public static class TestClass { public const string TestConst = "testConst"; public readonly static string TestStaticField = "testStaticField"; public static string TestStaticProperty { get { return "testStaticProperty"; } } public static string TestStaticMethod(string value) { return value; } public static string TestStaticMethod(string value1, string value2) { return value1 + value2; } } }
我想要获取以下内容:
1.常量TestConst的值
2.静态字段TestStaticField的值
3.静态属性TestStaticProperty的值
4.静态方法TestStaticMethod(string value)的调用结果
5.静态方法TestStaticMethod(string value1,string value2)的调用结果
好了,在VS中是无法直接使用的,因为类文件属性未修改为"编译",但是这个类会被动态编译成一个程序集.
App_Code生成的动态程序集可以通过 System.Web.Compilation.BuildManager.CodeAssemblies 拿到(PreApplicationStart阶段无法获取)
public static IEnumerable<Assembly> GetApp_CodeAssemblies() { if (BuildManager.CodeAssemblies != null) { foreach (object item in BuildManager.CodeAssemblies) { Assembly assembly; try { assembly = (Assembly)item; } catch { continue; } yield return assembly; } } }
首先我们需要获取类型的Type对象,为了提高性能,代码中使用字典来缓存数据
static readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>(); static readonly ConcurrentDictionary<Tuple<Type, string>, object> _constMap = new ConcurrentDictionary<Tuple<Type, string>, object>(); static readonly ConcurrentDictionary<Tuple<Type, string>, Func<object>> _staticFieldFactoryMap = new ConcurrentDictionary<Tuple<Type, string>, Func<object>>(); static readonly ConcurrentDictionary<Tuple<Type, string>, Func<object>> _staticPropertyFactoryMap = new ConcurrentDictionary<Tuple<Type, string>, Func<object>>(); static readonly ConcurrentDictionary<Tuple<Type, string, string>, Delegate> _staticMethodFactoryMap = new ConcurrentDictionary<Tuple<Type, string, string>, Delegate>();
为了使用方便,扩展了微软的ConcurrentDictionary类
namespace System.Web { internal class ConcurrentDictionary<TKey, TValue> : System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> { public TValue Get(TKey key, Func<TValue> factory) { TValue value; if (!this.TryGetValue(key, out value)) { value = factory(); this.TryAdd(key, value); } return value; } } }
#region Types public static Type[] GetTypes(string fullName) { return GetTypesInternal(fullName).ToArray(); } public static Type GetType(string fullName) { return _typeMap.Get(fullName, () => GetTypesInternal(fullName).FirstOrDefault()); } static IEnumerable<Type> GetTypesInternal(string fullName) { foreach (var assembly in Tools.GetApp_CodeAssemblies()) { var type = assembly.GetTypes().FirstOrDefault(o => o.FullName == fullName); if (type != null) { yield return type; } } } #endregion
好了,拿到类型了,按顺序解决上面的5个问题
1.常量值
/// <summary> /// 获取常量值 /// </summary> /// <param name="fullTypeName"></param> /// <param name="constName"></param> /// <returns></returns> public static object GetConstValue(string fullTypeName, string constName) { var type = GetType(fullTypeName); if (type == null) { return null; } return GetConstValue(type, constName); } /// <summary> /// 获取常量值 /// </summary> /// <param name="type"></param> /// <param name="constName"></param> /// <returns></returns> public static object GetConstValue(Type type, string constName) { if (type == null) { throw new ArgumentNullException("type"); } return _constMap.Get(new Tuple<Type, string>(type, constName), () => { var field = type.GetField(constName); if (field == null) { return null; } if ((field.Attributes & System.Reflection.FieldAttributes.Literal) != 0) { return field.GetValue(null); } return null; }); }
2.静态字段值
/// <summary> /// 获取静态字段值 /// </summary> /// <param name="fullTypeName"></param> /// <param name="fieldName"></param> /// <returns></returns> public static object GetStaticFieldValue(string fullTypeName, string fieldName) { var type = GetType(fullTypeName); if (type == null) { return null; } return GetStaticFieldValue(type, fieldName); } /// <summary> /// 获取静态字段值 /// </summary> /// <param name="type"></param> /// <param name="fieldName"></param> /// <returns></returns> public static object GetStaticFieldValue(Type type, string fieldName) { if (type == null) { throw new ArgumentNullException("type"); } var factory = _staticFieldFactoryMap.Get(new Tuple<Type, string>(type, fieldName), () => { var field = type.GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); if (field == null) { return null; } var fieldExpression = Expression.Field(null, field); return Expression.Lambda<Func<object>>(fieldExpression).Compile(); }); return factory == null ? null : factory(); }
3.静态属性值
/// <summary> /// 获取静态属性值 /// </summary> /// <param name="fullTypeName"></param> /// <param name="propertyName"></param> /// <returns></returns> public static object GetStaticPropertyValue(string fullTypeName, string propertyName) { var type = GetType(fullTypeName); if (type == null) { return null; } return GetStaticPropertyValue(type, propertyName); } /// <summary> /// 获取静态属性值 /// </summary> /// <param name="type"></param> /// <param name="propertyName"></param> /// <returns></returns> public static object GetStaticPropertyValue(Type type, string propertyName) { if (type == null) { throw new ArgumentNullException("type"); } var factory = _staticPropertyFactoryMap.Get(new Tuple<Type, string>(type, propertyName), () => { var property = type.GetProperty(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); if (property == null) { return null; } var propertyExpression = Expression.Property(null, property); return Expression.Lambda<Func<object>>(propertyExpression).Compile(); }); return factory == null ? null : factory(); }
4,5.方法返回值
/// <summary> /// 获取静态方法值 /// </summary> /// <param name="fullTypeName"></param> /// <param name="methodName"></param> /// <param name="args"></param> /// <returns></returns> public static object GetStaticMethodValue(string fullTypeName, string methodName, params dynamic[] args) { var type = GetType(fullTypeName); if (type == null) { return null; } return GetStaticMethodValue(type, methodName, args); } /// <summary> /// 获取静态方法值 /// </summary> /// <param name="type"></param> /// <param name="methodName"></param> /// <param name="args"></param> /// <returns></returns> public static object GetStaticMethodValue(Type type, string methodName, params dynamic[] args) { if (type == null) { throw new ArgumentNullException("type"); } var types = args.Select(o => (Type)o.GetType()).ToArray(); string argKey = Tools.GetValueFromIEnumerable(types.Select(o => o.FullName)); dynamic factory = _staticMethodFactoryMap.Get(new Tuple<Type, string, string>(type, methodName, argKey), () => { var method = type.GetMethod(methodName, types); if (method == null) { return null; } if (method.ReturnType == null) { return null; } var parameters = new ParameterExpression[args.Length]; for (int i = 0; i < args.Length; i++) { parameters[i] = Expression.Parameter(args[i].GetType(), "args" + i); } var methodExpression = Expression.Call(null, method, parameters); return Expression.Lambda(methodExpression, parameters).Compile(); }); if (factory == null) { return null; } switch (args.Length) { case 0: return factory(); case 1: return factory(args[0]); case 2: return factory(args[0], args[1]); case 3: return factory(args[0], args[1], args[2]); case 4: return factory(args[0], args[1], args[2], args[3]); case 5: return factory(args[0], args[1], args[2], args[3], args[4]); case 6: return factory(args[0], args[1], args[2], args[3], args[4], args[5]); case 7: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); case 8: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); case 9: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); case 10: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); case 11: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); case 12: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); case 13: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]); case 14: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]); case 15: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14]); case 16: return factory(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15]); default: throw new NotImplementedException("参数太多了..."); } }
好了,做个测试:
public class TestController : Controller { // GET: Test public ActionResult Index() { string testConst = App_CodeHelper.GetConstValue("WebTest.App_Code.TestClass", "TestConst") as string; string testStaticField = App_CodeHelper.GetStaticFieldValue("WebTest.App_Code.TestClass", "TestStaticField") as string; string testStaticProperty = App_CodeHelper.GetStaticPropertyValue("WebTest.App_Code.TestClass", "TestStaticProperty") as string; string testStaticMethod1 = App_CodeHelper.GetStaticMethodValue("WebTest.App_Code.TestClass", "TestStaticMethod", "我是value") as string; string testStaticMethod2 = App_CodeHelper.GetStaticMethodValue("WebTest.App_Code.TestClass", "TestStaticMethod", "我是value1", "我是value2") as string; return Json(new { testConst, testStaticField, testStaticProperty, testStaticMethod1, testStaticMethod2 }, JsonRequestBehavior.AllowGet); } }
得到结果:
{"testConst":"testConst","testStaticField":"testStaticField","testStaticProperty":"testStaticProperty","testStaticMethod1":"我是value","testStaticMethod2":"我是value1我是value2"}
现在我们用记事本打开TestClass类文件,修改代码保存
namespace WebTest.App_Code { public static class TestClass { public const string TestConst = "testConst1"; public readonly static string TestStaticField = "testStaticField1"; public static string TestStaticProperty { get { return "testStaticProperty1"; } } public static string TestStaticMethod(string value) { return value+"1"; } public static string TestStaticMethod(string value1, string value2) { return value1 +"+"+ value2; } } }
此时应用程序重启,再次访问,结果变成了:
{"testConst":"testConst1","testStaticField":"testStaticField1","testStaticProperty":"testStaticProperty1","testStaticMethod1":"我是value1","testStaticMethod2":"我是value1+我是value2"}
效果还是不错的,当然如果只是使用字段属性的话完全可以使用web.config,但是动态调用方法的话还是可以派上用场的!!!