繁体   English   中英

C#如果异步,如何获取调用方法的方法名称

[英]C# How to get the method name of the calling method if async

我有一个由两个项目组成的解决方案,一个 Blazor 服务器网站和一个类库。 在类库中,我创建了一个类,允许我向库中其他类中的函数添加自定义属性。 这允许我用可以访问该函数的角色来装饰一个函数。 然后我有一个类被调用来提取这些标签并检查数据库是否一致。 当调用函数是常规函数时,这很好用。 但是,如果它是一个异步函数,我正在查看的堆栈帧将返回“MoveNext”的值而不是方法的名称。 如果该方法是异步的,有没有办法获取调用方法的方法名称? 这是我的伪代码:

添加自定义属性的类:

namespace DataAccessLibrary
{
    [AttributeUsage(AttributeTargets.Method |AttributeTargets.ReturnValue)]
    public class MELAdvanceRoles: Attribute
    {
        public string[] _Roles { get; set; }
        public MELAdvanceRoles(string[] roles)
        {
            _Roles = roles;           
        }
    }
}

使用自定义属性的示例函数:

namespace DataAccessLibrary
{
    public class ProgramData: IProgramData
    {
        private readonly ISqlDataAccess _db;      
        private readonly IUserRights _userRights;

        public ProgramData(ISqlDataAccess db, IUserRights userRights)
        {
            _db = db;            
            _userRights = userRights;
        }   
        [MELAdvanceRoles(new string[] { "MANAGER", "EMPLOYEE", "READ_ONLY" })]
        public async Task<int> UpdateProgram(ProgramModel program)
        {   
            if (_userRights.CanUserEditProgram(program.id))
                //protected stuff
            }
            else
            {
                throw new Exception("403 Forbidden");
            }                
        }
}

_userRights 中的 CanUserEditProgram 函数

public bool CanUserEditProgram(int program_id)
{         
    //get roles in the custom attribute  
    string[] allowed_roles = GetMethodRoles();
    // use that list for database stuff
}

public string[] GetMethodRoles()
{
    string[] roles = { };
    StackTrace stackTrace = new StackTrace();
    //it seems that functions that are async are not on the second frame
    string methodName = stackTrace.GetFrame(2).GetMethod().Name;
    //mehtodname returns 'MoveNext' if the calling method is async
    MethodInfo mInfo = stackTrace.GetFrame(2).GetMethod().ReflectedType.GetMethod(methodName);
    //which means mInfo returns null
    bool isDef = Attribute.IsDefined(mInfo, typeof(MELAdvanceRoles));
    if (isDef)
    {
        MELAdvanceRoles obsAttr = (MELAdvanceRoles)Attribute.GetCustomAttribute(mInfo, typeof(MELAdvanceRoles));
        if (obsAttr != null)
        {
            roles = obsAttr._Roles;
        }
    }
    return roles;
}

谢谢

最初认为这是一个疯狂的想法,我很感兴趣,可以探索如果必须的话我将如何解决这个问题。 这是一个可能的方法的第一个演示哈希。

定义所有具有授权的类都需要实现的接口。

using System;
using System.Collections.Generic;

namespace StackOverflow.Answers
{
    public interface IClassAuthorization
    {
        public List<string> UserRoles { get; set; }

        public Boolean UserHasRole(string role)
        {
            if (UserRoles is null)
                throw new ApplicationException("No UserRoles defined for object.  If you are using Class Authorization you must set the UserRoles");
            return UserRoles.Any(item => item.Equals(role, StringComparison.CurrentCultureIgnoreCase));
        }
    }
}

定义一个结果类。 不严格要求,您可以为未实现返回空值,...这只是为您提供了一种无需求助于输出即可返回结果和数据的方法。 您可以在新的 WeatherForecastService 中看到所有三个实现。

namespace StackOverflow.Answers
{
    public class ClassAuthorizationResult
    {
        public ClassAuthorizationResultType Status { get; private set; } = ClassAuthorizationResultType.NotDefined;

        public object Data { get; set; } = null;

        public static ClassAuthorizationResult Success(object data)
            => new ClassAuthorizationResult { Status=ClassAuthorizationResultType.Success, Data = data };

        public static ClassAuthorizationResult Failure()
            => new ClassAuthorizationResult { Status = ClassAuthorizationResultType.Failure, Data = null };

        public static ClassAuthorizationResult NotDefined()
            => new ClassAuthorizationResult { Status = ClassAuthorizationResultType.NotDefined, Data = null };

        public enum ClassAuthorizationResultType
        {
            NotDefined,
            Success,
            Failure
        }
    }
}

这是新的 WeatherForecastService:

using StackOverflow.Answers.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace StackOverflow.Answers
{
    public class AuthorizedWeatherForecastService : IClassAuthorization
    {
        public List<string> UserRoles { get; set; } = null;

        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private IClassAuthorization _interface => this;

        private List<WeatherForecast> records;

        public AuthorizedWeatherForecastService()
        {
            records = GetWeatherForecasts;
        }

        //  Null return
        public Task<List<WeatherForecast>> ForecastsAsync()
            => Task.FromResult(_interface.UserHasRole("User")
                ? this.records
                : null);
        
        // ClassAuthorizationResult return
        public Task<ClassAuthorizationResult> GetForecastsAsync()
            => Task.FromResult(_interface.UserHasRole("User")
                ? ClassAuthorizationResult.Success(this.records)
                : ClassAuthorizationResult.Failure());

        // Out return
        public Task<bool> GetForecastsAsync(out List<WeatherForecast> list)
        {
            var ok = _interface.UserHasRole("User");
            list = ok ? this.records : new List<WeatherForecast>();
            return Task.FromResult(ok);
        }

        private List<WeatherForecast> GetWeatherForecasts
        {
            get
            {
                var rng = new Random();
                return Enumerable.Range(1, 5).Select(index => new WeatherForecast
                {
                    Date = DateTime.Now.AddDays(index),
                    TemperatureC = rng.Next(-20, 55),
                    Summary = Summaries[rng.Next(Summaries.Length)]
                }).ToList();
            }
        }
    }
}

然后在FetchData

    [Inject] AuthorizedWeatherForecastService ForecastService { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ForecastService.UserRoles = new List<string> { "User" };
        var result = await ForecastService.GetForecastsAsync();
        if (result.Status == ClassAuthorizationResult.ClassAuthorizationResultType.Success)
            forecasts = (List<WeatherForecast>)result.Data;
    }

您可以从 AuthenticationState 获取用户角色。 如果您的类是服务,则直接注入 IAuthenticationStateProvider 并注册AuthenticationStateChanged事件以在用户更改时更新角色。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM