如何将 Validation<string, Unit> 转换为成功时为空的字符串?

Posted

技术标签:

【中文标题】如何将 Validation<string, Unit> 转换为成功时为空的字符串?【英文标题】:How do I convert a Validation<string, Unit> to a string that's empty on success? 【发布时间】:2020-08-05 18:35:55 【问题描述】:

我是函数式编程的新手,并且正在尝试在一个非常命令式的代码库中工作,所以我正在尝试编写主要是 FP 的代码,但不能完全。

我必须为下面的方法写一个覆盖...

protected override string CreateEntity(XElement xe)

...其中xe 包含数据库更新所需的两条数据。该方法需要验证xe 的内容,然后进行更新,成功则返回“”,否则返回以竖线分隔的错误列表。

我从语言扩展测试中的credit card validation sample 中得到启发,并编写了一些如下的辅助方法(实现与示例非常相似,因此省略)...

private Validation<string, int> ValidateRegionID(XElement xe)

在示例中,他对这些集合使用了 Apply,如果一切正常,则返回一个 Validation,这是新的信用卡对象,否则返回一个 Error。就我而言,如果一切顺利(因为我正在更新数据库,而不是创建新对象),我没有任何东西可以返回,所以到此为止......

Validation<string, Unit> validation = (ValidateRegionID(xe), ValidateClinAppsID(xe))
  .Apply((regionID, clinAppsID) =>
  
    // Do the database update (yes, impure code)...
    return Unit.Default;
  );

我不知道这是否是最好的方法,所以欢迎任何建议。

我更大的问题是下一步该做什么。以下工作,但对我来说看起来不是很优雅或 FP...

return validation.IsFail
  ? string.Join("|", validation.FailAsEnumerable())
  : "";

有更好的方法吗?

【问题讨论】:

【参考方案1】:

如果验证失败,您可以简单地抛出异常。 稍后调用您的代码的任何人都可以对其应用异常处理。

另一个选项是发送错误代码而不是消息。在这种情况下,被调用者可以将每个错误代码转换为特定错误。

【讨论】:

感谢您的回复,但正如我所说,我需要覆盖现有方法,因此无法更改调用代码的作用。我必须返回一个字符串,如果一切顺利,该字符串将为空,否则包含管道分隔的错误。 @AvrohomYisroel 在这种情况下你写的没问题,如果你确实需要将错误消息组合在一起,我看不到其他选项。【参考方案2】:

代码的最后一部分(格式错误)可以这样完成:

        [Fact]
        void Test()
        
            Validation<string, Unit> resultFromDatabase = Fail<string, Unit>("error");

            var showError1 = string.Join("|", resultFromDatabase.FailToSeq()); // use the fact that FailToSeq() will return empty seq for success
            var showError2 = string.Join("|", resultFromDatabase.IfSuccess(Seq<string>())); // explicitly supply a value of the error type (Seq<string>) for success case
            var showError3 = resultFromDatabase.Match(_ => "", errors => string.Join("|", errors)); // only call string.Join for errors
        

选择您最喜欢的变体。如果你想要干净的代码/不要依赖 FailToSeq() 实现在这两种情况下工作,你应该使用MatchIfSuccess/IfFail 明确处理这两种情况。

如果你想改进你的整体设计(避免不纯的代码),你可能需要改变一些基本的东西。一个(非常 FP)选项是使用 Free Monad,LanguageExt 取得了进展以使其更容易,但这可能是转换传统 OO 代码库的一大步。见:

https://github.com/louthy/language-ext/releases/tag/3.4.11(使用 CodeGen 的免费 Monad) https://github.com/louthy/language-ext/wiki/Thinking-Functionally:-Application-Architecture

【讨论】:

谢谢。我没有任何理由不想使用FailToSeq()(我应该有什么理由吗?),这提供了一个简洁的解决方案。我很高兴转向一种非常 FP 的方法,但我正在使用一个非常命令式的大型代码库,以及一个不太热衷于彻底改变的经理。我也因缺乏理解而受到阻碍,尽管随着我的努力,这种情况正在变得更好。再次感谢。 不客气。在本地改进代码是完全没问题的。关于FailToSeq():如果您也将其称为“成功”,则会增加两个(小)风险:如果 LanguageExt 中的实现发生更改(例如,禁止对“成功”=> 异常进行此操作),您就是代码中断。我不认为保罗会改变这一点(但似乎这不在单元测试中)。第二个风险是您的代码的读者可能想知道在“成功”情况下到底发生了什么(异常与空结果)。取决于您的上下文,整体可读性...

以上是关于如何将 Validation<string, Unit> 转换为成功时为空的字符串?的主要内容,如果未能解决你的问题,请参考以下文章

struts中validation验证无效

springMVC引入Validation详解

如何在 Kotlin 中将 String 转换为 Int?

[TypeScript] Decorator-based Validation using Class Validator

如何将 ko.validation 错误与相关的视图模型字段名称联系在一起

如何使用 Knockout-Validation 将无效项目添加到 observableArray 以使 validObservable 无效