Проблема 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());

Не уверен, что это сработает, и я сам не пробовал, но это первое, что пришло в голову.

Если я получу шанс позже, я загляну в код связывателя модели по умолчанию и посмотрю, есть ли что-нибудь очевидное...

Другие вопросы по тегам