如何避免重复的条件逻辑?

3

我正在为我的.NET Core应用程序实现安全特性,但发现自己不断重复相同的条件逻辑。 有没有一种方法可以在一个地方进行概括,并将其应用于我想要的部分? 我记得使用委托或Func来处理这种情况,但我不太确定... 有什么想法吗?

以下是我尝试编写一次并在多个地方应用的代码。

var currentUser = _httpContext.HttpContext.Session.GetCurrentUser<SessionContext>();
if(currentUser.Roles.Any())
{
    // ex query here. This could be any piece of code
    var q = from u in _dbContext.Users
            join d in _dbContext.Users on u.DoctorId equals d.Id into ud
            from docU in ud.DefaultIfEmpty()
            select new
            {
                User = u,
                Doctor = docU
            };

    if(!currentUser.Roles.Contains("Administrator"))
    {
        if(currentUser.Roles.Contains("Doctor"))
        {
            //do something here
           //ex.
           q = q.Where(x => (x.Doctor != null ? x.Doctor.Id == currentUserId : false));
        }
        else if (currentUser.Roles.Contains("Patient"))
        {
            //do something else here
            //ex.
            q = q.Where(x => x.User.Id == currentUserId);
        }
    }
}
else
    throw new Exception("No roles applied to logged in user");

你是在说这个特定的代码块在多个地方出现,还是在担心这个单一代码块中的多个if语句? - Lasse V. Karlsen
我在多个地方都有这段代码块。我对多个 if 语句不太担心。 - roonz11
如果不知道在 If 语句中做了什么,很难知道会发生什么变化。如果这是针对控制器方法的 API,您可以添加一个类似于[Authorize(Roles = "Doctor", "Patient")]的授权属性。 - ejwill
我正在if语句中编写LINQ查询。但是我猜我的目标是在这些if语句中编写任何内容,但保持外部逻辑不变。我已更新示例。干杯! - roonz11
1
我认为你应该看一下C#中的规范模式。 - Vadim Bondaruk
2个回答

0
你可以创建一个新的 service
public class MyHttpContextService : IMyHttpContextService
{
    IHttpContextAccessor _httpContext;

    public MyHttpContextService(IHttpContextAccessor httpContext)
    {
        _httpContext = httpContext;
    }

    public string CheckUserRoles()
    {
        try
        {
            var currentUser = _httpContext?.HttpContext?.Session?.GetCurrentUser<SessionContext>();
            if (currentUser != null)
            {
                if(currentUser.Roles.Any())
                {
                    if(!currentUser.Roles.Contains("Administrator"))
                    {
                        if(currentUser.Roles.Contains("Doctor"))
                        {
                            //do something here
                        }
                        else if (currentUser.Roles.Contains("Patient"))
                        {
                            //do something else here
                        }
                    }
                }
            }
            else
            {
                // if currentUser == null
            }
        }
        catch (Exception ex)
        {
            // exception handling
        }

    }
}

注意这一行

var currentUser = _httpContext.HttpContext.Session.GetCurrentUser<SessionContext>();

被替换为

var currentUser = _httpContext?.HttpContext?.Session?.GetCurrentUser<SessionContext>();

创建适当的接口。
public interface IMyHttpContextService
{
    string CheckUserRoles();
}

在这个例子中,string是返回类型,但它不是必须的。
最后,使用以下代码注册该服务。
services.AddScoped<IMyHttpContextService, MyHttpContextService>();

其中services

IServiceCollection services

你可以使用AddTransient或者AddSingleton代替AddScoped更多关于对象生命周期和依赖注入的内容。这三个关键字决定了一个对象的生命周期,或者在这种情况下,服务的生命周期。

注册从Startup.cs开始(但说实话,所有东西都是从Startup.cs开始的,因此得名)。更多关于Startup.cs的内容。换句话说,在Startup.cs中调用方法。

public void ConfigureServices(IServiceCollection services)

在运行时被调用的。

在方法ConfigureServices内部调用另一个方法,例如MapInterfaces,用于接口映射并将其传递给services。 方法MapInterfaces将是ServiceExtensions.cs中的一个static方法。

public static void MapInterfaces(IServiceCollection services)
{
    services.AddScoped<IMyHttpContextService, MyHttpContextService>();
}

更好的方法是创建一个扩展方法了解更多关于扩展方法ServiceExtensions.cs是一个静态类,这是创建扩展方法的条件。扩展方法也需要是静态的。这将是方法签名。
static void MapInterfaces(this IServiceCollection services)

当然,不要忘记访问修饰符(至少与 ServiceExtensions.cs 类的可见性相同)。注意 this 关键字。
然后在 Startup.cs 的 ConfigureServices 方法中调用来自 ServiceExtensions.cs 的扩展方法 MapInterfaces,如下所示。
services.MapInterfaces();

最后,无论何时您需要方法CheckUserRoles,请像这样调用它。
_myHttpContextService.CheckUserRoles();

编辑:你已经改变了方法的实现,但这并不影响你完成解决方案的方式。


0

这里是用Swift编写的一些代码。 我正在使用面向函数的编程,使用字典。


struct User {
    var Roles: Set<String> = ["Doctor"]
}

func channel(user: User, _ roles: [String:() -> ()]) {
    for i in roles {
        if user.Roles.contains(i.key) { i.value() }
    }
}

let currentUser = User()
channel(user: currentUser,
       [
        "Doctor": {
        // Code for doctor
        },

        "Admin": {
        // Code for admin
        },

        "Blah": {
        // Code for blah
        },

        // You can even add more
    ]
)

您可以使用枚举创建一个 Enum
为什么要使用枚举?
使用普通的字符串很容易出现拼写错误
而通过使用枚举,如果您出现拼写错误,Swift 会给出错误提示。非常有帮助!

enum UserRolls { case doctor, admin, patient, other(String) }
extension UserRolls: Hashable {}

struct User {
    var Roles: Set<UserRolls> = [.doctor]
}

func channel(user: User, _ roles: [UserRolls:() -> ()]) {
    for i in roles {
        if user.Roles.contains(i.key) { i.value() }
    }
}

let currentUser = User()
channel(user: currentUser,
       [
        .doctor: {
        // Code for doctor
        },

        .admin: {
        // Code for admin
        },

        .other("Blah"): {
        // Code for blah
        },

        // You can even add more
    ]
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接