C# 属性是否丢失表达式x=>;中的元数据;x、 AccountNumber.ToString()是否使用FlientValidation?

C# 属性是否丢失表达式x=>;中的元数据;x、 AccountNumber.ToString()是否使用FlientValidation?,c#,asp.net-core,.net-5,fluentvalidation,C#,Asp.net Core,.net 5,Fluentvalidation,我正在为一个个人项目构建一个web应用程序,在.NET 5.0的RESTful API中使用FlientValidation时遇到了一个不寻常的行为。总之,直截了当地说: 这是我的DTO: public class ClientDto { public string ClientName { get; set; } public int? AccountNumber { get; set; } public int InitialFunds { get; set; }

我正在为一个个人项目构建一个web应用程序,在.NET 5.0的RESTful API中使用FlientValidation时遇到了一个不寻常的行为。总之,直截了当地说:

这是我的DTO:

public class ClientDto
{
    public string ClientName { get; set; }

    public int? AccountNumber { get; set; }

    public int InitialFunds { get; set; }
}
A有一个验证器,对于
AccountNumber

public class ClientDtoValidator : AbstractValidator<ClientDto>
{
    public ClientDtoValidator()
    {
        RuleFor(x => x.ClientName).NotNull().NotEmpty();
        RuleFor(x => x.AccountNumber.ToString()).MaximumLength(5).When(x => x.AccountNumber != null);
    }
}
酒店的名字在哪里?这是我刚才尝试发送的JSON请求:

{
  "ClientName" : "Mike",
  "AccountNumber" : 123456 
}
如果尝试一个
AccountNumber
,比如12345,服务器返回200。ClientName还正确验证:

{
  "ClientName" : "",
  "AccountNumber" : 12345
}
答复:

{ 
    {
    "title": "One or more validation errors occurred.",
    "status": 400,
    "errors": {
        "": [
            "The length of '' must be 5 characters or fewer. You entered 6 characters."
        ]
    }
}
{
    "title": "One or more validation errors occurred.",
    "status": 400,
     "errors": {
        "ClientName": [
            "'Client Name' must not be empty."
        ]
    }
}
回到验证器,如果我把规则换成简单的东西

//RuleFor(x => x.AccountNumber.ToString()).MaximumLength(5).When(x => x.AccountNumber != null);
RuleFor(x => x.AccountNumber).NotNull().NotEmpty();
这样的要求

{
  "ClientName" : null,
  "AccountNumber" : null
}
将显示这两个字段的名称来自.NET管道的某个位置:

"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
    "ClientName": [
        "'Client Name' must not be empty."
    ],
    "AccountNumber": [
        "'Account Number' must not be empty."
    ]
}

我的赌注是在
RuleFor(x=>x.AccountNumber.ToString())
中调用
.ToString()
,但我不是CLR专家。也不是这方面的“FluentValidator专家”。想法?

事实证明,自定义验证非常简单。对于这个特殊的案例,我把这个

 RuleFor(x => x.AccountNumber)
                .When(x => x.AccountNumber.HasValue == false)
                .WithMessage("Account Number' must not be empty.");
RuleFor(x => x.AccountNumber.ToString()).MaximumLength(5).When(x => x.AccountNumber != null);
进入这个

RuleFor(x => x.AccountNumber).Custom((accountNumber, context) =>
{
    if (accountNumber.ToString().Length > 5)
        context.AddFailure(@$"The length of 'AccountNumber' must be 5 characters or fewer. 
        You entered {accountNumber.ToString().Length} characters.");
}).When(x => x.AccountNumber != null);
它绕过了框架找不到属性名的整个问题。即使消息是可自定义的,但我将其保留为默认值:

{
  "ClientName" : "Joe",
  "AccountNumber" : 123456
}
回应

"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-276c77469c6acd4a9bf6f353a5606327-d956a33f62c9eb46-00",
"errors": {
    "AccountNumber": [
        "The length of 'AccountNumber' must be 5 characters or fewer. You entered 6 characters."
    ]
}
好东西

编辑

使其通用(我使用了一个静态类,因为我看不出不这样做的原因):

公共静态类CustomValidationRules
{
公共静态无效检查MaxLengthValidationFailure(int-length,int-maxLength,
字符串属性名称,FluentValidation.ValidationContext ValidationContext)
{
如果(长度>最大长度)
validationContext.AddFailure(@$“{propertyName}的长度必须为{maxLength}
字符或更少。您输入了{length}个字符。“);
}
}
这样使用:

public class ClientDtoValidator : AbstractValidator<ClientDto>
{
    public ClientDtoValidator()
    {
        RuleFor(x => x.ClientName).NotNull().NotEmpty();

        RuleFor(x => x.CreditCardNumber).Custom((creditCardNumber, context) =>
        {
            CustomValidationRules.CheckMaxLengthValidationFailure(creditCardNumber.ToString().Length, 16, "CreditCardNumber", context);
        }).When(x => x.CreditCardNumber != null);


        RuleFor(x => x.AccountNumber).Custom((accountNumber, context) =>
        {
            CustomValidationRules.CheckMaxLengthValidationFailure(accountNumber.ToString().Length, 5, "AccountNumber", context);
        }).When(x => x.AccountNumber != null);
    }
}
公共类ClientDtoValidator:AbstractValidator
{
公共客户端Tovalidator()
{
RuleFor(x=>x.ClientName).NotNull().NotEmpty();
规则(x=>x.CreditCardNumber)。自定义((CreditCardNumber,上下文)=>
{
CustomValidationRules.CheckMaxLengthValidationFailure(creditCardNumber.ToString().Length,16,“creditCardNumber”,上下文);
}).When(x=>x.CreditCardNumber!=null);
RuleFor(x=>x.AccountNumber).Custom((AccountNumber,context)=>
{
CustomValidationRules.CheckMaxLengthValidationFailure(accountNumber.ToString().Length,5,“accountNumber”,上下文);
}).When(x=>x.AccountNumber!=null);
}
}

事实证明,自定义验证非常简单。对于这个特殊的案例,我把这个

RuleFor(x => x.AccountNumber.ToString()).MaximumLength(5).When(x => x.AccountNumber != null);
进入这个

RuleFor(x => x.AccountNumber).Custom((accountNumber, context) =>
{
    if (accountNumber.ToString().Length > 5)
        context.AddFailure(@$"The length of 'AccountNumber' must be 5 characters or fewer. 
        You entered {accountNumber.ToString().Length} characters.");
}).When(x => x.AccountNumber != null);
它绕过了框架找不到属性名的整个问题。即使消息是可自定义的,但我将其保留为默认值:

{
  "ClientName" : "Joe",
  "AccountNumber" : 123456
}
回应

"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-276c77469c6acd4a9bf6f353a5606327-d956a33f62c9eb46-00",
"errors": {
    "AccountNumber": [
        "The length of 'AccountNumber' must be 5 characters or fewer. You entered 6 characters."
    ]
}
好东西

编辑

使其通用(我使用了一个静态类,因为我看不出不这样做的原因):

公共静态类CustomValidationRules
{
公共静态无效检查MaxLengthValidationFailure(int-length,int-maxLength,
字符串属性名称,FluentValidation.ValidationContext ValidationContext)
{
如果(长度>最大长度)
validationContext.AddFailure(@$“{propertyName}的长度必须为{maxLength}
字符或更少。您输入了{length}个字符。“);
}
}
这样使用:

public class ClientDtoValidator : AbstractValidator<ClientDto>
{
    public ClientDtoValidator()
    {
        RuleFor(x => x.ClientName).NotNull().NotEmpty();

        RuleFor(x => x.CreditCardNumber).Custom((creditCardNumber, context) =>
        {
            CustomValidationRules.CheckMaxLengthValidationFailure(creditCardNumber.ToString().Length, 16, "CreditCardNumber", context);
        }).When(x => x.CreditCardNumber != null);


        RuleFor(x => x.AccountNumber).Custom((accountNumber, context) =>
        {
            CustomValidationRules.CheckMaxLengthValidationFailure(accountNumber.ToString().Length, 5, "AccountNumber", context);
        }).When(x => x.AccountNumber != null);
    }
}
公共类ClientDtoValidator:AbstractValidator
{
公共客户端Tovalidator()
{
RuleFor(x=>x.ClientName).NotNull().NotEmpty();
规则(x=>x.CreditCardNumber)。自定义((CreditCardNumber,上下文)=>
{
CustomValidationRules.CheckMaxLengthValidationFailure(creditCardNumber.ToString().Length,16,“creditCardNumber”,上下文);
}).When(x=>x.CreditCardNumber!=null);
RuleFor(x=>x.AccountNumber).Custom((AccountNumber,context)=>
{
CustomValidationRules.CheckMaxLengthValidationFailure(accountNumber.ToString().Length,5,“accountNumber”,上下文);
}).When(x=>x.AccountNumber!=null);
}
}
RuleFor(x=>x.AccountNumber.ToString())
中使用ToString()是个问题,它不再是DTO属性的成员表达式/规则,而是字符串表达式

您不需要编写自定义代码来处理此问题

Transform(x=>x.AccountNumber,x=>x.ToString())。最大长度(5)。当(x=>x.AccountNumber!=null)将是一种方法(注意:)

另一种方式

RuleFor(x=>x.AccountNumber).inclusivebeween(199999).When(x=>x.AccountNumber!=null)

因为您在使用
时总是在服务器端对其进行评估,
必须
是另一个选项。如果有更多选项可用,主要的问题是如果不需要自定义代码,就避免使用它。

RuleFor(x=>x.AccountNumber.ToString())中使用ToString()
是问题所在,它不再是DTO属性的成员表达式/规则,而是字符串表达式

您不需要编写自定义代码来处理此问题

Transform(x=>x.AccountNumber,x=>x.ToString())。最大长度(5)。当(x=>x.AccountNumber!=null)将是一种方法(注意:)

另一种方式

RuleFor(x=>x.AccountNumber).inclusivebeween(199999).When(x=>x.AccountNumber!=null)


因为您在使用
时总是在服务器端对其进行评估,
必须
是另一个选项。如果有更多的选项可用,主要的是避免使用不需要的自定义代码。

它使用的是反编译的表达式。它正在寻找一个成员表达式,以获取其名称(属性或字段),但它没有得到该名称,而是得到一个方法调用表达式。所以它默认为空白。对我来说有道理。你的帐号是真的吗