我有设备(如移动电话,支付终端等),可以要求在数据库中持有的卡列表.为了安全起见,设备需要对自己进行授权,并在JWT令牌中显示它们的序列号.如果这些都不匹配,我们将拒绝该请求.

多亏了最近对相关问题的大量帮助,我现在有了两个在类似场景中使用的方法……

static Either<int, string> GetDeviceSerialNumberFromToken() {
  // Get the JWT token from the request header, and extract the device serial number.
  // If the token is invalid or serial number isn't present, we return
  // an error code (int). If the data is all OK, we return the serial
  // number. For simplicity, the latter is used here...
  return "abc123";
}

static async Task<Either<int, Device>> GetDevice(string serialNumber) {
  // Check if we have a device with that serial number, and some other checks
  // As this involves the database, this method is async. Here we simulate that...
  await Task.Delay(10);
  return new Device();
}

这些都非常简单,省略了参数等,但这不应该影响问题.

在当前的场景中,我实际上并不需要Device对象,我只需要知道GetDevice返回了它,而不是错误代码.

假设我们通过了这两个方法,然后我想获取卡片列表并返回它.我有一个足够简单的方法来得到名单...

static async Task<List<Card>> GetCards() {
  // As this involves the database, this method is async. Here we simulate that...
  await Task.Delay(10);
  return new List<Card>();
}

我的意图是将这一切联系在一起,就像我在其他场景中所做的那样……

var cardList = await(
        from deviceSerialNumber in GetDeviceSerialNumberFromToken().ToAsync()
        from _ in GetDevice(deviceSerialNumber).ToAsync()
        from cards in GetCards()
        select cards
);

由于这将是一个Either,因此我将对此调用Match,如果前两个方法中的任何一个失败,则返回错误代码,但该部分与此问题无关.

我的问题是,这会在调用GetCards()"Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List>' to 'LanguageExt.Guard'."的行上给出一个编译器错误

按照我得到的previous question分的答案,我试着加ToAsync(),如下所示……

from cards in GetCards().ToAsync()

...但这给了一个编译器错误"'Task<List>' does not contain a definition for 'ToAsync' and the best extension method overload 'TaskTryExtensions.ToAsync(Try)' requires a receiver of type 'Try'."

我不能把电话打到GetCards(),因为在这样的查询中,你只能打到第一行.我试着反其道而行之(在判断前获得数据).

var cardList = await(
        from cards in await GetCards()
        from deviceSerialNumber in GetDeviceSerialNumberFromToken().ToAsync()
        from _ in GetDevice(deviceSerialNumber).ToAsync()
        select cards
);

...但这给出了编译器错误"Could not find an implementation of the query pattern for source type 'List'. 'SelectMany' not found."

在这一点上,我被困住了.像往常一样,我想象有一个非常简单的答案,但我看不到它.有谁能给点建议吗?

谢谢

推荐答案

当您使用

from x in foo.ToAsync()
from y in bar.ToAsync()

语法中,所有值必须在同一个Monad中.在这里,您从GetDeviceSerialNumberFromToken().ToAsync()开始,这是一个EitherAsync值,因此所有后续值也必须是EitherAsync值.

如果你试着得到minimal, reproducible example分,这一点对你来说会更明显.当我对OP中发布的代码执行此操作时,报告的不是编译器错误,而是以下代码:

在源类型为‘EitherAsync<int, <anonymous type: string deviceSerialNumber, Device _>>’的查询表达式的后续FROM子句中不允许使用类型为‘Task<List<Card>>’的表达式.类型推断在调用"SelectMany"时失败

我认为,从这条信息中应该相当清楚,你需要找到一种方法,将Task<List<Card>>提升到EitherAsync<int, List<Card>>.

一种 Select 是使用RightAsync:

var cardList = await (
    from deviceSerialNumber in GetDeviceSerialNumberFromToken().ToAsync()
    from _ in GetDevice(deviceSerialNumber).ToAsync()
    from cards in EitherAsync<int, List<Card>>.RightAsync(GetCards())
    select cards);

由于我对LanguageExt知之甚少,可能还有比这更方便的帮助器方法,但这就是它的要点.


事实上,如果进口LanguageExt.Prelude分,事情就会变得更容易:

var cardList = await (
    from deviceSerialNumber in GetDeviceSerialNumberFromToken().ToAsync()
    from _ in GetDevice(deviceSerialNumber).ToAsync()
    from cards in RightAsync<int, List<Card>>(GetCards())
    select cards);

Csharp相关问答推荐

在依赖性注入和继承之间进行 Select

如何在C#中将对象[*,*]直接转换为字符串[*,*]?

Blazor-从数据库内部服务器提取错误

为什么SignalR在每个Blazor服务器应用程序启动时最多启动8个服务器?

如何让NLog停止写入冗余信息?

未在数据流块之间传播完成

Savagger使用Fastendpoint更改用户界面参数

自定义列表按字符串的部分排序

try 链接被委派者(多播委托)时,无法将获取运算符应用于类型为';方法组&39;和方法组';的操作数

如何将%{v_扩展}转换为%{v_扩展}>>

Content WithTargetPath实际上是有效的MSBuild项吗?

JsonPath在Newtonsoft.Json';S实现中的赋值

如何强制新设置在Unity中工作?

为什么C#/MSBuild会自发地为不同的项目使用不同的输出路径?

这是否比决定是否使用ConfigureAWait(False)更好?

C#无法将.csv列转换为用于JSON转换的列表

c#在后台实现类型化数组

如何在C#中用Serilog记录类路径、方法名和行编号

如何获取我在SQL中输入的值

OPC UA标签收集(OPCFoundation NETStandard库,代码更正)