Проблема ASP.NET MVC с использованием объектов отражения, созданных с помощью подшивки модели по умолчанию
У меня странная проблема в ASP.NET MVC, когда объекты не обновляются с помощью UpdateModel, когда передается formCollection. UpdateModel не работает должным образом, когда обновляемый объект создается с помощью отражения.
Сценарий: у меня есть приложение, которое имеет около 50 справочных таблиц, каждая из которых включает в себя одну и ту же схему, включая типичные поля, такие как id, title, description, isactive и creationon. Вместо того, чтобы создавать 50 представлений, я хотел иметь одно представление, которое могло бы отображать данные из всех справочных таблиц. Я создал интерфейс под названием IReferenceEntity и реализовал его в каждом из POCO, представляющих мои таблицы поиска.
Используя этот интерфейс, я могу легко заполнить представление записью из таблицы поиска. (Я передаю предметы на просмотр через следующее.)
System.Web.Mvc.ViewPage<MyNamespece.IReferenceEntity>
От базы данных до представления, все работает отлично.
Однако, когда я пытаюсь обновить модель на почте, я сталкиваюсь с некоторыми проблемами.
Если я явно объявляю ссылку на объект, как показано ниже, все работает отлично, и значения моего объекта обновляются значениями из моей формы. Следовательно, я могу затем обновить базу данных.
AccountStatus a = new AccountStatus();
UpdateModel(a, formCollection.ToValueProvider());
К сожалению, жесткое кодирование типа объекта полностью устранит причину использования интерфейса.
(Основная цель приложения - иметь возможность динамически добавлять новые таблицы, например таблицы поиска, без необходимости делать что-то "особенное". Это достигается путем отражения загруженных сборок и определения местоположения любых классов, которые реализуют определенный интерфейс или базовый класс.)
Моя стратегия состоит в том, чтобы определить конкретный тип объекта при обратной передаче, а затем создать экземпляр типа с помощью отражения. (Механизм, который я использую для определения типа, несколько примитивен. Я включаю его в качестве скрытого поля в форме. Лучшие идеи приветствуются.)
Когда я создаю экземпляр объекта, используя отражение с помощью любого из следующих методов, ни один из объектов не обновляется UpdateModel.
Type t = {Magically Determined Type}
object b = Activator.CreatorInstance(t);
UpdateModel(b, formCollection.ToValueProvider());
Type t = {Magically Determined Type}
var c = Activator.CreatorInstance(t);
UpdateModel(c, formCollection.ToValueProvider());
Type t = {Magically Determined Type}
IReferenceEntity d = Activator.CreatorInstance(t);
UpdateModel(d, formCollection.ToValueProvider());
Примечание. Я проверил, что все объекты, создаваемые с помощью повторного выбора, имеют правильный тип.
У кого-нибудь есть идеи, почему это может происходить? Я несколько озадачен.
Если бы я был действительно "трудным", я мог бы создать объект фабрики, который бы во многих случаях создавал экземпляры любого из этих ссылочных объектов / объектов поиска. Однако это нарушит способность приложения разрешать добавление и обнаружение новых справочных таблиц, и это не совсем так чисто.
Кроме того, я мог бы попытаться наследовать реальный базовый класс ReferenceEntity, а не интерфейс, но сомневаюсь, будет ли это иметь какое-то значение. Проблема, по-видимому, связана с использованием объектов отражения, созданных в связывателе моделей.
Любая помощь приветствуется.
Энтони
3 ответа
Авги ответил на это на форумах ASP.NET. Он работал только с несколькими незначительными изменениями. Спасибо, Оги.
Проблема заключается в том, что методы [Try]UpdateModel позволяют указывать тип модели только с помощью универсального параметра, поэтому они не позволяют указывать динамический тип модели. Я создал билет для этого.
Вы можете увидеть реализацию метода TryModelUpdate здесь. Так что не сложно написать собственную перегрузку:
public virtual bool TryUpdateModelDynamic<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class
{
if (model == null)
{
throw new ArgumentNullException("model");
}
if (valueProvider == null)
{
throw new ArgumentNullException("valueProvider");
}
//Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
IModelBinder binder = Binders.GetBinder( /*typeof(TModel)*/model.GetType());
ModelBindingContext bindingContext = new ModelBindingContext()
{
Model = model,
ModelName = prefix,
ModelState = ModelState,
//ModelType = typeof(TModel), // old
ModelType = model.GetType(),
// new
//PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
binder.BindModel(ControllerContext, bindingContext);
return ModelState.IsValid;
}
Содержит ли ваш IReferenceEntity установщики свойств и получателей? Я бы подумал, что последний пример будет работать, если в интерфейсе есть установщики свойств, хотя для его компиляции вам нужно будет привести его к типу.
Type t = {Magically Determined Type}
IReferenceEntity d = Activator.CreatorInstance(t) as IReferenceEntity;
UpdateModel(d, formCollection.ToValueProvider());
Обычно причина, по которой он не устанавливает свойство в классе, заключается в том, что он не может найти метод открытого сеттера, доступный для использования через отражение.
Просто быстрая "другая вещь, чтобы попробовать":
UpdateModel(d as IReferenceEntity, formCollection.ToValueProvider());
Не уверен, что это сработает, и я сам не пробовал, но это первое, что пришло в голову.
Если я получу шанс позже, я загляну в код связывателя модели по умолчанию и посмотрю, есть ли что-нибудь очевидное...