C# MVC CMS - 自定义远程验证

Posted

技术标签:

【中文标题】C# MVC CMS - 自定义远程验证【英文标题】:C# MVC CMS - Customising Remote Validation 【发布时间】:2017-01-09 09:19:36 【问题描述】:

在下面的链接中,我问了一个关于如何确保字段不包含相同值的问题(例如,当字段上存在唯一约束时,正确地导致 C# 在被破坏时抛出异常)。根据我收到的答案,它解决了这个问题,但提出了另一个问题。

Ensuring another record does not already contain the same value for a field

我现在遇到的主要问题是,当我创建一个新视图时。验证按预期工作。简而言之 - 系统需要检查 ViewName 和 ViewPath(路由)是否都是唯一的,因此需要搜索 DB。

但是,当我编辑视图时,验证会再次启动(实际上不应该,因为显然视图已经存在,因为您正在编辑它)。

我现在的问题是如何自定义远程验证以使编辑与创建的工作方式不同。虽然我们不应该能够编辑视图的名称以匹配现有视图,但我们也不应该仅仅因为它与当前视图相同而停止保存当前视图。

下面是我的模型(不是(希望)由工具生成的部分 :-):

[MetadataType(typeof(IViewMetaData))]
public partial class View : IViewMetaData  

public interface IViewMetaData

    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(DALResources), ErrorMessageResourceName = "ErrorRequiredField")]
    [StringLength(50, ErrorMessageResourceType = typeof(DALResources), ErrorMessageResourceName = "ErrorLessThanCharacters")]
    [Display(ResourceType = typeof(DALResources), Name = "ViewName")]
    [Remote("IsViewNameAvailable", "Validation")]
    string ViewName  get; set; 

    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(DALResources), ErrorMessageResourceName = "ErrorRequiredField")]
    [StringLength(400, ErrorMessageResourceType = typeof(DALResources), ErrorMessageResourceName = "ErrorLessThanCharacters")]
    [Display(ResourceType = typeof(DALResources), Name = "ViewPath")]
    [Remote("IsViewPathAvailable", "Validation")]
    string ViewPath  get; set; 

    [Display(ResourceType = typeof(DALResources), Name = "ViewContent")]
    string ViewContent  get; set; 

我遇到问题的部分是下面定义的 [Remote] 验证属性:

[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class ValidationController : Controller

    private FRCMSV1Entities db = new FRCMSV1Entities();

    public JsonResult IsViewNameAvailable(View view)
    
        bool isViewNameInvalid = db.View.Any(v => v.ViewName == view.ViewName && v.Id != view.Id);

        if (!isViewNameInvalid)
            return Json(true, JsonRequestBehavior.AllowGet);

        string suggestedViewName = string.Format(UI_Prototype_MVC_Resources.ErrorViewAlreadyExists, view.ViewName);

        for (int i = 1; i < 100; i++)
        
            string altViewName = view.ViewName + i.ToString();
            bool doesAltViewNameExist = db.View.Any(v => v.ViewName == altViewName);
            if (!doesAltViewNameExist)
            
                suggestedViewName = string.Format(UI_Prototype_MVC_Resources.ErrorViewNotAvailableTry, view.ViewName, altViewName);
                break;
            
        
        return Json(suggestedViewName, JsonRequestBehavior.AllowGet);
    

    public JsonResult IsViewPathAvailable(View view)
    
        bool doesViewPathExist = db.View.Any(v => v.ViewPath == view.ViewPath && v.Id != view.Id);

        if (!doesViewPathExist)
            return Json(true, JsonRequestBehavior.AllowGet);

        string suggestedViewPath = string.Format(UI_Prototype_MVC_Resources.ErrorViewAlreadyExists, view.ViewPath);

        for (int i = 1; i < 100; i++)
        
            string altViewPath = view.ViewPath + i.ToString();
            bool doesAltViewPathExist = db.View.Any(v => v.ViewPath == altViewPath);
            if (!doesAltViewPathExist)
            
                suggestedViewPath = string.Format(UI_Prototype_MVC_Resources.ErrorViewNotAvailableTry, view.ViewPath, altViewPath);
                break;
            
        
        return Json(suggestedViewPath, JsonRequestBehavior.AllowGet);
    

问题是,验证需要在创建和编辑时都进行相同的工作。它只需要对编辑进行额外检查以确保我们仍然引用相同的记录,如果是这样,则无需显示验证消息,因为没有任何问题。

我的问题是: 1.我如何让它按预期工作。 2.我可以看到这两种方法几乎相同,这违反了DRY原则。我怎样才能使它更通用并简化它。但是第一个问题确实是我想回答的问题,因为重构不起作用的东西是没有意义的。

如需更多信息,上述代码也是对以下链接代码的编辑:

https://msdn.microsoft.com/en-us/library/gg508808(VS.98).aspx

感谢您的帮助。

【问题讨论】:

【参考方案1】:

您需要添加一个参数以将模型的 ID 属性传递为AdditionalFields。假设它的int Id,那么

[Remote("IsViewPathAvailable", "Validation", AdditionalFields = "Id")]
public string ViewName  get; set; 

方法应该是

public JsonResult IsViewNameAvailable(string viewName, int? id)

请注意,在Edit 视图中,您包含Id 属性的隐藏输入,因此其值将由jquery.validate 远程函数回传。

然后您可以检查id 参数是null(即它是新的)还是有一个值(它是现有的)并调整查询以适应。

bool isViewNameInvalid;
if (id.HasValue)

    isViewNameInvalid = db.View.Any(v => v.ViewName == viewName && v.Id != id);

else

    isViewNameInvalid = db.View.Any(v => v.ViewName == ViewName);

当前发生的情况是 Remote 仅发布 ViewName 属性的值,并且由于您的参数是模型,它使用默认的 id 值 (0) 和您的查询被翻译成Any(v =&gt; v.ViewName == viewName &amp;&amp; v.Id != 0);

我还建议使用视图模型而不是您的 partial class

旁注:从生成suggestedViewName 的代码来看,您期望很多ViewName 具有相同的值,这意味着您可能会在for 循环内部进行大量数据库调用。您可以考虑使用 linq .StartsWith() 查询来获取以您的 ViewName 值开头的所有记录,然后检查循环中的内存集。

【讨论】:

您的回答有效。再次感谢你的帮助。我将在单独的 cmets 中解决您的每个建议。使用 Remote 的建议确实减少了控制器中的代码。我唯一不喜欢的是这是一个验证问题,但它被设置为控制器。我可能会尝试找到另一种方法来做到这一点,并希望得到任何建议。 视图模型。我确实会使用 ViewModel,但我现在只是对此进行原型设计,并想看看我是否可以从中获得一个功能系统。稍后我会将它分成几层并做所有这些好事情,这就是 ViewModel 真正变得必不可少的时候。但是,我同意,应该使用它们。 .StartsWith() - 这是一个很好的建议。我现在才实施。再次感谢斯蒂芬的所有帮助。

以上是关于C# MVC CMS - 自定义远程验证的主要内容,如果未能解决你的问题,请参考以下文章

Sitecore 8.0(构建 3)MVC WFFM 自定义验证器不工作

帝国CMS自定义页面的添加与目录式链接的处理

MVC 自定义验证:比较两个日期

MVC 自定义属性验证登录

Spring 3 MVC:使用自定义验证器显示验证消息

C# MVC Core 自定义 Bootstrap CSS