Пользовательская привязка модели, состояние модели и аннотации данных - PullRequest
9 голосов
/ 28 апреля 2011

У меня есть несколько вопросов относительно привязки пользовательской модели, состояния модели и аннотаций данных.

1) Излишне ли выполнять проверку в связывателе пользовательской модели, если у меня есть аннотации данных в моей модели, потому что, как я думал, смысл аннотаций данных был.

2) Почему мой контроллер считает состояние модели допустимым, даже если оно отсутствует, в основном я делаю свойство Name пустым или слишком коротким.

3) Можно ли считать пользовательские связующие модели методами конструктора, потому что это то, о чем они мне напоминают.

Сначала вот моя модель.

public class Projects
{     
    [Key]
    [Required]
    public Guid ProjectGuid { get; set; }

    [Required]
    public string AccountName { get; set; }

    [Required(ErrorMessage = "Project name required")]
    [StringLength(128, ErrorMessage = "Project name cannot exceed 128 characters")]
    [MinLength(3, ErrorMessage = "Project name must be at least 3 characters")]
    public string Name { get; set; }

    [Required]
    public long TotalTime { get; set; }
}

Затем я использую пользовательское связующее для модели, чтобы связать некоторые свойства модели. Пожалуйста, не берите в голову, что это быстро и грязно, просто пытаясь заставить это функционировать и затем рефакторинг это.

public class ProjectModelBinder : IModelBinder
{
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }
        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

        //
        // Is this redundant because of the data annotations?!?!
        //
        if (p.AccountName == null)
            bindingContext.ModelState.AddModelError("Name", "Name is required");

        if (p.AccountName.Length < 3)
            bindingContext.ModelState.AddModelError("Name", "Minimum length is 3 characters");

        if (p.AccountName.Length > 128)
            bindingContext.ModelState.AddModelError("Name", "Maximum length is 128 characters");

        return p;
    }
}

Теперь мой контроллер действия.

    [HttpPost]
    public ActionResult CreateProject([ModelBinder(typeof(ProjectModelBinder))]Project project)
    {
        //
        // For some reason the model state comes back as valid even when I force an error
        //
        if (!ModelState.IsValid)
            return Content(Boolean.FalseString);

        //_projectRepository.CreateProject(project);

        return Content(Boolean.TrueString);

    }

EDIT

Я нашел код в другом вопросе stackoverflow, но я не уверен, в какой момент я бы вставил следующие значения в это возможное решение.

Что я хочу добавить при создании нового объекта:

        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

Как мне получить приведенный выше код в нижеследующее (Возможное решение):

    public class ProjectModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(Project))
            {
                ModelBindingContext newBindingContext = new ModelBindingContext()
                {

                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new Project(), // construct a Project object,
                        typeof(Project)         // using the Project metadata
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider

                };

                // call the default model binder this new binding context
                return base.BindModel(controllerContext, newBindingContext);
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }

    }

}

1 Ответ

9 голосов
/ 28 апреля 2011

Вы обнаружите, что все работает намного проще, если вы унаследуете от DefaultModelBinder, переопределите метод BindModel, вызовете метод base.BindModel, а затем внесете изменения вручную (установка guid, имени учетной записи и общего времени).

1) Излишне подтверждать, как вы это сделали. Вы можете написать код, отражающий метаданные валидации во многом так же, как это делает по умолчанию, или просто удалить валидацию аннотаций данных, поскольку вы не используете их в связывателе модели.

2) Я не знаю, это кажется правильным, вы должны пройтись по коду и убедиться, что ваш пользовательский компоновщик заполняет все применимые правила.

3) Это точно фабрика, но не столько конструктор.


РЕДАКТИРОВАТЬ: вы не можете быть ближе к решению, просто установите нужные свойства в функции фабрики моделей

public class ProjectModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(Project))
        {
            ModelBindingContext newBindingContext = new ModelBindingContext()
            {

                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                    () => new Project()  // construct a Project object
                    {
                        ProjectGuid = System.Guid.NewGuid(),
                        AccountName = controllerContext.HttpContext.User.Identity.Name,
                        // don't set name, thats the default binder's job
                        TotalTime = 0,
                    }, 
                    typeof(Project)         // using the Project metadata
                ),
                ModelState = bindingContext.ModelState,
                ValueProvider = bindingContext.ValueProvider

            };

            // call the default model binder this new binding context
            return base.BindModel(controllerContext, newBindingContext);
        }
        else
        {
            return base.BindModel(controllerContext, bindingContext);
        }
    }

}

Или вы можете поочередно переопределить метод CreateModel:

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
{
    if (modelType == typeof(Project))
    {
        Project model = new Project()
        {
            ProjectGuid = System.Guid.NewGuid(),
            AccountName = controllerContext.HttpContext.User.Identity.Name,
            // don't set name, thats the default binder's job
            TotalTime = 0,
        };

        return model;
    }

    throw new NotSupportedException("You can only use the ProjectModelBinder on parameters of type Project.");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...