Доска рекордов на WCF и RavenDB. Сервис

16.04.2014 at 19:29

Несколько раз на работе делали миниигры — пасхалки на новый год или еще какой праздник. Каждый раз единственное, что не успевали — таблица рекордов. Ее и буду делать. Доска будет представлять собой сервис с 2 методами — добавить результат и получить топ n результатов.

Для начала создадим интерфейс сервиса — контракт в терминах WCF

/// <summary>
/// Контракт сервиса доски рекордов.
/// </summary>
[ServiceContract]
public interface IScoreBoard
{
  /// <summary>
  /// Получить список лучших результатов.
  /// </summary>
  /// <param name="count">Количество результатов.</param>
  /// <returns>Список лучших результатов.</returns>
  [OperationContract]
  List<ScoreBoardResult> GetTopResults(int count);

  /// <summary>
  /// Добавить результат.
  /// </summary>
  /// <param name="result">Результат.</param>
  [OperationContract]
  void AddResult(ScoreBoardResult result);
}

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

/// <summary>
/// Результат в таблице рекордов.
/// </summary>
[DataContract]
public class ScoreBoardResult
{
  /// <summary>
  /// Игра.
  /// </summary>
  [DataMember]
  public string Game { get; set; }

  /// <summary>
  /// Код пользователя.
  /// </summary>
  [DataMember]
  public string UserCode { get; set; }

  /// <summary>
  /// Имя пользователя.
  /// </summary>
  [DataMember]
  public string UserName { get; set; }

  /// <summary>
  /// Набранные очки.
  /// </summary>
  [DataMember]
  public int Score { get; set; }
}

Подробнее об атрибутах:
ServiceContract — атрибут контракта сервиса. Это интерфейс, по которому можно работать с сервисом.
OperationContract — метод интерфейса сервиса.
DataContract — данные, которые передаются между клиентом и сервером.
DataMember — свойство, которое передается при передаче класса, помеченного DataContract.

Теперь добавляем в конфиг приложения такой кусок:

<system.serviceModel>
  <services>
    <service name="ScoreBoardServer.ScoreBoardService" 
             behaviorConfiguration="returnFaults">
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:9875"/>
        </baseAddresses>
      </host>
      <endpoint address="net.tcp://localhost:9876/"
                binding="netTcpBinding"
                contract="ScoreBoardServer.IScoreBoard" />
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="returnFaults">
        <serviceMetadata httpGetEnabled="True"/>
        <serviceDebug includeExceptionDetailInFaults="True" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Осталось только добавить класс с реализацией самого сервиса:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
  internal class ScoreBoardService : IScoreBoard

Здесь InstanceContextMode.Single означает, что все запросы клиентов будет обрабатывать один и тот же экземпляр сервиса.
При старте приложения сервис запускается так:

using (var serviceHost =
  new ServiceHost(typeof(ScoreBoardService)))
{
  serviceHost.Open();
       
  Console.WriteLine("The service is ready.");
  Console.WriteLine("Press <ENTER> to terminate service.");
  Console.ReadLine();

  serviceHost.Close();
}

Теперь, если открыть http://localhost:9875, то появится сообщение о том, что сервис успешно запущен. Можно приступать к клиенту и хранению данных. Об этом во второй части.

Tags: