如何更好地构建架构

时间:2019-01-05 17:18:58

标签: c# api asp.net-core architecture global-variables

我有一个ASP.NET Core应用程序,该应用程序从另一个库中调用服务。的 服务与需要sessionId的外部API一起使用。我们必须调用Login API方法来获取sessionId。这个sessionId的寿命以及何时可以更改-我们不知道。规则是:sessionId可以对1个请求有效,对于10个请求,对于100个请求,可以有效1分钟,10分钟,1天……没人知道。

该服务有许多方法可以调用类似的API:

public class BillRequest
{
    private readonly HttpClient client;

    public BillRequest()
    {
        client = new HttpClient
        {
            BaseAddress = new Uri("https://myapi.com/api/v2/")
        };
    }

    public async Task<List<Dto1>> CustomerBankAccountListAsync(int start, int count)
    {
        List<KeyValuePair<string, string>> nvc = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("sessionId", CURRENT_SESSION_ID)
        };

        var customerStream = await client.PostAsync("List/CustomerBankAccount.json", new FormUrlEncodedContent(nvc));
        var customerString = await customerStream.Content.ReadAsStringAsync();
        //....
    }

    public async Task<List<Dto2>> Method2(int start, int count)
    {
        List<KeyValuePair<string, string>> nvc = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("sessionId", CURRENT_SESSION_ID)
        };

        var customerStream = await client.PostAsync("List/Method2.json", new FormUrlEncodedContent(nvc));
        var customerString = await customerStream.Content.ReadAsStringAsync();
        //....
    }

    // logic to get SessionId here
    public async Task LoginAsync()
    {

    }

如何实现将此sessionId保存在服务内部?

有许多实现方法:

  1. 每次调用方法之前,都要调用Login方法。易于实现,但方法很糟糕,因为那时我们有许多不必要的请求,并且只使用一次sessionId

  2. 在网络应用程序级别保存sessionId并尝试捕获异常,当任何方法返回“无效的sessionId”,然后调用Login方法,该方法将返回一个新的{ {1}}。在这种情况下,我们必须将sessionId传递给sessionId类的构造函数。它可以工作,但是我不喜欢将服务责任转移到其他人,因为如何使用API​​是服务的内部责任。

  3. BillRequest保存在服务本身中,并在服务内部调用sessionId方法,当旧的Login被认为无效时,用新的方法重写等。但是如何将其保存为内存中的“静态”?我不想将其保存到任何外部位置(文件系统,云等),但是我也无法保存到class的变量中,因为可以重新创建class的对象...

1 个答案:

答案 0 :(得分:1)

我建议在此方面应将某些精神转向函数式编程。

sessionID视为独立值的而不是单个对象。然后可以通过以下方式(在含义上等效)重新定义您的问题:给定类型的流(在您的情况下为string),如何观察其流程并进行反应关于即将到来的更改,您的源代码无法控制哪些更改?

好吧,有一个答案已被Enterprise™证明:reactive extensions

从技术上讲,这种转变意味着您正在处理控制器内部的IObservable<string>,它可以通过标准.NET Core DI方法注入,也可以仅由构造方法定义。这非常灵活,因为rX为您提供了完全可测试,难以置信的强大工具集来处理此类任务。 rX也与本机Task兼容,因此与async/await功能兼容。一个不错的事实是,从外部世界注入所需的行为并用一种更合适的行为来装饰可观察的事物确实很容易:因此,您很安全:一旦第3方的服务逻辑发生变化,您几乎可以毫不费力地立即采用您的代码库。 / p>

IObservable<string>里面将会是什么?好吧,我不能说,因为您没有提供足够的信息。可能是interval询问远程服务器当前sessionID是否仍然有效,如果不是,则运行-重新登录过程并通知其订户有关新值;它可能是timer负责编译时已知的到期规则,它可能是您需要的复杂逻辑:rX足够灵活,不会限制您使用它可以实现的目标当您处理(可能是无限的)流时。

因此,这意味着您不需要任何全局值。只需订阅会话ID的流,并获取最新的会话ID(当前有效的会话ID)即可完成工作并处置您的订阅。它不昂贵并且不会影响性能。两者都不会破坏并发性。如果您想坚持使用通用的.NET方式,请将rX放入Taskawait中。

P.S。提供实施所需的99%已经存在;您只需要将其组合即可。