ASP.NET MVC:избегайте тесной связи при привязке записи формы к параметру
-
22-08-2019 - |
Вопрос
Допустим, у меня есть интерфейс, подобный:
interface IThing {
int Id { get; set; }
string Title { get; set; }
}
И в ASP.NET MVC у меня есть форма, которая отправляет контроллеру действие, подобное этому:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NewThing([Bind(Exclude = "Id")] SimpleThing thing) {
// code to validate and persist the thing can go here
}
Где Что - то простое это конкретный класс, который едва ли реализует Все.
Однако я бы хотел, чтобы все мои методы работали с интерфейсом.У меня есть сборка данных, которая использует NHiberate и свой собственный Все реализация (давайте назовем это Реальное что - то).Я не могу передать ему SimpleThing, потому что он будет жаловаться на "неизвестный объект".
Есть ли у кого-нибудь какие-нибудь идеи о более чистом способе сделать это?Я думал о чем-то вроде использования заводского класса.Но как бы мне заставить MVC form binder использовать его?
Спасибо!
Решение 3
Здесь было несколько хороших предложений, и я действительно придумал решение, которое работает.Однако в итоге у меня получилось кое-что в целом.Я только что создал модели, специфичные для данных формы, которые я публиковал, и использовал привязку модели по умолчанию.
Намного проще, и это позволяет мне собирать данные, которые не являются частью моей модели предметной области (т.Е.например, поле "комментарии").
Другие советы
Вы можете использовать пользовательские модельные связующие.Однако статья о msdn совершенно бесполезна.Так что лучше воспользоваться поиском и найти что-нибудь получше.Доступно множество статей.
Я придумал два подхода к этому.
Первым было добавление кода в мой класс репозитория NHibernate для перевода простого типа POCO, используемого контроллером MVC (Что - то простое) к типу объекта , который хотел использовать NHibernate (Реальное что - то):
/// <summary>
/// A NHibernate generic repository. Provides base of common
/// methods to retrieve and update data.
/// </summary>
/// <typeparam name="T">The base type to expose
/// repository methods for.</typeparam>
/// <typeparam name="K">The concrete type used by NHibernate</typeparam>
public class NHRepositoryBase<T, K>
: IRepository<T>
where T : class
where K : T, new()
{
// repository methods ...
/// <summary>
/// Return T item as a type of K, converting it if necessary
/// </summary>
protected static K GetKnownEntity(T item) {
if (typeof(T) != typeof(K)) {
K knownEntity = new K();
foreach (var prop in typeof(T).GetProperties()) {
object value = prop.GetValue(item, null);
prop.SetValue(knownEntity, value, null);
}
return knownEntity;
} else {
return (K)item;
}
}
Таким образом, любой метод в репозитории может вызвать GetKnownEntity(T item), и он скопирует свойства передаваемого вами элемента в тип, который требуется NHibernate.Очевидно, это показалось немного неуклюжим, поэтому я заглянул в пользовательские папки для привязки моделей.
Во втором подходе я создал пользовательскую модель binder, подобную этой:
public class FactoryModelBinder<T>
: DefaultModelBinder
where T : new()
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type modelType) {
return new T();
}
}
Затем я зарегистрировал это в Global.asax.cs с помощью:
ModelBinders.Binders.Add(typeof(IThing),
new FactoryModelBinder<RealThing>());
И это прекрасно работает с действием контроллера, которое выглядит следующим образом:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NewThing([Bind(Exclude = "Id")] IThing thing) {
// code to process the thing goes here
}
Мне нравится второй подход, но большая часть моих материалов по внедрению зависимостей находится в классе Controller.Мне не нравится добавлять все эти сопоставления ModelBinder в Global.asax.cs.
Это не прямой ответ на ваш вопрос.
Мы используем несколько иной подход для решения той же проблемы, что и у вас.Наши контроллеры принимают DTO, которые соответствуют постоянному объекту поле за полем.Затем мы используем Автоматический преобразователь чтобы создать постоянные объекты, которые будут отправлены в базу данных.Это устраняет ненужные интерфейсы и блокирует общедоступный API (означает, что переименование поля объекта persistence не нарушит наш клиентский код).