简体   繁体   English

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

[英]C# MVC CMS - Customising Remote Validation

At the link below I asked a question about how to ensure a field does not already contain the same value (for example when there is a unique constraint on a field which correctly causes C# to throw an exception when voilated). 在下面的链接中,我问了一个有关如何确保字段不包含相同值的问题(例如,当字段上存在唯一约束时,正确约束C#时会导致抛出异常)。 With the answer I received, it solved that problem but presented another. 用我收到的答案,它解决了这个问题,但提出了另一个问题。

Ensuring another record does not already contain the same value for a field 确保另一个记录尚未包含相同的值

The main issue I now have is that when I create a new View. 我现在遇到的主要问题是创建新视图时。 The validation works as expected. 验证按预期进行。 In brief - The system needs to check that the ViewName and ViewPath (route) are both unique so a search of the DB is required. 简而言之-系统需要检查ViewName和ViewPath(路由)是否都是唯一的,因此需要搜索数据库。

However, when I edit the view, the validation kicks in again (and it actually should not because obviously the view exists already because you are editing it). 但是,当我编辑视图时,验证会再次生效(实际上不应该这样做,因为显然是因为您正在编辑视图而已存在)。

My issue now is how do I customise the remote validation to work differently for edit vs create. 我现在的问题是如何自定义远程验证,以使其在编辑和创建时都可以不同地工作。 While we should not be able to edit the name of a view to match an existing view, we should also not be stopped from saving the current view simply because it is the same as the current view. 虽然我们不能编辑视图名称以匹配现有视图,但也不应仅仅因为它与当前视图相同而停止保存当前视图。

Below is my Model (the part that is not (hopefully) generated by a tool :-): 以下是我的模型(不是(希望)由工具生成的部分:-):

[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; }
}

The part I am having a problem with is the [Remote] validation attribute which is defined below: 我遇到问题的部分是[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);
    }
}

The problem is, the validation needs to work the same on both create and edit. 问题是,验证需要在创建和编辑上都相同。 It just needs to do an additional check on edit to ensure we are still referring to the same record and if so, then there is no need to show the validation message because there is nothing wrong. 它只需要对编辑进行额外的检查以确保我们仍在引用相同的记录,如果是这样,则因为没有错,所以无需显示验证消息。

My question is: 1. How do I get this to work as expected. 我的问题是:1.如何使它按预期工作。 2. I can see that both methods are pretty much identical, which violates the DRY principle. 2.我可以看到这两种方法几乎相同,这违反了DRY原理。 How can I make this more generic and simplify it. 我如何才能使它更通用并简化它。 However the first question is really the one I would like answered because there is no point in refactoring something that doesn't work. 但是,第一个问题确实是我想回答的问题,因为重构没有用的东西没有意义。

For more information, the above code is also an edit of the code at the following link: 有关更多信息,以上代码也是以下链接中代码的编辑:

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

Thanks for any help. 谢谢你的帮助。

You need to add a parameter to pass the ID property of the model as AdditionalFields . 您需要添加一个参数以将模型的ID属性作为AdditionalFields传递。 Assuming its int Id , then 假设其int Id ,则

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

and the the method should be 方法应该是

public JsonResult IsViewNameAvailable(string viewName, int? id)

Note that in the Edit view, you include a hidden input for the Id property, so its value will be posted back by the jquery.validate remote function. 请注意,在“ Edit视图中,您为Id属性包括了一个隐藏的输入,因此其值将由jquery.validate远程函数发布回去。

You can then check if the id parameter is null (ie it's new) or has a value (it's existing) and adjust the queries to suit. 然后,您可以检查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);
}

What is currently happening is that the Remote is only posting the value of the ViewName property, and because your parameter is the model, it is initialized with the default id value ( 0 ) and your query is translated to Any(v => v.ViewName == viewName && v.Id != 0); 当前正在发生的事情是, Remote仅发布ViewName属性的值,并且由于您的参数是模型,因此将使用默认的id值( 0 )对其进行初始化,并且您的查询将转换为Any(v => v.ViewName == viewName && v.Id != 0);

I also recommend using a view model rather that your partial class 我还建议使用视图模型,而不是partial class

Side note: from the code that generates suggestedViewName , your expecting a lot of ViewName with the same value, meaning your possibly making numerous database calls inside you for loop. 附注:从生成的代码suggestedViewName ,你的期望很高的ViewName以相同的值,这意味着在你里面你可能使众多数据库调用for循环。 You could consider using linq .StartsWith() query to get all the records that start with your ViewName value, and then check the in-memory set in your loop. 您可以考虑使用linq .StartsWith()查询来获取所有以ViewName值开头的记录,然后检查循环中设置的内存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM