四、Attribute

Attribute分多种

Attribute称为特性,语法:特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。

1、FilterAttribute(过滤器)可以看出mvc 引用的是System.Web.Mvc,webapi 引用的是System.Web.Http.Filters ,不知道小伙伴们有看出来别的区别没有,对的,有的 ,虚方法传入类不同,这样导致传入构造与输出构造也将不同了。

ActionFilterAttribute过滤器-基于Web.MVC

自定义Filter需要继承ActionFilterAttribute抽象类:例如Action控制器用于登录时候的验证有四种:分别

public class FilterAttribute : ActionFilterAttribute
{
    public string Message { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        base.OnResultExecuting(filterContext);
        filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        base.OnResultExecuted(filterContext);
        filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
    }
} 

  然后在调用过滤器的时候,添加上该参数,Controller代码如下:

[Filter(Message="刘备")] //参数给上
public ActionResult Index()
{
    return View();
} 

另一种使用方式是进入Control执行下Action也执行一下

通过如果标签打到Controller上的话,TestFilterAttributeFilter将作用到Controller下的所有的Action。 

  默认情况下Action上打了某个自定义标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起作用了。
  补充:如果Action没有打上该标签,那么Controller上的标签便会被执行。

如果想让Action上的标签执行一次,然后Controller上的标签也执行一次,那么应该如何操作呢?

 我们只需在FilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可也就是让其成为可以多次执行的Action。

[AttributeUsage(AttributeTargets.All,AllowMultiple = true)]
    public class FilterAttribute : ActionFilterAttribute
    {
        public string Message { get; set; }
        ......

  

有时我们想有些公共的方法需要每个Action都执行,但是又不想再每一个Controller上都打上Action标签?答案就在Global.asax中。

如果是WebAPI则是WebApiConfig.cs中配置全局。

 例如:项目Session过滤

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebDemo
{
    public class LoginAttribute: ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var user = HttpContext.Current.Session["user"];
            if (user==null)
            {
                filterContext.HttpContext.Response.Write("<script>top.location.href = '/Account/Index'</script>");
            }
        }
    }
}

  使用方式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebDemo.Controllers
{
    [Login]
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
    }
}

ActionFilterAttribute过滤器-基于Web.Http.Filters

WebAPI 执行Action执行后执行的过滤器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http.Filters;

namespace WebApplication2.App_Start
{
    public class VLDHousingEnquiriesAttribute :ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            //抓取API控制器返回的值  注:该控制器必须是WebApi类型的控制器
            /*
            public string index()
            {
                return "请求成功!";
            }
            */
            var data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
            /*即data="请求成功!"*/

            if (data.ToString() != "请求成功!")
            {
                // 取得由 API 返回的状态代码
                var StatusCode = actionExecutedContext.ActionContext.Response.StatusCode;
                //请求是否成功
                var IsSuccess = actionExecutedContext.ActionContext.Response.IsSuccessStatusCode;
                //判断基本请求是经过action的状态标识
                //结果转为自定义消息格式
                var str = new { dsa = "更改action内容" };
                HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") };
                // 重新封装回传格式
                actionExecutedContext.Response = result;//过滤器内部会监听内容是否被改变
                return;
            }
            else
            {
                //这里不做任何更改
            }
        }
    }
}

 

  执行action之前执行的过滤器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace Web.App_Start
{
    public class VLDApiAttribute
    {
    }
    public class VLDHousingEnquiriesAttribute : ActionFilterAttribute //必须引用using System.Web.Http.Filters;
    {
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            /*var str = new { dsa = "dasdas" };
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") };
            actionContext.Response = result;*/ //如果不注释就不会执行action  因为你拿到过滤器的actionContext上下文 进行Response操作了。
        //var request = HttpContext.Current.Request;var s = request["apiId"];
            base.OnActionExecuting(actionContext);
        }


    }
}

  

Executing->Action->Executed

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Filters;
using WebApplication2.Controllers;

namespace WebApplication2.App_Start
{
    public class VLDHousingEnquiriesAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            /*var str = new { dsa = "dasdas" };
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") };
            actionContext.Response = result;*/ //上面将做实体验证,判断是否返回Response值对应将执行不执行Action和Executed(Executed是跟在Action后的,Action都不执行何来的Executed)
            base.OnActionExecuting(actionContext);
        }

        /* //Action是WebApi类型
        [VLDHousingEnquiriesAttribute]
        [HttpGet]
        public dynamic index()
        {
            return new { id = "3",name="5" };
        }
        */

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var response = actionExecutedContext.Response = actionExecutedContext.Response ?? new HttpResponseMessage();
            var data = response.Content.ReadAsAsync<object>().Result;

            if (data.ToString() == "请求成功!")//通过判断返回Response值封装请求  这里Data是json对象 返回都要转换 Json字符串传到前台
            {
                // 取得由 API 返回的状态代码
                var StatusCode = response.StatusCode;
                //请求是否成功
                var IsSuccess = response.IsSuccessStatusCode;
                //结果转为自定义消息格式
                var str = new { dsa = "更改action内容" };
                // 重新封装回传格式
                //HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str.ToString(), Encoding.GetEncoding("UTF-8"), "application/json") };
                //actionExecutedContext.Response = result;//过滤器内部会监听内容是否被改变
                //或者
                response.Content = new StringContent(Json.Encode(str), Encoding.UTF8, "application/json");//过滤器内部会监听内容是否被改变
                return;
            }
            else
            {
                //这里不做任何更改
            }

        }

    }
}

  

  

在OnActionExecuting中阻止后面Action的执行

 filterContext.Result = new HttpNotFoundResult();//阻止后续Action的执行  原理一样改变了 Result基于 mvc的过滤器。原由是:filterContext.Result只要不为空Action就会终止。直接响应请求。

带参数的自定义Filter

还是按照之前添加自定义过滤器的方法,添加一个自定义过滤器,只是里面多了一个属性,代码如下:

public class FilterAttribute : ActionFilterAttribute
    {
        public string Message { get; set; }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
            filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);
            filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
            filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
        }
    }

  

[Filter(Message="刘备")]  //参数给上
public ActionResult Index()
{
    return View();
}

  输出结果如下:

AuthorizeAttribute过滤器-基于Web.Http.Filters还是MVC 看你是MVC控制器还是WebAPI

 使用场景:1、未登录访问功能模块的时候 2、未登录获取分页数据  解决方式1、 父控制器继承处理 2、过滤器

1、重写Authorize有以下几个要点需注意:

1、HandleUnauthorizedRequest中基类方法已经将Response的状态设为”HttpStatusCode.Unauthorized(即401)“,重写时手请动改为”HttpStatusCode.Forbidden(即403)“,否则按401状态往下执行,就要被重定向到登录页-401错误又对应了Web.config中的

<authentication mode="Forms">

<forms loginUrl="~/" timeout="2880" />

</authentication>

2、webApi下的授权筛选attribute为System.Web.Http.AuthorizeAttribute,而Mvc下用的是System.Web.Mvc.AuthorizeAttribute。这里别继承错了,否则授权筛选attrbute拦截不了。

3、WebApi下Authorize.HandleUnauthorizedRequest的参数filterContext在此上下文里response还为空,需要手动创建。

/// <summary>
    /// 重写实现处理授权失败时返回json,避免跳转登录页
    /// </summary>
    public class ApiAuthorize : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
        {
            base.HandleUnauthorizedRequest(filterContext);

            var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
            response.StatusCode = HttpStatusCode.Forbidden;
            var content = new Result
            {
                success = false,
                errs = new[] { "服务端拒绝访问:你没有权限,或者掉线了" }
            };
            response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
        }
    }

  为防止上下文为null

var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
或者
if  (filterContext ==  null )  
{  
    throw new  ArgumentNullException( "filterContext" );   
}else  
{  
    string  path = context.HttpContext.Request.Path;  
    string  strUrl =  "/Account/LogOn?returnUrl={0}" ;        
    context.HttpContext.Response.Redirect( string .Format(strUrl, HttpUtility.UrlEncode(path)),  true );          
}  

  

 

chrome下查看返回状态:

 还有一种方式

跨域攻击---自然来路页面和目标页面不在同一个域下,所以直接判断来路域和当前自己的域就可以了。

比如内嵌Iframe或者Ajax调用的

代码顺序为:OnAuthorization-->AuthorizeCore-->HandleUnauthorizedRequest 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace Admin.MyAttribute
{
    [AttributeUsage(AttributeTargets.All, Inherited = true)]
    public class CheckAuthority : AuthorizeAttribute
    {
 
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            bool Pass = true;
            Uri UrlReferrer = httpContext.Request.UrlReferrer;//获取来路
            if (UrlReferrer == null)
            {
                httpContext.Response.StatusCode = 401;//无权限状态码
 
                Pass = false;
            }
            else 
            {
                 Uri ThisUrl = httpContext.Request.Url;//当前请求的URL
                if (UrlReferrer.Authority  != ThisUrl.Authority)
                {
                    httpContext.Response.StatusCode = 401;//无权限状态码
                    Pass = false;
                }
            }
 
 
            return Pass;
        }
 
       
 
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            base.HandleUnauthorizedRequest(filterContext);
            if (filterContext.HttpContext.Response.StatusCode == 401)
                filterContext.Result = new RedirectResult("/");
        }
 
       
 
      
    }
}

  AuthorizeAttribute的OnAuthorization方法内部调用了AuthorizeCore方法,这个方法是实现验证和授权逻辑的地方,如果这个方法返回true,

  表示授权成功,如果返回false, 表示授权失败, 会给上下文设置一个HttpUnauthorizedResult,这个ActionResult执行的结果是向浏览器返回。   

  关于 HttpUnauthorizedResult 测试 结果可以这样写 filterContext.Result = new HttpUnauthorizedResult("no authentication");

调用方法

 [MyAttribute.CheckAuthority]--先验证,通过验证才执行
        public ActionResult Index()
        {
           
            return View();
        }

 

当我们因不想每个控制器都添加授权属性的时候,而在全局配置文件内注册后,如果有些控制器的action方法不需要验证,则在action上添加属性[AllowAnonymous]

注意:

WebApi层接口效果示例

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Web.Common.Method;
using Web.Common.ReqResult;
using WebBLL;
using WebCommon; 

namespace Web.App_Start
{
    public class SignVerifyAttribute : ActionFilterAttribute
    {
        private static readonly int ApiMinutes = Convert.ToInt32(ConfigHelper.GetConfigValue("ApiMinutes")); //请求超时时间
        private static readonly IList<string> BaseParamKey = new List<string>()
        {
            "apiId"      //必填-用户标识
            , "timeStamp"//必填-时间戳[单位毫秒]
            , "nonce_Str"//必填-随机字符串
            , "sign"     //必填-签名
        };
        public override void OnActionExecuting(HttpActionContext actionContext)
        {

            try
            {
                var context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
                var param = new NameValueCollection();
                var method = context.Request.HttpMethod.ToUpperInvariant();
                param = method.Equals("GET", StringComparison.OrdinalIgnoreCase) ? context.Request.QueryString : context.Request.Form;
                #region 判断基础参数是否存在
                foreach (var item in BaseParamKey)
                {
                    if (!param.AllKeys.Any(x => x.Trim().Equals(item, StringComparison.OrdinalIgnoreCase)))
                    {
                        var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                        var content = new ReqResult<string>
                        {
                            code = Convert.ToString((int)SatausCode.CommonCode.incomplete),
                            message = item + SatausCode.GetDisplayDescription(SatausCode.CommonCode.incomplete),
                            result = null
                        };
                        response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                        return;
                    }
                }
                #endregion
                #region 验证时间戳格式&&请求时间
                var currentTimeStamp = OperData.GetTmeStamp(DateTime.Now);
                var getTimeStamp = param["timestamp"];
                long getIntTimeStamp = 0;
                if (!long.TryParse(getTimeStamp, out getIntTimeStamp))
                {
                    var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                    var content = new ReqResult<string>
                    {
                        code = Convert.ToString((int)SatausCode.CommonCode.timeStamp),
                        message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.timeStamp),
                        result = null
                    };
                    response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                    return;
                }
                //请求时间默认不能超过5分钟
                if (Convert.ToInt64(currentTimeStamp) - getIntTimeStamp > new TimeSpan(0, 0, ApiMinutes, 0).Ticks)
                {
                    var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                    var content = new ReqResult<string>
                    {
                        code = Convert.ToString((int)SatausCode.CommonCode.timeout),
                        message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.timeout),
                        result = null
                    };
                    response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                    return;
                }
                #endregion
                #region 验证ApiId用户是否有权限->生成key
                string ApiKeyUserKey = Convert.ToString(CacheHelper.Instance.Get(String.Format(param["apiId"], "ApiKeyUserKey")));//仅获取key
                if (ApiKeyUserKey == null)
                {
                    var ApiKeyInfo = Tbl_ApiUserManager.GetTbl_ApiUserAll().Where(x => x.ApiId == param["apiId"]).SingleOrDefault();
                    if (!string.IsNullOrWhiteSpace(ApiKeyInfo.ApiId))
                    {
                        CacheHelper.Instance.Add(String.Format(ApiKeyInfo.ApiId, "ApiKeyUserKey"), ApiKeyInfo.ApiKey, 5);
                    }
                    else
                    {
                        var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                        var content = new ReqResult<string>
                        {
                            code = Convert.ToString((int)SatausCode.CommonCode.power),
                            message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.power),
                            result = null
                        };
                        response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                        return;
                    }
                }
                #endregion
                #region 按key升序排序的待签名字符串并将所有参数加密
                //按key升序排序的待签名字符串
                var str = new StringBuilder();
                foreach (var key in param.AllKeys.OrderBy(x => x))
                {
                    if (key.Equals("sign", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }
                    str.AppendFormat("{0}={1}&", key, HttpUtility.UrlEncode(param.Get(key.Trim())));
                }
                str.AppendFormat("apikey={0}", ApiKeyUserKey);
                var calSignature = OperData.MD5Encrypt16(str.ToString());
                var signature = param.Get("sign").Trim();
                if (!calSignature.Equals(signature, StringComparison.OrdinalIgnoreCase))
                {
                    var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                    var content = new ReqResult<string>
                    {
                        code = Convert.ToString((int)SatausCode.CommonCode.sign),
                        message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.sign),
                        result = null
                    };
                    response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                    return;
                }
                #endregion
            }
            catch (Exception ex)
            {
                var response = actionContext.Response = actionContext.Response ?? new HttpResponseMessage();
                var content = new ReqResult<string>
                {
                    code = Convert.ToString((int)SatausCode.CommonCode.ex),
                    message = SatausCode.GetDisplayDescription(SatausCode.CommonCode.ex),
                    result = null
                };
                response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
                return;
            }
            base.OnActionExecuting(actionContext);
        }
    }
}

我的业务 写接口的 ing 验证接口合法性 action 处理事件 ed 记录日志的 但是出现了 ing 验证 ed 不会执行的(ed里面的日志不记录了)具体是

onactionexecuting里面不让他进入action里面对么?

但是 会导致 onactionexecuted 这个记录日志的过滤器 也不执行了,解决方案:onactionexecuting 创建日志  通过状态码Guid ,然后如果经过在action就在 onactionexecuted 更新日志,否在就在不进入action也就不会进入onactionexecuted 的onactionexecuting 过滤器return false 上一行去更新。

 (onactionexecuting、onactionexecuted 统简称ing和ed过滤器)

3、异常拦截器

1、创建拦截器并写入代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Mvc;

namespace Web.App_Start
{
    /// <summary>
    /// 异常拦截/实践通过拦截视图
    /// </summary>
    public class ExceptionFilterAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            if (filterContext.ExceptionHandled)
            {
                return;
            }
            Exception ex = filterContext.Exception;
            string message =
                 $"消息类型:{ex.GetType().Name}<br />" +
                 $"消息内容:{ex.Message}<br />" +
                 $"引发异常的方法:{ex.TargetSite}<br />" +
                 $"引发异常的对象:{ex.Source}<br />" +
                 $"异常目录:{filterContext.RouteData.GetRequiredString("controller")}<br />" +
                 $"异常方法:{filterContext.RouteData.GetRequiredString("action")}<br />" +
                 $"错误详细记录:{ex.StackTrace}";
            /*string message = string.Format("<br>消息类型:{0}<br>消息内容:{1}<br>引发异常的方法:{2}<br>引发异常的对象:{3}"
                      , filterContext.Exception.GetType().Name
                      , filterContext.Exception.Message
                      , filterContext.Exception.TargetSite
                      , filterContext.Exception.Source);
            string errorMessage = string.Format(
                "Error Message: {0}<br/>Error StackTrace: {1}",
                ex.message,
                ex.StackTrace
            );*/
            filterContext.HttpContext.Response.Write(message);
            filterContext.ExceptionHandled = true;   //不报告异常
        }
    }
}

  

2、添加全局

3、运行测试

 

特例:

2、系统过滤器 OutputCache过滤器

1、ActionResult结果进行缓存,概括地说,就是当你的请求参数没有发生变化时,直接从缓存中取结果,不会再走服务端的Action代码了。

[OutputCache(Duration = 300)]
public ActionResult Index(int? id,string name)
{
    return DateTime.Now.ToString();
}

  如果你重复调用Index() action(不断刷新当前页面), 那么你将看到当前的内容在Duration = 20秒内是不变的.

 

请求此Action的url可以为: person/Index?id=100&name="bird"。
当第一次请求这个地址时,会执行Index方法,并把结果缓存起来,且过期时间为300秒。
接下来,如果不改变id和name参数的前提下,在距离上次请求300秒内,再来请求这个地址,不会执行Index方法,直接从缓存中拿结果。
当id或者name的参数值发生变化时,发送请求时,会执行index方法,然后再缓存此结果。
[Output(Duration=300)],这种方法没有指明具体针对哪个参数来缓存,所以默认是针对所有参数,即任何一个参数值发生变化,都会缓存一份。
那么,如果,我想指定具体的参数,进行缓存该如何做呢?请看下一个方案。

2、通过对请求指定参数的ActionResult结果进行缓存

[OutputCache(Duration = 300,VaryByParam="id")]

  此种方式,指明了缓存是针对哪个参数来做的,即只有当id参数值发生变化的时候,才做缓存,其他机制同第一种。

2、FlagAttribute

 枚举优雅基本用法-在值String和int之间转换

  //需要的方法
   public string GetEnumDescription(Enum enumValue)
        {
            string str = enumValue.ToString();
            System.Reflection.FieldInfo field = enumValue.GetType().GetField(str);
            object[] objs = field.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
            if (objs == null || objs.Length == 0) return str;
            System.ComponentModel.DescriptionAttribute da = (System.ComponentModel.DescriptionAttribute)objs[0];
            return da.Description;
        }


   //定义枚举
     enum RoleType
    {
        [Description("子公司负责人")]
        ZMSManager = 1,
        [Description("集团人力")]
        JTHR = 2,
        [Description("考核人")]
        AssessPerson = 3,
        [Description("订立人")]
        MakePerson = 4,
        [Description("系统管理员")]
        SysManager = 5
    }

//调用方法
  string returnValue = GetEnumDescription((RoleType)(Enum.Parse(typeof(RoleType),"1"))); //返回值字符串:子公司负责人

  

Flags方式 

  如果对一个值可以包含多个,那么可以使用枚举,加上Flags

  1. 使用FlagsAttribute枚举才是对数字值执行按位运算 (AND、 OR 独占或) 的自定义属性。
  2. 在 2 的幂,即 1、 2、 4、 8 等中定义枚举常量。 这意味着不重叠中组合的枚举常量的各个标志。

在权限的管理中,常常会出现一个权限包含的现象。例如,有三种基本权限:职员A、职员B、职员C.在此基础上,有经理权限,它包括A和B两种权限;还有老板权限,包含A/B/C三种权限。

在代码中,我们可以用枚举来管理这些权限。

 class Program
    {
        [Flags]//表示可以将枚举对象视为位标志
        public enum EnumHasFlag
        {
            A = 1 << 0,
            B = 1 << 1,
            C = 1 << 2,  //定义权限枚举
            Manager = A | B, //通过Flags进行域计算得到的另一个枚举
            Boss = A | B | C,
        }//权限设计如果是常量的话通常用2的幂次方,防止值重复

        /*
        <<左移操作符,
        将第一个操作数向左移动第二个操作数指定的位数,空出的位置补0。
        左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8。
        如:
        x<<1= x*2
        x<<2= x*4
        x<<3= x*8
        x<<4= x*16  //至于为什么是 2/4/8?答:因为左移动计算就是将二进制的1向左边移动例如:左边二进制|右边常量  0001=1    左移则为0010=2 继续左移动 0100=4 位移计算方式常量则就是4,这是位移的计算方式。  
        */
        
        static void Main(string[] args)
        {
            var rightA = EnumHasFlag.Boss;
            var rightB = EnumHasFlag.Manager;
            
            if (rightA.HasFlag(EnumHasFlag.C)) Console.WriteLine("rightA can do this");
            if (rightB.HasFlag(EnumHasFlag.C)) Console.WriteLine("rightB can do this");          
        }
    }  

与数据库管理

using System;
using System.ComponentModel;
namespace myApp
{
class Program
   {
        [Flags]
        public enum Permission
        {
            create = 1,
            read = 2,
            update = 4,
            delete = 8,        
        }
        
       static void Main(string[] args)
       {
            Permission permission = Permission.create | Permission.read | Permission.update | Permission.delete;
            Console.WriteLine("1、枚举创建,并赋值……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);

            permission = (Permission)Enum.Parse(typeof(Permission), "5");
            Console.WriteLine("2、通过数字字符串转换……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);

            permission = (Permission)Enum.Parse(typeof(Permission), "update, delete, read", true);
            Console.WriteLine("3、通过枚举名称字符串转换……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);

            permission = (Permission)7;
            Console.WriteLine("4、直接用数字强制转换……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);

            permission = permission & ~Permission.read;
            Console.WriteLine("5、去掉一个枚举项……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);

            permission = permission|Permission.delete;
            Console.WriteLine("6、加上一个枚举项……");
            Console.WriteLine(permission.ToString());
            Console.WriteLine((int)permission);     
            //1   3   5  7 都是在权限指定 在数据库内  
       /*  
                判断是否包含指定枚举:
                | 符号,把所有二进制数据进行合并,有一个或两个1都返回1
                则:

                color1|Color.Red; 等于5|1,等于5

                color1|Color.Bule; 等于5|4,等于5

                color1|Color.Green; 等于5|2,等于7

                color1|Color.White; 等于5|8,等于13

       */ 
       }
   }
}

  

 在数据库中判断:

AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission)

  上面的sql语句同样可以判断多个权限

 

 这里权限我改了 不是枚举 是int数据结构如下  

/*
Navicat MySQL Data Transfer

Source Server         : 47.94.174.85_3306
Source Server Version : 50719
Source Host           : 47.94.174.85:3306
Source Database       : testDb

Target Server Type    : MYSQL
Target Server Version : 50719
File Encoding         : 65001

Date: 2019-04-14 23:54:20
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for t_authorization
-- ----------------------------
DROP TABLE IF EXISTS `t_authorization`;
CREATE TABLE `t_authorization` (
  `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id自增',
  `Name` varchar(50) NOT NULL,
  `Pass` varchar(50) NOT NULL,
  `permission` int(11) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_authorization
-- ----------------------------
INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1');
INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2');
INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4');

  执行的操作如下

set @permission=1;
UPDATE t_authorization SET Name= '4444' WHERE id= '1' AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission) --  AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission)  如何数据是我上面的那样的话 这样Sql执行就相当于 验证该字段 @permission局部变量是否等于数据库permission字段,permission字段为int类型--  权限肯定不会这样做的,存的应该是权限标识的集合 已测--   

经验证 permission 我一开始写 1   2  4  是错的 不应该是数*(这数不是奇偶数而是2的立方数叠加的),应该是对应的数即权限的集合值 例如 3  5  6  7  9。    网上写法   (@permission IS NULL OR @permission=0 OR permission &@permission =@permission)  中解读

 @permission IS NULL    是null 为true执行

 @permission=0   是0为true执行

OR permission &@permission =@permission  按位与

比如:

SELECT 4&1            0

SELECT 4&2            0  

SELECT 4&4            4   

SELECT 5&4            4

SELECT 6&4            4

SELECT 7&4            4

SELECT 8&4            0

SELECT 9&4            4

SELECT 10&4          4

SELECT 8&2            0  //说明值相没有包含值9  

SELECT 9&2            0  //说明值相没有包含值9   

SELECT 8&2            2

SELECT 9&2            0

注意:左边&右边      即左边是权限集合,右边是权限标识   最后结果也是权限标识

SQL语句如下:

SELECT * FROM TableName WHERE ColumnName & 1=1
/*或者*/
SELECT * FROM TableName WHERE ColumnName | 1=ColumnName

  

或者

set @permission=1;
UPDATE t_authorization SET Name= '4444' WHERE id= '1' AND (@permission IS NOT NULL OR @permission!=0 OR permission &@permission =@permission)
--   
@permission IS NOT NULL    不为空
@permission!=0  不为0   permission某个权限集合  @permission 某个权限 最好关联外表即权限的列表
--

  

  

首先:

1、判断权限不是靠  大于比较的     1      2     4     8         这是权限每一种展示              3    5   6  7  9 10  11  12  13  14  15  则是这几个可以拼接的合  代表一种包含的权限      通过位移 计算是哪几种权限的集合          

2、靠位移(经检验位移运算符已解决, 比如4这个权限集合是从1开始 那几个数值位移得到的,然后就可以几个数值进行判断是否包含-   位移的是二进制的1 所以和1组合的对于我们二进制数)

 数据库动态添加权限数据

INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1');
INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2');
INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4');
例如要动态插入这个三个后面,可能出现的情况1、位移数会越来越大。2、删除一个值,将新的插入进入 可能会导致权限 乱(仅局限与编辑操作)
解决方案:
方案一
1、Id设置自增。
2、设置权限值全部为2。
3、权限计算通过   2>>(主键-1)
即我们插入的数据格式

INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '2');
INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2');
INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '2');

注:这样我们的权限控制都是通过计算得出(甚至字段列2都不需要)。

 方案二

INSERT INTO `t_authorization` VALUES ('1', '张三', '4444', '1');
INSERT INTO `t_authorization` VALUES ('2', '李四', '123456', '2');
INSERT INTO `t_authorization` VALUES ('3', '王五', '123456', '4');

这种添加一个权限列  把权限值 写入类内

 3、DescriptionAttribute

所谓的enum扩展 其实就是用反射动态获取给定值

4、HelpAttribute

[HelpAttribute("Information on the class MyClass")]
class MyClass
{
}

一、C#中几个简单的内置Attribute

1、Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。 
2、DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。 
3、Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。

C# typeof() 和 GetType()区是什么?
1、typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。 
2、GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof()相同,返回Type类型的当前对象的类型。 

比如有这样一个变量i: 
Int32 i = new Int32(); 

i.GetType()返回值是Int32的类型,但是无法使用typeof(i),因为i是一个变量,如果要使用typeof(),则只能:typeof(Int32),返回的同样是Int32的类型。

  

原文地址:https://www.cnblogs.com/fger/p/10706308.html