【问题标题】:Use FluentValidation to access the database validating more than one field使用 FluentValidation 访问验证多个字段的数据库
【发布时间】:2021-03-12 20:08:11
【问题描述】:

我学会了如何使用流利的验证器我想知道你是否可以帮助我解决一个问题。我有一个个人系统,在我的控制器中,create post 方法具有以下代码:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create (TicketViewModel model)
{
    ICollection <Piece> pieces;
    try
    {
        if (ModelState.IsValid)
        {
            if (_ticketRepository.GetById (model.Id)! = null)
            {
                Console.WriteLine ("Ticket already exists!");
            }

            var _model = _mapper.Map <Ticket> (model);
            var count = _ticketRepository.FindAllByPiece (_model);

            if (count> 0)
            {
                ModelState.AddModelError ("", "This ticket already exists for this room. Check the piece, date and time.");
                pieces = _pieceRepository.FindAll ();
                model.Pieces = pieces;
                return View (model);
            }

            _ticketRepository.Insert (_model);
            model.Seats = RegistrationSeats (model.QuantityOfSeats);

            return RedirectToAction ("Index");
        }
        pieces = _pieceRepository.FindAll ();

        model.Pieces = pieces;

        return View (model);
    }
    catch (Exception ex)
    {
        Console.WriteLine (ex.Message);
        return View (model);
    }
}

请注意,在上面的代码中,我有这样的部分:

var count = _ticketRepository.FindAllByPiece (_model);    
if (count> 0)
{
    ModelState.AddModelError ("", "This ticket already exists for this room. Check the piece, date and time.");
    pieces = _pieceRepository.FindAll ();
    model.Pieces = pieces;
    return View (model);
}
  • 此代码 sn-p 从存储库中调用一项服务,该服务检查通过的模型(票证)是否与银行中已有的模型(票证)具有相同的 partId、日期和时间表。如果存在具有这些相同值的记录,则他不会添加票证,直到该人创建与已存在的票证不同的票证。
  • 这是访问银行进行此搜索的服务代码:
public int FindAllByPiece(Ticket model)
{
    return _saleTheaterTicketsContext.Tickets.Include(x => x.Piece).Where(x => x.PieceId == model.PieceId && x.Date == model.Date && x.Schedule == model.Schedule).Count();
}
  • 这很好,并在视图中显示我在ModelState.AddModelError 添加的消息我确实喜欢在视图中:
<strong class = "text-danger">@ Html.ValidationSummary (true)</strong>
  • 我的问题是,当我使用 FluentValidation 时,我想知道是否有任何方法可以做到这一点,这样控制器就不会装满东西。我做了一些研究,但无济于事。我尝试在 Validation 类中实例化存储库(我将在下面放置代码),但我不知道如何对这 3 个字段一起进行验证,将模型传递给存储库。你能帮我吗?
  • 这是使用 FluentValidator 的 Validation 类中的代码:
public class TicketViewModelValidator: AbstractValidator <TicketViewModel>
{
    public TicketViewModelValidator ()
    {
        RuleFor (x => x.Price)
            .NotEmpty (). WithMessage ("Enter the price")
            .GreaterThan (0) .WithMessage ("The price must be greater than 0.00");
        RuleFor (x => x.QuantityOfSeats)
            .NotEmpty (). WithMessage ("Enter the number of seats")
            .GreaterThanOrEqualTo (10) .WithMessage ("The number of seats must be at least 10");
        RuleFor (x => x.Date)
            .NotEmpty (). WithMessage ("Enter Date")
            .Must (ValidDate) .WithMessage ("Date must be greater than or equal to today");
        RuleFor (x => x.Schedule)
            .NotEmpty (). WithMessage ("Enter time");
        RuleFor (x => x.PieceId)
            .NotEmpty (). WithMessage ("Select the part");
    }

    public static bool ValidDate (DateTime date)
    {
        if (date.Date> = DateTime.Now.Date)
        {
            return true;
        }

        return false;
    }
}

【问题讨论】:

  • 这个问题和LINQ有什么关系?
  • @Thomas Weller e DavidG 大写的标题有什么问题?
  • @NetMage 这个问题与 linq 有关,因为如果您仔细观察,您会发现我在服务上进行的查询使用 linq,但我想在流畅的验证规则中做同样的事情。
  • 我不确定 LINQ 是如何参与的,但我要指出的是,您应该在服务器端复制客户端验证,以确保伪造提交无法绕过验证。
  • @NetMage,我上面显示的“FindAllPieces”服务显然使用了 Linq。 Include、Where 等。Fluent Validation 验证双方。

标签: c# linq asp.net-core entity-framework-core fluentvalidation


【解决方案1】:

Fluent 验证使用“必须”方法处理此问题。以下是您需要做的事情的简化

  1. 将 _ticketRepository 的实例传递给验证器。这要求您使用依赖注入。我假设您正在控制器中执行此操作。您的验证器的构造函数将被修改为如下所示:

    public class TicketViewModelValidator: AbstractValidator <TicketViewModel>
    {
        private ITiekctRepository _ticketRepository;
    
        public TicketViewModelValidator (ITicketRepository ticketRepository)
        {
            _ticketRepository = ticketRepository;
            //...rules go here
        }
  1. 修改规则以检查存储库的条件。例如,您可能会执行以下操作:
RuleFor(t => t.Id).NotEmpty().Must(BeUnique).WithMessage("A ticket must have an ID that is unique in the database").

然后像这样定义一个“BeUnique”方法

    private bool BeUnique(TicketViewModelTicketViewModel instance)
    {
         // The instance is the model being validated.
         if (_ticketRepository.GetById (instance.Id) == null)
             return true
         return false;
    }

这里有很大的灵活性,因此请仔细阅读文档。请注意,由于您正在处理模型(在方法中称为实例),因此您可以访问所有属性并且一次可以检查多个内容。不过,您可能不想这样做,因为它使返回良好的错误消息变得更加困难。这种技术适用于 Entity Framework,因为 model.Id 引用的数据对象将被缓存在 dbContext 中,并在以后需要时重复使用,因此它保存为查询。

请注意,我在没有使用编译器或 Intellisense 的情况下从内存中输入了很多这样的内容来获得正确的语法。不过基本流程应该可以工作。

【讨论】:

  • @GlenSills,感谢您的回复。但是,我遇到了如下问题:我编写了说话的代码,但是当我在 Must 方法中调用 BeUnique 时,它​​显示错误消息“参数 2:无法从 '方法组' 转换为 Func ”。我以为是因为方法必须是静态的,但如果我为 BeUnique 设置静态,我用来将 ViewModel 转换为 Model 的存储库和 AutoMapper 实例中会出现错误。
  • 尝试改成private bool BeUnique(int id),其中id是viewmodel id。
  • @GleennSills,我认为您不太了解我的上下文。如果相同的日期和时间相同,我有一张我不想创建的票证,还有一些其他票证记录具有相同的片断、日期和时间值。通过我咨询银行的样子你就会明白:
  • public int BeUnique(Ticket model) { return _saleTheaterTicketsContext.Tickets.Include(x => x.Piece).Where(x => x.PieceId == model.PieceId && x.Date == model.Date && x.Schedule == model.Schedule).Count(); }
  • 我希望 FluentValidation 能够访问它,但我不知道怎么做。仅传递 id 甚至会有所帮助,因为在将此模型分配给 BeUnique 服务后,我可以获得通过 id 找到票的查询,但这只会在编辑屏幕上很好,因为我正在创建,我仍然没有一个身份证。由于尚未完成注册,因此在 create 操作中初始 id 为零。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 2014-03-26
  • 1970-01-01
相关资源
最近更新 更多