<optgroup id="kjtai"><li id="kjtai"><source id="kjtai"></source></li></optgroup><acronym id="kjtai"><sup id="kjtai"></sup></acronym>

    <legend id="kjtai"><i id="kjtai"></i></legend>
    <span id="kjtai"><sup id="kjtai"></sup></span>
    <track id="kjtai"></track>
    1. 通过创建动态类型 动态构建Expression Select表达式来控制Property可见性

      通过创建动态类型 动态构建Expression Select表达式来控制Property可见性

      项目中经常遇到的一个场景,根据当前登录用户权限,仅返回权限内可见的内容。参考了很多开源框架,更多的是在ViewModel层面硬编码实现。这种方式太过繁琐,每个需要相应逻辑的地方都要写一遍。经过研究,笔者提供另外一种实现,目前已经应用到项目中。这里记录一下,也希望能给需要的人提供一个参考。

      1、定义用于Property可见性的属性PermissionAttribute

      PermissionAttribute.Permissions保存了被授权的权限列表(假设权限类型是string)。构造函数要求permissions不能为空,你可以选择不在Property上使用此属性(对所有权限可见),或者传递一个空数组(对所有权限隐藏)。

      ///<summary>
      /// 访问许可属性
      ///</summary>
      [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
      public class PermissionAttribute : Attribute
      {
          public readonly IEnumerable<string> Permissions;
          public PermissionAttribute([NotNull] params string[] permissions)
          {
              this.Permissions = permissions.Distinct();
          }
      }
      

      2、定义Entity,给个别Property添加PermissionAttribute属性来控制可见性

      Name属性的访问权限授权给3、4权限,Cities授权给1权限,Id属性对所有权限隐藏,Code属性对所有权限都是可见的。

      ///<summary>
      /// 省份实体
      ///</summary>
      [Table("Province")]
      public class Province
      {
          /// <summary>
          /// 自增主键
          /// </summary>
          [Key, Permission(new string[0])]
          public int Id { get; set; }
      
          /// <summary>
          /// 省份编码
          /// </summary>
          [StringLength(10)]
          public string Code { get; set; }
      
          /// <summary>
          /// 省份名称
          /// </summary>
          [StringLength(64), Permission("3", "4")]
          public string Name { get; set; }
          /// <summary>
          /// 城市列表
          /// </summary>
          [Permission("1")]
          public List<object> Cities { get; set; }
      }
      

      3、构建表达式

      ExpressionExtensions类提供了根据授权列表IEnumerable<string> permissions构建表达式的方法,并扩展一个SelectPermissionDynamic方法把sources映射为表达式返回的结果类型——动态构建的类型。

      public static class ExpressionExtensions
      {
          /// <summary>
          /// 根据权限动态查找
          /// </summary>
          /// <typeparam name="TSource"></typeparam>
          /// <param name="sources"></param>
          /// <param name="permissions"></param>
          /// <returns></returns>
          public static IQueryable<object> SelectPermissionDynamic<TSource>(this IQueryable<TSource> sources, IEnumerable<string> permissions)
          {
              var selector = BuildExpression<TSource>(permissions);
              return sources.Select(selector);
          }
      
          /// <summary>
          /// 构建表达式
          /// </summary>
          /// <param name="sources"></param>
          /// <param name="permissions"></param>
          /// <returns></returns>
          public static Expression<Func<TSource, object>> BuildExpression<TSource>(IEnumerable<string> permissions)
          {
              Type sourceType = typeof(TSource);
              Dictionary<string, PropertyInfo> sourceProperties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(prop =>
              {
                  if (!prop.CanRead) { return false; }
                  var perms = prop.GetCustomAttribute<PermissionAttribute>();
                  return (perms == null || perms.Permissions.Intersect(permissions).Any());
              }).ToDictionary(p => p.Name, p => p);
      
              Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);
      
              ParameterExpression sourceItem = Expression.Parameter(sourceType, "t");
              IEnumerable<MemberBinding> bindings = dynamicType.GetRuntimeProperties().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();
      
              return Expression.Lambda<Func<TSource, object>>(Expression.MemberInit(
                  Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);
          }
      }
      

      上述代码片段调用了LinqRuntimeTypeBuilder.GetDynamicType方法构建动态类型,下面给出LinqRuntimeTypeBuilder的源码。

      public static class LinqRuntimeTypeBuilder
      {
          private static readonly AssemblyName AssemblyName = new AssemblyName() { Name = "LinqRuntimeTypes4iTheoChan" };
          private static readonly ModuleBuilder ModuleBuilder;
          private static readonly Dictionary<string, Type> BuiltTypes = new Dictionary<string, Type>();
      
          static LinqRuntimeTypeBuilder()
          {
              ModuleBuilder = AssemblyBuilder.DefineDynamicAssembly(AssemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(AssemblyName.Name);
          }
      
          private static string GetTypeKey(Dictionary<string, Type> properties)
          {
              //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter
              string key = string.Empty;
              foreach (var prop in properties)
                  key += prop.Key + ";" + prop.Value.Name + ";";
      
              return key;
          }
      
          private const MethodAttributes RuntimeGetSetAttrs = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
      
          public static Type BuildDynamicType([NotNull] Dictionary<string, Type> properties)
          {
              if (null == properties)
                  throw new ArgumentNullException(nameof(properties));
              if (0 == properties.Count)
                  throw new ArgumentOutOfRangeException(nameof(properties), "fields must have at least 1 property definition");
      
              try
              {
                  // Acquires an exclusive lock on the specified object.
                  Monitor.Enter(BuiltTypes);
                  string className = GetTypeKey(properties);
      
                  if (BuiltTypes.ContainsKey(className))
                      return BuiltTypes[className];
      
                  TypeBuilder typeBdr = ModuleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);
      
                  foreach (var prop in properties)
                  {
                      var propertyBdr = typeBdr.DefineProperty(name: prop.Key, attributes: PropertyAttributes.None, returnType: prop.Value, parameterTypes: null);
                      var fieldBdr = typeBdr.DefineField("itheofield_" + prop.Key, prop.Value, FieldAttributes.Private);
      
                      MethodBuilder getMethodBdr = typeBdr.DefineMethod("get_" + prop.Key, RuntimeGetSetAttrs, prop.Value, Type.EmptyTypes);
                      ILGenerator getIL = getMethodBdr.GetILGenerator();
                      getIL.Emit(OpCodes.Ldarg_0);
                      getIL.Emit(OpCodes.Ldfld, fieldBdr);
                      getIL.Emit(OpCodes.Ret);
      
                      MethodBuilder setMethodBdr = typeBdr.DefineMethod("set_" + prop.Key, RuntimeGetSetAttrs, null, new Type[] { prop.Value });
                      ILGenerator setIL = setMethodBdr.GetILGenerator();
                      setIL.Emit(OpCodes.Ldarg_0);
                      setIL.Emit(OpCodes.Ldarg_1);
                      setIL.Emit(OpCodes.Stfld, fieldBdr);
                      setIL.Emit(OpCodes.Ret);
      
                      propertyBdr.SetGetMethod(getMethodBdr);
                      propertyBdr.SetSetMethod(setMethodBdr);
                  }
      
                  BuiltTypes[className] = typeBdr.CreateType();
      
                  return BuiltTypes[className];
              }
              catch
              {
                  throw;
              }
              finally
              {
                  Monitor.Exit(BuiltTypes);
              }
          }
      
          private static string GetTypeKey(IEnumerable<PropertyInfo> properties)
          {
              return GetTypeKey(properties.ToDictionary(f => f.Name, f => f.PropertyType));
          }
      
          public static Type GetDynamicType(IEnumerable<PropertyInfo> properties)
          {
              return BuildDynamicType(properties.ToDictionary(f => f.Name, f => f.PropertyType));
          }
      }
      
      

      4、测试调用

      Controller中增加一个Action,查询DBContext.Provinces,并用上面扩展的SelectPermissionDynamic方法映射到动态类型返回当前用户权限范围内可见的内容。代码片段如下:

      [HttpGet, Route(nameof(Visibility))]
      public IActionResult Visibility(string id)
      {
          var querable = _dbContext.Provinces.SelectPermissionDynamic(id.Split(',')).Take(2);
          return Json(querable.ToList());
      }
      

      测试case
      访问/Test/Visibility?id=2,3,预期返回CodeName属性;
      访问/Test/Visibility?id=8,9,预期返回Code属性;
      如下图所示,返回符合预期,测试通过!
      测试通过!

      参考文档:
      [1] https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.assemblybuilder.definedynamicassembly?view=net-5.0
      [2] https://stackoverflow.com/questions/606104/how-to-create-linq-expression-tree-to-select-an-anonymous-type

      原文:http://www.cbaiibvip.com/itheo/p/14358495.html

      作者:Theo·Chan
      版权:本文版权归作者和博客园共有
      转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
      posted @ 2021-02-01 19:02  Theo·Chan  阅读(91)  评论(2编辑  收藏
      手机购彩 心机婊的说说 | 潮州话骂人经典的100句 | 即兴rap说唱歌词 | 大连话段子 | 为什么微笑表情是骂人 | 眼睛猴 | 美惊现神秘飞行物 | 天津日报大厦俯视图 | 四川话撩妹最经典句子 | 骂人的最高境界的句子 | 歹毒 | 天蛾人图片 | 你个锤子是什么意思 | 数学爱情表白公式520 | 希勒湖为什么是粉色的 | 非洲象人族阴经真图片 | 海市蜃楼为什么会死人 | 为什么不能对猫头鹰笑 | 道教神仙排位图108 | 放大细胞十亿倍是宇宙 |