5

У меня возникли проблемы с попыткой подумать, что лучший способ - воссоздать объект базы данных в контроллере Action.Объекты базы данных Modelbinding в ASPNET MVC

Я хочу использовать ModelBinders, поэтому в своем действии у меня есть доступ к объекту через параметр, вместо необходимости повторять код для получения объекта из базы данных на основе параметра идентификатора. Поэтому я думал о том, что ModelBinder выполняет вызов уровня данных для получения исходного объекта (или создает новый, если он не существует в базе данных), а затем связывает любые свойства с объектом базы данных, чтобы обновить его. Однако я читал, что ModelBinders не должны делать запросы к базе данных (первый комментарий этого article).

Если ModelBinder не должен выполнять запрос базы данных (так что просто используйте DefaultModelBinder), то что относительно объектов базы данных, которые имеют свойства, которые являются другими объектами db? Они никогда не будут назначены.

Сохранение объекта после того, как пользователь отредактировал его (1 или 2 свойства доступны для редактирования) объект ModelBinded будет отсутствовать данными, поэтому сохранение его как это приведет к тому, что данные в базе данных будут перезаписаны с недопустимыми значениями , или NOT-NULL сбой.

Итак, каков наилучший способ получить объект в действии контроллера из базы данных, связанной с данными формы, отправленными назад из представления?

Обратите внимание на использование NHibernate.

+0

Я нахожусь в точно такой же ситуации, как и вы (также используя NH). Я реализовал модельное связующее, чтобы избежать дублирования кода. Каков ваш вывод о доступе к БД из связующего? –

+0

В итоге я встал на сторону доступа к базе данных в связующих. Мое модели просмотра теперь разделены на мои модели домена. Существуют проблемы со связыванием непосредственно с вашими моделями домена (nhibernate очистит связанный объект в конце запроса с помощью, возможно, недопустимых данных, и если вы не создадите новый сеанс, повторно получая объект, который вы используете, вы в конечном итоге используете недействительный связанный объект в течение всего запроса). –

+0

Спасибо, что сообщили мне. –

ответ

4

Я получаю объект модели из базы данных, а затем использую UpdateModel (или TryUpdateModel) объекта для обновления значений из параметров формы.

public ActionResult Update(int id) 
{ 
    DataContext dc = new DataContext(); 
    MyModel model = dc.MyModels.Where(m => m.ID == id).SingleOrDefault(); 

    string[] whitelist = new string[] { "Name", "Property1", "Property2" }; 

    if (!TryUpdateModel(model, whitelist)) { 
     ... model error handling... 
     return View("Edit"); 
    } 

    ViewData.Model = model; 

    return View("Show"); 
} 
+1

Я обнаружил, что если у меня есть свойство на моей модели, которое назначено на какое-то значение и которое не существует в FormCollection, то при выполнении TryUpdateModel свойство имеет значение null, даже если я включаю/исключаю свойства. Таким образом, в основном TryUpdateModel вызывает потерю данных, что плохо. –

+0

Он должен заменять только те свойства, которые указаны в белом списке, если вы указали один из них. Я знаю, что это работает, поскольку у меня есть некоторые виды редактирования, которые могут обновлять только некоторые значения, а не другие. Вы можете посмотреть фактический код для него по адресу http://www.codeplex.com/aspnet для проверки. – tvanfosson

2

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

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

public class ProductBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, 
     ModelBindingContext bindingContext, Type modelType) 
    { 
     if(modelType != typeof(Product)) 
      return null; 

     var form = controllerContext.HttpContext.Request.Form; 
     int id = Int32.Parse(form["Id"]); 
     if(id == 0) 
      return base.CreateModel(controllerContext, bindingContext, modelType); 

     IProductRepository repository = ServiceLocator.Resolve<IProductRepository>(); 

     return repository.Fetch(id);          
    }  
} 

Вы можете даже сделать эту работу для всех ваших лиц, если вы можете использовать базовый класс или интерфейс, который предоставляет идентификатор класса.

Вы должны установить это в Global.asax:

ModelBinders.Binders.Add(typeof(Product), new ProductBinder()); 

, а затем вы можете сделать это:

public ActionResult Save([Bind] Product product) 
{ 
    .... 

    _repository.Save(product); 
} 
+0

Это то, что я делал изначально, поскольку для меня имеет смысл делать это здесь. Но, прочитав в нескольких местах, что ModelBinder не должен запрашивать базу данных, мне заставило думать дважды. –

+1

Это немного вонючий, но если это заставляет вас чувствовать себя лучше, [ARDataBind] из проекта Castle делает именно это. –

+1

Попадание в базу данных - это ModeBinder - отличная идея. Кроме того, связующее выполняет BEFORE ActionFilters, поэтому имейте это в виду при доступе к базе данных из связующих. –

-1

Вы на самом деле не попасть в базу данных. Просто установить идентификатор объектов будет достаточно, чтобы установить связь вверх, но наблюдайте за своими каскадами. Убедитесь, что ваши настройки cascde не будут обновлять связанный объект, поскольку он очистит значения.

+2

Это действительно опасный совет. Вы должны всегда выбирать перед обновлением, потому что есть много областей, где вы, возможно, не обновляете все записи в базе данных, которые в конечном итоге будут перезаписаны пустыми значениями. –

+0

Очень верно, был глупый ответ, потому что я не правильно прочитал вопрос! –

0

Позвольте мне сначала заявить, что я не рекомендую обращаться к базе данных от ModelBinders, поскольку с точки зрения Separation Concern ModelBinders должна быть ответственна только за интерпретацию запроса клиента, очевидно, что базы данных нет.
Если вы не хотите повторить себя (DRY), использование репозиториев/услуги Однако, если и действительно хотите сделать это так, то

В global.asax.CS Регистрация пользовательских MyModelBinderProvider в MVC

ModelBinderProviders.BinderProviders.Add(new EntityModelBinderProvider 
{ 
    ConnectionString = "my connection string" 
)); 

Cunstruct обычай ModelBinderProvider содержать параметры базы данных

public class EntityBinderProvider: IModelBinderProvider 
{ 
    public string ConnectionString { get; set; } 

    public IModelBinder GetBinder(Type modelType) 
    { 
     if (Is known entity) 
      return new EntityBinder(ConnectionString); 
     else 
      return null; 
    } 
} 

Следуйте дальнейшим инструкциям из Бен Scheirman

Смежные вопросы