Часть 19 – Менеджер провайдеров метаданных

В создаваемом примере веб-приложения существуют классы Модели, которые требуют различного подхода для создания метаданных. И если UserProfileModel использует атрибуты DataAnnotations и провайдера по умолчанию, то для PaymentModel его необходимо разработать.

Ядро ASP.NET MVC 3 предоставляет возможность указать только одного провайдера метаданных в качестве текущего. Устанавливать нужный экземпляр самостоятельно каждый раз при создании Модели не очень хороший подход. Возрастает вероятность допустить ошибку. Поэтому разработаем менеджера, который будет выбирать нужный вариант для каждого класса Модели.

Давайте сразу приступим к делу. В папке Models создадим папку MetadataProviders. В ней будут расположены все создаваемые в этой части классы.

Интерфейс для провайдеров

Разработаем интерфейс, с помощью которого менеджер будет взаимодействовать с провайдерами. В него войдут аналоги трех абстрактных методов получения метаданных Модели, которые описаны в классе ModelMetadataProvider: GetMetadataForProperties(), GetMetadataForProperty(), GetMetadataForType().

Также потребуется еще один метод, с помощью которого для текущей Модели можно будет определить соответствующего провайдера. Назовем его IsAbleToProvideMetadata(). В качестве параметра он будет получать тип, для свойства которого необходимо создать метаданные. В результате, метод должен вернуть значение true, если создание экземпляра ModelMetadata возможно, или false в противном случае.

Разместим созданный интерфейс в файле IModelMetadataProvider.cs.

namespace MVCDemo.Models.MetadataProviders
{
    using System;
    using System.Collections.Generic;
    using System.Web.Mvc;

    public interface IModelMetadataProvider
    {
        bool IsAbleToProvideMetadata(Type containerType);

        IEnumerable<ModelMetadata> GetMetadataForProperties(
            object container, Type containerType);

        ModelMetadata GetMetadataForProperty(
            Func<object> modelAccessor, Type containerType, string propertyName);

        ModelMetadata GetMetadataForType(
            Func<object> modelAccessor, Type modelType);
    }
}

Разработка менеджера провайдеров

Приступим к созданию менеджера. Он будет являться наследником ModelMetadataProvider и использоваться в веб-приложении в качестве текущего провайдера. Стоит отметить следующие моменты:

  • Свойство Providers используется для хранения списка провайдеров.
  • В конструкторе потребуем указание провайдера метаданных по умолчанию. Он будет использоваться в том случае, если ни один из провайдеров не взял на себя работу с текущей Моделью. При этом, как и ядро ASP.NET MVC 3, будем считать значение null аналогом указания класса EmptyModelMetadataProvider.
  • При обращении к методам, унаследованным от базового класса ModelMetadataProvider, с помощью метода IsAbleToProvideMetadata() будет осуществляться выбор подходящего экземпляра провайдера и переадресация запроса к нему.

Класс менеджера разместим в файле ModelMetadataProvidersManager.cs:

namespace MVCDemo.Models.MetadataProviders
{
    using System;
    using System.Collections.Generic;
    using System.Web.Mvc;

    public class ModelMetadataProvidersManager : ModelMetadataProvider
    {
        private readonly ModelMetadataProvider _defaultProvider;

        public List<IModelMetadataProvider> Providers { get; private set; }

        public ModelMetadataProvidersManager(ModelMetadataProvider defaultProvider)
        {
            this.Providers = new List<IModelMetadataProvider>();

            this._defaultProvider = 
                (defaultProvider != null) ?
                defaultProvider : new EmptyModelMetadataProvider();
        }

        #region ModelMetadataProvider methods

        public override IEnumerable<ModelMetadata> GetMetadataForProperties(
            object container, Type containerType)
        {
            foreach (var metadataProvider in this.Providers) {
                if (metadataProvider.IsAbleToProvideMetadata(containerType)) {
                    return metadataProvider.GetMetadataForProperties(container, containerType);
                }
            }

            return this._defaultProvider.GetMetadataForProperties(container, containerType);
        }

        public override ModelMetadata GetMetadataForProperty(
            Func<object> modelAccessor, Type containerType, string propertyName)
        {
            foreach (var metadataProvider in this.Providers) {
                if (metadataProvider.IsAbleToProvideMetadata(containerType)) {
                    return metadataProvider.GetMetadataForProperty(
                        modelAccessor, containerType, propertyName);
                }
            }

            return this._defaultProvider.GetMetadataForProperty(
                modelAccessor, containerType, propertyName);
        }

        public override ModelMetadata GetMetadataForType(
            Func<object> modelAccessor, Type modelType)
        {
            foreach (var metadataProvider in this.Providers) {
                if (metadataProvider.IsAbleToProvideMetadata(modelType)) {
                    return metadataProvider.GetMetadataForType(modelAccessor, modelType);
                }
            }

            return this._defaultProvider.GetMetadataForType(modelAccessor, modelType);
        }

        #endregion
    }
}

Задействуем созданный менеджер метаданных в проекте (файл Global.asax):

namespace MVCDemo
{
    using System.Web.Mvc;
    using System.Web.Routing;
    using MVCDemo.Models.MetadataProviders;

    public class MvcApplication : System.Web.HttpApplication
    {
        .........

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            var manager = new ModelMetadataProvidersManager(ModelMetadataProviders.Current);
            ModelMetadataProviders.Current = manager;
        }
    }
}

Обратите внимание, что в конструктор ModelMetadataProvidersManager передается значение ModelMetadataProviders.Current. Таким образом по умолчанию для ASP.NET MVC 3 будет использоваться экземпляр DataAnnotationsModelMetadataProvider.

Если сейчас запустить веб-приложение, то не будет видно никаких визуальных изменений. Ведь всё ещё используется только один провайдер метаданных Модели. Поэтому давайте приступим к разработке реализации варианта, использующего данные из XML файлов.


Исходный код проекта (C#, Visual Studio 2010): MVCDemo-Part19.zip (498 Kb)

Комментарии (3) -

Паштет 06.04.2011 15:35:22

            if (defaultProvider == null) {
                this._defaultProvider = new EmptyModelMetadataProvider();
            }

            this._defaultProvider = defaultProvider;

если defaultProvider == null присваеваем экземпляр класса EmptyModelMetadataProvider потом перетираем это значение на null. Верно?

Спасибо что заметили. Поправил. Должно быть

defaultProvider = new EmptyModelMetadataProvider();

т.е. null считаем за EmptyModelMetadataProvider

nice

Pingbacks and trackbacks (3)+

Добавить комментарий