通用弹出选择页面业务过滤示例
背景
目前系统中,使用较多的通用选择包括:人员选择,品类选择,供应商选择等。采用的模式,基本上是点击按钮或者不可写的文本框。类似图:
其基本实现,主要是通过调用dialog加载人员选择/品类选择/供应商选择页面。
缺点分析
采用此方法可以满足大多数的应用场景,但是如果当对弹出里的数据进行初始过滤就会变得很麻烦,拿上图弹出页面“人员选择”举例:如果有个按钮,它的功能是弹出人员选择页面,但是它想要的数据源不是默认的数据源,现有页面的数据源是所有的人员,虽然提供了部分查询语句进行过滤,但是一旦业务需求只展示内部用户或者只显示某个特殊的人员,再或者这个页面是外部和内部都使用的页面,外面人员使用时看不到所有的用户,那么此页面会变得不使用。当然也有人说我们可以在取数据加上这些逻辑就可以了呀。但是随着需求越来越多,程序就会变成很难维护,并且臃肿不堪。更有甚至,光人员选择页面需要提供多个页面,或者多个数据源来维护。
解决办法
图解:
- 创建一个获取所有用户的方法
- 创建一个补充方法
- 取交集
要求:1和3是不可以变动的,不然就少了通用性,2是可以随时变动,2的变动会影响最终的结果
代码实现:
获取用户的代码
/// <summary> /// 获取用户 /// </summary> /// <param name="conditon"></param> /// <returns></returns> public string GetUserList(SelectUserParamDto conditon) { using (var dbContext = new Context()) { var users = (from j in dbContext.T_User where j.IsAdmin == conditon.IsAdmin && j.IsSupplier == conditon.IsSupplier select new SelectUserDto { UserID = c.UserID, UserCode = c.UserCode, Name = c.Name, Mail = c.Mail, OrgName = IsCN ? b.OrgCName : b.OrgEName, PositionName = IsCN ? b.PositionCName : b.PositionEName, CreateTime = c.CreateTime, EnableDes = c.Enable == true ? Resource.PMOrg.PMOrg.Enable : Resource.PMOrg.PMOrg.Disabled }); if (!string.IsNullOrEmpty(conditon.Method)) { var otherMethod = Call(dbContext, conditon); //关联新的人员 users = from a in users join b in otherMethod on a.UserID equals b.UserId select a; } return users } }
2.增加补充方法
public class CommonForCall { /// <summary> /// 获取跟供应商相关的发布人 /// </summary> /// <param name="dbContext"></param> /// <param name="conditon"></param> /// <returns></returns> public IQueryable<QueryResultDto> GetUsers(SMS_V40Context dbContext, SelectUserParamDto conditon) { if (conditon.CurrentUser.IsSupplier == true) { var currentUser = dbContext.T_User.FirstOrDefault(j => j.UserID == conditon.CurrentUser.UserID); if (currentUser == null) throw new Exception("非法用户"); var info = from a in dbContext.T_SC_Scar join b in dbContext.T_PM_User on a.Publisher equals b.UserID where a.SupplierId == currentUser.SupplierID select new QueryResultDto { UserId = b.UserID, UserCode = b.UserCode }; return info; } else { var info = from a in dbContext.T_PM_User.Where(j => j.IsSupplier != true) select new QueryResultDto { UserId = a.UserID, UserCode = a.UserCode }; return info; } } }
3.通用方法调用
public class CommonCall { /// <summary> /// 动态调用传递的方法 /// </summary> /// <param name="dbContext"></param> /// <param name="condition"></param> /// <returns></returns> public static IQueryable<T1> Call<T1>(SMS_V40Context dbContext, ConditionDto condition) { var instance = new CommonForCall(); var instType = instance.GetType(); var dynamicMethod = new DynamicMethod("", typeof(IQueryable<T1>), new Type[] { instType, typeof(SMS_V40Context), typeof(ConditionDto) }, true); var stringMethod = instType.GetMethod(condition.Method, new Type[] { typeof(SMS_V40Context), typeof(ConditionDto) }); if (stringMethod == null) throw new Exception("未发现需要调用的方法方法"); var ilGen = dynamicMethod.GetILGenerator();//IL生成器 //压参数到堆栈上 ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Ldarg_2); //调用方法 ilGen.Emit(OpCodes.Callvirt, stringMethod); ilGen.Emit(OpCodes.Ret);//结束并返回值 //生成委托 var gan = (Func<SMS_V40Context, ConditionDto, IQueryable<T1>>)dynamicMethod.CreateDelegate(typeof(Func<SMS_V40Context, ConditionDto, IQueryable<T1>>), instance); //调用委托返回结果 return gan(dbContext, condition); } }
4.前台调用页面的时候需要传递一个Method的参数,用于告诉后端方法,此次调用的补充方法是哪个。所有的补充方法放入类CommonForCall中。
以上是整个实现,每次需要有特殊业务的时候,只需要在CommonForCall中创建一个特殊业务的方法,然后前台的Method参数置为创建的特殊业务方法名即可