21 апреля 2012 г.

Организация работы с базой данных посредством сервисов WCF

 

Часть 1. Создание тестовой базы данных.

   Целью данной статьи является создание сервиса при помощи технологии Windows Communication Foundation для демонстрации слоя доступа к базе данных в контексте сервис-ориентированной архитектуры.

  Первым шагом является разработка тестовой базы данных, к которой будут осуществляться запросы.

  Для этого выполним следующую последовательность действий:

  1) Открыть приложение Microsoft SQL Server Management Studio и подключится к серверу баз данных на локальном компьютере или на удаленном хосте;

  2) Создадим базу данных выполнив следующий скрипт:

CREATE DATABASE [ProductsDB] ON PRIMARY

( NAME = N'ProductsDB',

FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\ProductsDB.mdf' ,

SIZE = 3072KB , FILEGROWTH = 1024KB )

LOG ON ( NAME = N'ProductsDB_log',

FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\ProductsDB_log.ldf' ,

SIZE = 1024KB , FILEGROWTH = 10%) GO

   При необходимости можно изменить параметр FILENAME для файла базы данных и лог-файла.

   3) Создадим таблицу Products в ранее созданной базе данных ProductsDB, выполнив следующий скрипт:

USE [ProductsDB] GO

CREATE TABLE [dbo].[tb_Products]

( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NOT NULL, [Description] [nvarchar](500) NOT NULL,

[Price] [int] NOT NULL, CONSTRAINT [PK_tb_Products] PRIMARY KEY CLUSTERED

( [Id] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO

   4) Для тестирования различных способов взаимодействия с базой данных создадим тестовую хранимую процедуру, которая будет возвращать записи из таблицы tb_Products, у которых поле Name будет совпадать с параметром @Name, переданным в хранимую процедуру:

CREATE PROCEDURE sp_GetProductsByName

@Name nvarchar(50)

AS BEGIN

SELECT Id, Name, Description, Price FROM tb_Products WHERE Name LIKE '%' + @Name + '%';

END GO

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

 

Часть 2. Разработка WCF сервиса.

Следующий этап – создание кода ля выполнения запросов к базе данных средствами языка программирования C#.

Откроем среду разработки Microsoft Visual Studio 2010 и создадим новый проект, выбрав тип проекта – ASP.NET Empty Web Application. Проекты на основе языка программирования ASP.NET идеально подходят для быстрого создания и развертывания сервисов WCF. Назовем проект «WCF_DataAccess».

clip_image002

Теперь приступим к созданию WCF сервиса. Перейдем в Solution Explorer и поставив курсор на узел проекта нажмем правую кнопку мыши. Далее в контекстном меню необходимо выбрать пункт «Add – New Item». Далее на вкладке Web нужно выбрать тип файла – «WCF Service». Назовем файл «DbService.svc».

clip_image004

Среда выполнения добавит в наш проект два новых файла «DbService.svc» и «IDbService.cs»:

clip_image005

Также необходимо создать вспомогательный класс ProductBO, который будет выполнять сразу несколько функций – это будет оболочка для записи из таблицы tb_Products, а также контракт данных для использования его в WCF. Сигнатура класса приведена ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
namespace WCF_DataAccess

{
   [DataContract]
   public class ProductBO
   {
       [DataMember]
       public int Id { get; set; }
       [DataMember]
       public string Name { get; set; }
       [DataMember]
       public string Description { get; set; }
       [DataMember]
       public int Price { get; set; }
}
}

Аттрибут [DataContract] означет что этот класс может использоваться для передачи данных между сервисом WCF и его клиентами. Атрибут [DataMember] указывает какие поля данных должны быть включены в сообщение при передаче по каналам связи WCF.

Теперь мы можем перейти к написанию сервиса. Сервис в WCF сосотит из двух частей:

1) Контракт сервиса – это файл «IDbService.cs». В этом файле находится контракт сервиса – интерфейс на языке C#, который помечен атрибутом [ServiceContract]. Интерфейс содержит набор определений методов, которые помечены атрибутом [OperationContract]. Данный интерфейс называется контрактом сервиса, птомучто он декларирует точное описание методов, которые будет предоставлять своим клиентам WCF сервис. Код интерфейса приведен ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_DataAccess
{
     [ServiceContract]
      public interface IDbService
      {
          [OperationContract]
          List<ProductBO> GetAllProducts();
          [OperationContract]
          void InsertProduct(ProductBO product);
          [OperationContract]
          void DeleteProductById(int productId);
         [OperationContract]
         List<ProductBO> GetProductsByName(string name);
     }
}

2) Реализация сервиса – файл «DbService.svc». Здесь находится класс DbService, который наследует и реализует контракт сервиса – интерфейс IDbService. Поскольку мы будем работать с базой данных через наш сервис, класс DbService будет также реализовывать интерфейс IDisposable.

public class DbService : IDbService, IDisposable

Класс будет содержать одно поле и конструктор:

private SqlConnection _connection;
public DbService()
{
    string connectionString = ConfigurationManager.ConnectionStrings["ProductsDBConnectionString"].ConnectionString;
    _connection = new SqlConnection(connectionString);
    _connection.Open();
}

В конструкторе создается соединение с базой данных. Строка соединения берется из файла конфигурации Web.config, где она имеет следующий вид:

<configuration>
     <connectionStrings>
            <add name="ProductsDBConnectionString" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=ProductsDB;Integrated Security=True" />
      </connectionStrings>

Приступим к реализации методов сервиса:

Выборка всех продуктов из таблицы:

public List<ProductBO> GetAllProducts()
{
       string query = "SELECT Id, Name, Description, Price FROM tb_Products";
       SqlCommand command = new SqlCommand(query, _connection);
       SqlDataReader dataReader = command.ExecuteReader();
       List<ProductBO> products = new List<ProductBO>();
       while (dataReader.Read())
      {
            products.Add(new ProductBO()
           {
               Id = dataReader.GetInt32(0),
               Name = dataReader.GetString(1),
               Description = dataReader.GetString(2),
               Price = dataReader.GetInt32(3)
          });
     }
     return products;
}

Добавление записи в таблицу продуктов:

public void InsertProduct(ProductBO product)
{
       string query = "INSERT INTO tb_Products VALUES (@name, @desc, @price);";
       SqlCommand command = new SqlCommand(query, _connection);
       command.Parameters.Add(new SqlParameter()
       {
            DbType = DbType.String,
            Value = product.Name,
            ParameterName = "name"
      });
      command.Parameters.Add(new SqlParameter()
      {
           DbType = DbType.String,
           Value = product.Description,
           ParameterName = "desc"
     });
     command.Parameters.Add(new SqlParameter()
     {
          DbType = DbType.Int32,
          Value = product.Price,
          ParameterName = "price"
     });
     command.ExecuteNonQuery();
}

Удаление продукта из таблицы по его Id:

public void DeleteProductById(int productId)
{
        string query = "DELETE FROM tb_Products WHERE Id = @id";
        SqlCommand command = new SqlCommand(query, _connection);
        command.Parameters.Add(new SqlParameter()
       {
            DbType = DbType.Int32,
            Value = productId,
            ParameterName = "id"
       });
       command.ExecuteNonQuery();
}

Выборка продуктов с фильтрацией продуктов по полю Name, при помощи хранимой процедуры:

public List<ProductBO> GetProductsByName(string name)
{
         SqlCommand command = new SqlCommand();
         command.Connection = _connection;
         command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "sp_GetProductsByName";
        command.Parameters.Add(new SqlParameter()
        {
             DbType = DbType.String,
             Value = name,
             ParameterName = "Name"
        });
        SqlDataReader dataReader = command.ExecuteReader();
        List<ProductBO> products = new List<ProductBO>();
        while (dataReader.Read())
        {
              products.Add(new ProductBO()
              {
                    Id = dataReader.GetInt32(0),
                    Name = dataReader.GetString(1),
                    Description = dataReader.GetString(2),
                    Price = dataReader.GetInt32(3)
              });
       }
       return products;
}

И последний метод – член интерфейса IDisposable – Dispose:

public void Dispose()
{
       _connection.Close();
}

Данный метод будет вызываться автоматически после завершения работы сервиса.

На этом написание кода сервиса завершено. Попробуем его запустить, для этого в Solution Explorer необходимо установить курсор на файл DbService.svc и в контекстном меню выбрать пункт “View In Browser”. После нажатия откроется окно браузера. Адрес страницы, которая откроется в браузере, и будет адресом нашего сервиса(он нам понадобится позже, для тестирования сервиса). В окне браузера должно быть приблизительно следующее содержимое:

clip_image007

Это значит, что наш сервис правильно настроен и работает. На этом разработка сервиса завершена. Давайте проверим его в действии.

 

Часть 3. Разработка клиента для тестирования сервиса.

Для тестирования сервиса создадим простое консольное приложение:

clip_image001

Для работы с сервисом нам понадобится ссылка на этот сервис. Чтобы ее добавить необходимо в Solution Explorer установить курсор на узел проекта и выбрать пункт «Add Service Reference». На экране появится следущее окно:

clip_image003

В поле Address необхимо ввести адрес из браузера и нажать кнопку Go. Затем в поле Namespace указать значение «Service Refference» - это пространство имен в котором будут расположены классы для работы с сервисом. В конце жмем кнопку «Ok».

Модифицируем файл Program.cs внеся туда код для использования нашего сервиса:

class Program
{
    static void Main(string[] args)
    {
          var service = new DbServiceClient();
          Console.WriteLine("Add product: ");
          ProductBO newProduct = new ProductBO() { Name = "Book", Description = "Classic", Price = 5 };
          service.InsertProduct(newProduct);
          Console.WriteLine("Success!\n");
          Console.WriteLine("Add product: ");
          newProduct = new ProductBO() { Name = "Pencil", Description = "Red", Price = 1 };
          service.InsertProduct(newProduct);
          Console.WriteLine("Success!\n");
          Console.WriteLine("Products list:");
          List<ProductBO> products = service.GetAllProducts().ToList();
          string productFormat = "Id - {0}, Name - {1}, Desc - {2}, Price - {3}";
          products.ForEach(product => Console.WriteLine(String.Format(productFormat, product.Id, product.Name,      product.Description, product.Price)));
          Console.WriteLine("Search by Name 'Book':");
          products = service.GetAllProducts().ToList();
          products.ForEach(product => Console.WriteLine(String.Format(productFormat, product.Id, product.Name,  product.Description, product.Price)));
          Console.WriteLine("Delete product with Id = 1: ");
          service.DeleteProductById(1);
          Console.WriteLine("Success!");
   }
}

Теперь нажав комбинацию клавиш Ctrl + F5 можно запустить тестовую программу.

На этом написание всех модулей системы завершено. Спасибо за внимание.

2 комментария:

  1. А если у меня визуалка 2012, то куда добавлять базу данных? в консоль или в wcf?

    ОтветитьУдалить
  2. Благодарю Евгений. С каждой такой статьёй мир становиться ярче!

    ОтветитьУдалить