none
Asp.Net MVC : Получение списка свойств на основе привязки модели. RRS feed

  • Вопрос

  • Решил написать обобщенную функцию частичного обновления данных для POCO классов в EF Codefirst. Т.е, чтобы обновлялись только указанные, причем без загрузки исходных значений из базы. Передается объект с данными из HTML формы, заполненными стандартным средством привязки модели и массив имен свойств объекта: 

    public void AddOrUpdate<T>(T entity, IEnumerable<string> properties = null) where T : class, IEntity, new()
            {
                if (entity.Id == 0)
                {
                    Set<T>().Add(entity);
                    return;
                }
    
                if (properties == null)
                {
                    throw new ArgumentException("Expected properties for update");
                }
    
                Set<T>().Attach(entity);
    
                var entry = Entry(entity);
    
                foreach (var property in properties)
                {
                    //  У NotMapped нельзя ставить "IsModified"
                    var attributes = entity
                        .GetType()
                        .GetProperty(property)
                        .GetCustomAttributes(typeof(NotMappedAttribute), false);
    
                    if ( attributes.Length > 0)
                    {
                        continue;
                    }
                    entry.Property(property).IsModified = true;
                }
            }
    

    Теперь вопрос - как корректно получить массив свойств? Через FormCollection не подходит - иногда бывает, что привязывается подствойство, т.е ключи FormCollection содержат значения вида Article.Id, Article.Name, а мне нужны только Name, Id.

    Если модель привязывается через параметр действия:

    public ActionResult Edit(Article updatedArticle)
    

    то в ModelState.Keys содержатся данные формы только касательно этого article, но тоже с префиксом - именем переменной. Т.е "updatedArticle.Id", "updatedArticle.Name" и т.д.

    Поэтому сейчас не придумал ничего лучше, чем написать г... грязный код:

    _articles.AddOrUpdate(article, ModelState.Keys.Select(s => s.Replace("updatedArticle.", "")));
    


    Как правильно получить "чистые" названия свойств при успешной привязке модели?

     

    12 ноября 2011 г. 5:39

Все ответы

  • 1) Почему Вы не пользуетесь возможностями ModelBinder-а? Именно он определяет как связываются параметры действия с содержимым HTTP запроса. Вот ссылка на статью, где можно это почитать http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx

    2) На мой взгляд вы допускаете серьезный архитектурный просчет - смешиваете код сохранения сущностей в бд и код работающий с HTTP запросом. Подумайте о реализации своего ModelBinder-а.

    15 ноября 2011 г. 6:43
  • Меня вполне устраивает DefaultModelBinder. Параметры действия привязываются корректно.

    Вопрос в том, как избежать паттерна обновления сущностей такого вида:

     

    MyEntity entity = _myDbContext.Find(id);
    
    UpdateModel(entity);
    
    _myDbContext.SaveChanges();
    


    Если у меня на форме редактируются 2 поля из модели, я не хочу для обновления извлекать из базы все данные модели, среди которых может быть несколько полей типа "varchar(max)".

    Кстати, этот паттерн вполне так смешивает код сохранения сущностей и работы с запросом. Точнее с данными формы. Как этого избежать, я не знаю. Entity Framework не может волшебным образом определить, какие свойства объекта нужно обновить, если обновлялся объект, не отслеживаемый контекстом.

    Мне нужно указать коду сохранения в репозитории, или как-там-еще-сделана-архитектура, имена свойств модели, которые должны обновиться. 

    15 ноября 2011 г. 7:06
  • Если Вы хотите обновлять только часть полей сущности, правильнее было бы сделать специальный ViewModel класс и работать уже с ним. Вот как пишут на хабре http://habrahabr.ru/blogs/aspnet_mvc/116298/. Код сохранения и обновления хранимой сущности из viewmodel можно написать в обобщенном варианте. Тогда вопрос каким именно образом приходят данные из HTTP запроса не должен вставать.
    15 ноября 2011 г. 11:45
  • Если Вы хотите обновлять только часть полей сущности, правильнее было бы сделать специальный ViewModel класс и работать уже с ним.

    Это хороший вариант для генерации представлений, но плохой для редактирования полей в форме.

    У меня есть класс сущности, у которого свойства помечены аттрибутами [Required], [StringLength], [Regexp]. Это часть доменной модели.

    Теперь мне надо одну и ту же сущность редактировать в разных местах с разным набором редактируемых свойств. Если я напишу для каждого случая ArticleEditContentViewModel, ArticleEditVisibilityViewMiodel, где перечислю только редактируемые свойства, как мне перенести на нужные свойства нужные аттрибуты? С помощью Ctrl+C/Ctrl+V?

    Я предпочитаю, чтобы при редактировании использовался именно класс из домена, чтобы имена свойств и валидация была определена в одном месте.

     

    Вообще, это вопросы к теме не относящиеся. Вопрос был в том, можно ли получить в контроллере список свойств, использованных в привязке модели. Да - хотелось бы узнать как, нет - буду использовать указанный выше паттерн. При чем тут вопросы архитектуры?

    ---

    Хотя, вариант с EditorViewModel хорош, особенно с использованием AutoMapper или другого маппера. Но вопрос тот же. Как передать аттрибуты валидации, описания и т.д. из доменной модели в ViewModel? Насколько я знаю, AutoMapper этот вопрос не решает. Если такой способ есть, было бы отлично, я бы первый полностью перешел на ViewModel при редактировании форм.


    • Изменено J.W.Harding 15 ноября 2011 г. 14:34
    15 ноября 2011 г. 13:26
  • Уважаемый пользователь!

    В вашей теме отсутствует активность в течение последних 5 дней. При отсутствии каких-либо действий в течение 2 последующих дней, тема будет переведена в разряд обсуждений. Вы можете возобновить дискуссию, просто оставив сообщение в данной теме


    Для связи [mail]
    14 декабря 2011 г. 5:22
    Модератор
  • Вот прямо из коробки - никак.

    Используемый Вами вариант, можно сказать, самый быстрый в реализации.

    Если хотите "более универсальное/красивое" решение, Вам действительно потребуется исследовать механизм работы DefaultModelBinder и влиять на него. А если не привязчик, то базовый контроллер переопределять, и там производить самостоятельную предобработку данных. Так или иначе потребуется задействовать механизмы рефлексии и применение кастомных атрибутов

    4 сентября 2017 г. 19:52