1

私のプロジェクトでは、以下のサンプルのように、別のクラスを使用するモデル クラスがあります。モデル内のプロパティの 1 つは、子オブジェクトのプロパティのいずれかに検証を依存しています。このサンプルでは、​​LastName プロパティは、Address.PostalCode プロパティの値に検証を依存しています。LastName プロパティを検証するカスタム検証属性を実装しましたが、うまく機能します。

public class User
{
    public static ValidationResult ValidateLastName(string lastName, ValidationContext context)
    {
        // Grab the model instance
        var user = context.ObjectInstance as User;
        if (user == null)
            throw new NullReferenceException();

        // Cross-property validation
        if (user.Address.postalCode.Length < 10000)
            return new ValidationResult("my LastName custom validation message.");

        return ValidationResult.Success;
    }

    [Display(Name = "Last name")]
    [CustomValidationAttribute(typeof(User), "ValidateLastName")]
    public string LastName { get; set; }

    [Display(Name = "First name")]
    public string FirstName { get; set; }

    [Display(Name = "Address:")]
    [CustomValidationAttribute(typeof(User), "ValidateAddress")]
    public AddressType Address { get; set; }
}

public class AddressType
{
    public string streetName = "";

    public string streetNumber = "";
    public string postalCode = "";
}

問題は、コントローラーで Address プロパティがビューから構築されず、常に null であることです。このサンプルでは、​​ビューで送信する内容に関係なく、user.Address は常に null です。ここにコントローラーコードがあります。

    [HttpPost]
    public ActionResult Create(User user)
    {
        if (ModelState.IsValid)
        {
            // creation code here
            return RedirectToAction("Index");
        }
        else
        {
            return View(user);
        }
    }

ビューは次のとおりです。

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.postalCode)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.postalCode)
            @Html.ValidationMessageFor(model => model.Address.postalCode)
        </div>

これを解決するために、次のようにビューのフィールドをモデルのプロパティにマップするカスタム ダミー バインダーを作成しました。

public class UserBinder : IModelBinder
{
    private string GetValue(ModelBindingContext bindingContext, string key)
    {
        var result = bindingContext.ValueProvider.GetValue(key);
        return (result == null) ? null : result.AttemptedValue;
    }

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        User instance = new User();
        instance.FirstName = GetValue(bindingContext, "FirstName"); //controllerContext.HttpContext.Request["FirstName"];
        instance.LastName = GetValue(bindingContext, "LastName"); //controllerContext.HttpContext.Request["LastName"];

        instance.Address = new AddressType();
        string streetName = controllerContext.HttpContext.Request["Address.streetName"];

        //ModelStateDictionary mState = bindingContext.ModelState;
        //mState.Add("LastName", new ModelState { });
        //mState.AddModelError("LastName", "There's an error.");

        instance.Address.streetName = streetName;
                    ...
        return instance;
    }

バインダーは正常に機能しますが、検証属性は機能しなくなりました。これよりもバインディングを行うためのより良い方法があるに違いないと思います。

このバインダーは、LastName を LastName に、Address.streetName を Address.streetName にマッピングしているだけです。この面倒なコードをすべて記述したり、カスタム検証メカニズムを壊したりせずに、これを達成する方法があるはずだと思います。

4

2 に答える 2

2

デフォルトのモデルバインディングが正しく機能するためには、パブリックフィールドの代わりにプロパティを使用する必要があります。

AddressTypeクラスを次のように変更します。

public class AddressType
{
    public string streetName { get; set; }
    public string streetNumber { get; set; }
    public string postalCode { get; set; }
}
于 2012-09-07T14:58:49.023 に答える
1

1つの解決策は、子クラスでパブリックフィールドの代わりにプロパティを使用することです-答えてくれたOdedに感謝します!

別の解決策は、コントローラーでTryValidateModelを呼び出すことです。これにより、バインダーが存在する場合でも検証コードが有効になります。

    [HttpPost]
    public ActionResult Create(User user)
    {
        if (TryValidateModel(user))
        {
            // creation code here
            return RedirectToAction("Index");
        }
        else
        {
            return View(user);
        }
    }
于 2012-09-07T15:21:20.720 に答える