МВВМ и объекты обслуживания - PullRequest
4 голосов
/ 08 июня 2011

В настоящее время мы разрабатываем систему с использованием WPF, которая будет взаимодействовать с другими системами с помощью веб-служб.

Мы стараемся создать как можно меньше слоев и отображений (сократить расходы, придерживаясь самого простого, что будетработа).

Мы будем использовать шаблон MVVM.

Таким образом, у нас должна быть модель представления.

Вопрос в том, можем ли мы использовать объекты, которые возвращаются из службы какобъекты модели, или мы должны отобразить их на объекты модели, определенные в клиенте?

Ответы [ 4 ]

3 голосов
/ 08 июня 2011

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

  1. В конечном итоге вы можете ввести требования к привязке данных ( INotifyPropertyChanged , INotifyCollectionChanged , IDataErrorInfo и т. Д.) В объекты данных службы.
  2. Становится все труднее развивать службу и приложение WPF независимо друг от друга.особенно в случае, когда служба может использоваться несколькими приложениями.

Вместо этого вам следует рассмотреть возможность использования Repository Pattern в вашей модели для инкапсуляции вашей коммуникации службы в качестве одного или несколькихрепозитории веб-сервисов.Репозитории веб-сервисов позволяют централизовать логику доступа к сервису и обеспечивают точку замены для модульных тестов, а также дают возможность кэшировать результаты предыдущих операций сервиса.

Репозитории действуют как мосты между данными иоперации, которые находятся в разных доменах.Хранилище отправляет соответствующие запросы к источнику данных, а затем сопоставляет результирующие наборы с бизнес-объектами, как правило, используя шаблон Data Mapper для преобразования между представлениями.

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

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

Пример интерфейса репозитория универсальных сервисов:

/// <summary>
/// Describes a service repository that separates the logic that retrieves, persists and maps data to the 
/// domain model from the business logic that acts on the domain model.
/// </summary>
/// <typeparam name="TChannel">The type of channel produced by the channel factory used by the repository.</typeparam>
/// <typeparam name="TMessage">The type of data contract to map to the domain entity of type <typeparamref name="T"/>.</typeparam>
/// <typeparam name="T">The type of domain entity mediated by the repository.</typeparam>
/// <typeparam name="TKey">The type of the key that uniquely identifies domain entities within the repository.</typeparam>
public interface IServiceRepository<TChannel, TMessage, T, TKey> : IRepository<T, TKey> 
  where T : class
{
  /// <summary>
  /// Occurs when the repository transitions from one state to another.
  /// </summary>
  event EventHandler<StateChangedEventArgs> StateChanged;

  /// <summary>
  /// Gets the configuration name used for the service endpoint.
  /// </summary>
  /// <value>
  /// The name of the endpoint in the application configuration file that is used 
  /// to create a channel to the service endpoint.
  /// </value>
  string EndpointConfigurationName
  {
      get;
  }

  /// <summary>
  /// Gets the current state of the service repository.
  /// </summary>
  /// <value>
  /// The current <see cref="CommunicationState"/> of the service repository.
  /// </value>
  CommunicationState State
  {
      get;
  }
}

Пример общего интерфейса репозитория:

/// <summary>
/// Describes a repository that separates the logic that retrieves, persists and maps data to the domain model 
/// from the business logic that acts on the domain model.
/// </summary>
/// <typeparam name="T">The type of domain entity mediated by the repository.</typeparam>
/// <typeparam name="TKey">The type of the key that uniquely identifies domain entities within the repository.</typeparam>
public interface IRepository<T, TKey> 
  where T : class
{
  /// <summary>
  /// Occurs when a repository action has been completed.
  /// </summary>
  event EventHandler<RepositoryActionCompletedEventArgs<T>> Completed;

  /// <summary>
  /// Occurs when a repository action fails to execute.
  /// </summary>
  event EventHandler<RepositoryActionFailedEventArgs<T>> Failed;

  /// <summary>
  /// Gets a value indicating if the repository has been disposed of.
  /// </summary>
  /// <value>
  /// <see langword="true" /> if the repository has been disposed of; otherwise, <see langword="false" />.
  /// </value>
  bool IsDisposed
  {
      get;
  }

  /// <summary>
  /// Adds a new <paramref name="entity"/> to the data source layer.
  /// </summary>
  /// <param name="entity">The entity of type <typeparamref name="T"/> to insert into the data source layer.</param>
  /// <param name="callback">
  /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is insert into the data source layer.
  /// </param>
  /// <returns>
  /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on.
  /// </returns>
  /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
  IRepository<T, TKey> Add(T entity, Action<T, Exception> callback = null);

  /// <summary>
  /// Retrieves all entities of type <typeparamref name="T"/> from the data source layer.
  /// </summary>
  /// <param name="callback">
  /// The optional <see langword="delegate"/> method that will be executed after all entities of type <typeparamref name="T"/> are retrieved from the data source layer.
  /// </param>
  /// <returns>
  /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on.
  /// </returns>
  IRepository<T, TKey> Get(Action<IEnumerable<T>, Exception> callback = null);

  /// <summary>
  /// Retrieves an entity of type <typeparamref name="T"/> from the data source layer that 
  /// matches the specified <paramref name="key"/>.
  /// </summary>
  /// <param name="key">The unique identifier of the entity of type <typeparamref name="T"/> to retrieve from the data source layer.</param>
  /// <param name="callback">
  /// The optional <see langword="delegate"/> method that will be executed after an entity of type <typeparamref name="T"/> that matches the specified <paramref name="key"/> is retrieved from the data source layer.
  /// </param>
  /// <returns>
  /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on.
  /// </returns>
  IRepository<T, TKey> Get(TKey key, Action<T, Exception> callback = null);

  /// <summary>
  /// Removes an existing <paramref name="entity"/> from the data source layer.
  /// </summary>
  /// <param name="entity">An entity of type <typeparamref name="T"/> to delete from the data source layer.</param>
  /// <param name="callback">
  /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is removed from the data source layer.
  /// </param>
  /// <returns>
  /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on.
  /// </returns>
  /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
  IRepository<T, TKey> Remove(T entity, Action<T, Exception> callback = null);

  /// <summary>
  /// Updates an existing <paramref name="entity"/> within the data source layer.
  /// </summary>
  /// <param name="entity">The entity of type <typeparamref name="T"/> to update within the data source layer.</param>
  /// <param name="callback">
  /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is updated within the data source layer.
  /// </param>
  /// <returns>
  /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on.
  /// </returns>
  /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
  IRepository<T, TKey> Update(T entity, Action<T, Exception> callback = null);
}
1 голос
/ 09 июня 2011

Я использую MVVM для приложения WPF, где нет сервисов, а модели извлекаются с использованием сервисов dll в памяти из того же решения. В такой среде, где сервис, выбирающий Модели, находится под нашим полным контролем, имеет смысл не иметь никакого дополнительного слоя. Таким образом, у нас есть только модель (которую извлекают сервисы dll в памяти), объекты ViewModel и виды.

Но если вы используете веб-сервисы, я предполагаю, что сервис будет развиваться независимо от его использования. Вы не захотите, чтобы ваше приложение ломалось или имело большие расходы на обслуживание, если ваши контракты на обслуживание изменятся завтра. Поэтому лучше всего заново создать свою собственную модель на стороне пользовательского интерфейса, используя модели WCF, а затем поработать над ней. Поэтому, если контракт изменится завтра, изменение, скорее всего, будет связано с логикой сопоставления модели WCF с вашей моделью пользовательского интерфейса, а не за ее пределами. Этот дополнительный слой стоит стоимости.

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

Не пытайтесь объединить ViewModel и Model в одно (путем реализации INotifyPropertyChange в самой модели). Я попробовал это, и вскоре это становится беспорядком по мере роста сложности вашего приложения. Кроме того, в этом случае ваша Модель будет расширяться со всеми реализациями команд. Они должны содержать только бизнес-логику и не брать на себя слишком много обязанностей, связанных с логикой пользовательского интерфейса.

1 голос
/ 08 июня 2011

Да, вы можете использовать сервисные объекты WCF на уровне вашей модели (как мы), хотя, как сказал @fantasticfix, если ваш объект сервера WCF изменяется, вы должны искать ссылки, чтобы исправить их.

Кроме того, если вы открываете всю Модель для представления и привязываетесь к свойствам Модели, вам необходимо убедиться, что объекты сервера WCF реализуют INotifyPropertyChanged.

1 голос
/ 08 июня 2011

Вам не нужно создавать собственный слой модели, НО: если сервис меняется, вам придется отражать эти изменения во всех слоях, которые ссылаются на модель. если вы сейчас создаете собственный слой модели, вы безопаснее, но это больше работает. как часто: больше работы спасет вас в конечном итоге намного больше работы в будущем. Если вы являетесь владельцем сервиса и уверены, что никогда не измените его (ха-ха), вам не нужен слой модели.

и, конечно, это зависит от того, соответствуют ли найденные объектами услуги вашим потребностям. если вы используете 1, 2 свойства от огромных объектов, возможно, это не очень хороший выбор ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...