代码之家  ›  专栏  ›  技术社区  ›  Jura Gorohovsky

ASP.NET核心自定义验证创建模型的新实例

  •  3
  • Jura Gorohovsky  · 技术社区  · 6 年前

    应用程序还没有使用任何类型的存储库,它只是将模型实例作为静态字段存储在控制器中。

    我一定是误解了模型验证的工作方式,但是调试并没有给我任何更好的线索,而不仅仅是显示每次都会出现一个新的长单词的验证上下文。

    我卡住了,请帮帮我。

    这是控制器:

    public class HomeController : Controller
    {
        private static WordGameModel _model;
    
        public IActionResult Index()
        {
            if (_model == null)
            {
                _model = new WordGameModel();
            }
            return View(_model);
        }
    
        [HttpPost]
        public IActionResult Index(WordGameModel incomingModel)
        {
            if (ModelState.IsValid)
            {
                _model.Words.Add(incomingModel.ContainedWordCandidate);
                return RedirectToAction(nameof(Index), _model);
            }
            return View(_model);
        }
    }
    

    游戏模式:

    public class WordGameModel
    {
        public WordGameModel()
        {
            if (DictionaryModel.Dictionary == null) DictionaryModel.LoadDictionary();
            LongWord = DictionaryModel.GetRandomLongWord();
            Words = new List<string>();
        }
    
        public string LongWord { get; set; }
        public List<string> Words { get; set; }
    
        [Required(ErrorMessage = "Empty word is not allowed")]
        [MinLength(5, ErrorMessage = "A word shouldn't be shorter than 5 characters")]
        [MatchesLettersInLongWord]
        [NotSubmittedPreviously]
        public string ContainedWordCandidate { get; set; }
    
        public bool WordWasNotSubmittedPreviously() => !Words.Contains(ContainedWordCandidate);
        public bool WordMatchesLettersInLongWord()
        {
            if (string.IsNullOrWhiteSpace(ContainedWordCandidate)) return false;
            return ContainedWordCandidate.All(letter => LongWord.Contains(letter));
        }
    }
    

    验证失败时的自定义验证属性:

    internal class MatchesLettersInLongWord : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            WordGameModel model = (WordGameModel) validationContext.ObjectInstance;
    
            if (model.WordMatchesLettersInLongWord()) return ValidationResult.Success;
    
            return new ValidationResult("The submitted word contains characters that the long word doesn't contain");
        }
    }
    

    @model WordGameModel
    
    <div class="row">
        <div class="col-md-12">
            <h2>@Model.LongWord</h2>
        </div>
    </div>
    
    <div class="row">
        <div class="col-md-6">
            <form id="wordForm" method="post">
                <div>
                    <input id="wordInput" asp-for="ContainedWordCandidate"/>
                    <input type="submit" name="Add" value="Add"/>
                    <span asp-validation-for="ContainedWordCandidate"></span>
                </div>
    
            </form>
        </div>
    </div>
    
    <div class="row">
        <div class="col-md-6">
            <ul>
                @foreach (var word in @Model.Words)
                {
                    <li>@word</li>
                }
            </ul>
        </div>
    </div>
    

    3 回复  |  直到 6 年前
        1
  •  1
  •   user3559349 user3559349    6 年前

    您的视图需要包含 LongWord 长词

    <form id="wordForm" method="post">
        <div>
            <input type="hidden" asp-for="LongWord" /> // add hidden input
            <input id="wordInput" asp-for="ContainedWordCandidate"/>
            <input type="submit" name="Add" value="Add"/>
            <span asp-validation-for="ContainedWordCandidate"></span>
        </div>
    </form>
    

    作为旁注,在post方法中 return RedirectToAction(nameof(Index)); -GET方法没有(也不应该)模型的参数,因此传递它是没有意义的(而且它只会创建一个难看的查询字符串)

        2
  •  1
  •   Simply Ged Saeed    6 年前

    transient 为每个请求创建一个新的。因此,即使您的静态变量应该仍然可用,但将其保留在控制器中并不是一个好方法。此外,您还希望保持您的模型干净,即不要将任何业务/游戏逻辑放入其中。使用不同的类。仅使用模型以确保值有效,即最小长度、要求等。

    一个更好的解决你的问题的方法是创建一个 singleton

    例如:

    public interface IWordService
    {
        IEnumerable<String> Words { get; }
    
        bool WordWasNotSubmittedPreviously(string word);
    
        bool WordMatchesLettersInLongWord(string longWord, string containedWordCandidate);
    
        void AddWordToList(string word);
    }
    
    public class WordService : IWordService
    {
        private List<string> _words;
    
        public IEnumerable<string> Words => _words;
    
        public WordService()
        {
            _words = new List<string>();
        }
    
        public bool WordWasNotSubmittedPreviously(string containedWordCandidate) => !_words.Contains(containedWordCandidate);
    
        public bool WordMatchesLettersInLongWord(string longWord, string containedWordCandidate)
        {
            if (string.IsNullOrWhiteSpace(containedWordCandidate)) return false;
            return containedWordCandidate.All(letter => longWord.Contains(letter));
        }
    
        public void AddWordToList(string word)
        {
            _words.Add(word);
        }
    }
    

    这项服务完成了你所有的工作 ValidationAttribute 但是我们可以使用依赖注入来确保我们只为整个应用程序创建一个依赖注入。

    在你身上 Startup.cs 将此添加到 ConfigureServices

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IWordService, WordService>();
    
        ....
    }
    

    现在我们可以把它注入到我们的控制器中,因为我们已经将它注册为 单子 我们每次都会得到相同的实例,即使我们得到的是不同的控制器实例:

    public class HomeController : Controller
    {
        private readonly IWordService _wordService;
    
        public HomeController(IWordService wordService)
        {
            _wordService = wordService;
        }
    
        [HttpPost]
        public IActionResult Index(WordGameModel incomingModel)
        {
            if (ModelState.IsValid)
            {
                // Use the `_wordService instance to perform your checks and validation
                ...
            }
    
            ...
        }
    }
    

    _wordService

    here

    还有 配置服务 方法 here

        3
  •  0
  •   Armin Torkashvand    6 年前

    mvc框架为此创建一个新的控制器实例。返回响应后,它将处理控制器。

    WordGameModel 将再次实例化,它的构造函数将创建一个新的长单词。
    您可以将对象保存在某个数据库中,以供每个用户提供该功能。