Часть 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».
Теперь приступим к созданию WCF сервиса. Перейдем в Solution Explorer и поставив курсор на узел проекта нажмем правую кнопку мыши. Далее в контекстном меню необходимо выбрать пункт «Add – New Item». Далее на вкладке Web нужно выбрать тип файла – «WCF Service». Назовем файл «DbService.svc».
Среда выполнения добавит в наш проект два новых файла «DbService.svc» и «IDbService.cs»:
Также необходимо создать вспомогательный класс 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”. После нажатия откроется окно браузера. Адрес страницы, которая откроется в браузере, и будет адресом нашего сервиса(он нам понадобится позже, для тестирования сервиса). В окне браузера должно быть приблизительно следующее содержимое:
Это значит, что наш сервис правильно настроен и работает. На этом разработка сервиса завершена. Давайте проверим его в действии.
Часть 3. Разработка клиента для тестирования сервиса.
Для тестирования сервиса создадим простое консольное приложение:
Для работы с сервисом нам понадобится ссылка на этот сервис. Чтобы ее добавить необходимо в Solution Explorer установить курсор на узел проекта и выбрать пункт «Add Service Reference». На экране появится следущее окно:
В поле 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 можно запустить тестовую программу.
На этом написание всех модулей системы завершено. Спасибо за внимание.