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

C# MVC CMS - Customising Remote Validation

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

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

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

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

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

[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 原则。我怎样才能使它更通用并简化它。然而,第一个问题确实是我想回答的问题,因为重构不起作用的东西是没有意义的。

更多信息,以上代码也是对以下link处代码的编辑:

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

感谢您的帮助。

您需要添加一个参数以将模型的 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 => v.ViewName == viewName && v.Id != 0);

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

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