Создание веб-сервиса интернет-магазина
Рассмотрим пример создания веб-сервиса интернет магазина. Создаваемый веб-сервис должен выполнять такой минимальный набор функций:
Предоставлять список каталогов интернет-магазина.
Предоставлять список товаров, содержащихся в выбранном каталоге.
Регистрировать заказ с указанием контактной информации заказчика, времени и адреса доставки, а также необходимых товаров и их количество.
Передавать статус заказа, сделанного заказчиком.
Спроектируем базу данных для хранения всех необходимых данных для работы веб-сервиса. Полученная в среде Visio физическая модель базы данных выглядит следующим образом:
Рис. 4.2. Физическая модель БД веб-сервиса.
Для хранения данных из базы данных в приложении веб-сервиса создадим следующий набор классов:
using System;
using System.Collections.Generic;
using BLToolkit.Mapping;
namespace WebShoping
{
public class Catalog
{
[MapField("CatalogId")]
public int Id;
public string Name;
}
public class Good
{
[MapField("GoodId")]
public int Id;
public string Name;
public double Price;
public bool Available;
}
public class Order
{
[MapField("OrderId")]
public Guid Id;
public string FIO;
public string Phone;
public string CardNo;
public string CardCVV2;
public string Address;
public DateTime Date;
public List<OrderedGood> Goods;
}
public class OrderedGood
{
public int Id;
public int Count;
}
public enum OrderStatus
{
[MapValue("R")]
Registered,
[MapValue("I")]
InWork,
[MapValue("S")]
Sended,
[MapValue("N")]
NotEnoughGoods,
[MapValue("W")]
WrongGoodId,
[DefaultValue()]
[NullValue()]
OrderNotFound
}
}
Как можно заметить, в приведенном выше коде используются тип данных List<OrderedGood>. Такая запись означает создание типизированного списка с типом данных OrderedGood. Возможность создания generic типов данных была введена в .NET 2.0 и за работу с ними отвечает пространство имен System.Collections.Generic.
Для работы с базой данных в создаваемом веб-сервисе используется библиотека «Business Logic Toolkit» (http://rsdn.ru/?article/files/libs/RsdnFrameworkData.xml), являющаяся проектом с открытым исходным кодом, разрабатываемом в рамках проекта RSDN. Эта библиотека берет на себя все функции по организации соединения с базой данных, исполнению запросов и отображению результатов запроса на классы приложений. В приведенном выше примере можно заметить использование атрибутов управления отображением, позволяющих установить правильное соответствие между именами атрибутов отношений базы данных и именами свойств используемых классов. Для перечисления «OrderStatus» был также задан программный кодификатор, задающий соотношение между возможными статусами заказа и значениями, хранимыми в базе данных.
Описав классы, используемые для хранения данных, можно приступить к написанию класса самого веб-сервиса:
using System;
using System.Collections.Generic;
using System.Web.Services;
using BLToolkit.Data;
namespace WebShoping
{
[WebService(Namespace = "http://www.shopservice.ru",
Description = "Интернет магазин")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ShopService : WebService
{
/// <summary>
/// Получает список катологов магазина
/// </summary>
/// <returns>Список каталогов</returns>
[WebMethod(Description = "Получить список каталогов",
CacheDuration = 300)]
public List<Catalog> GetCatalogs()
{
using (DbManager db = new DbManager())
{
db.SetSpCommand("GetCatalogs");
return db.ExecuteList<Catalog>();
}
}
/// <summary>
/// Получает товары из каталога
/// </summary>
/// <param name="catalogId">Ид каталога</param>
/// <returns>Список товаров</returns>
[WebMethod(Description = "Получить список товаров в каталоге",
CacheDuration = 300)]
public List<Good> GetGoods(int catalogId)
{
using (DbManager db = new DbManager())
{
db.SetSpCommand("GetGoods",
db.Parameter("@CatalogId", catalogId));
return db.ExecuteList<Good>();
}
}
/// <summary>
/// Регистрирует заказ в магазине
/// </summary>
/// <param name="order">Заказ</param>
/// <param name="statusName">Статус заказа</param>
/// <returns>Cумму заказа</returns>
[WebMethod(Description = "Зарегистрировать заказ")]
public double RegisterOrder(Order order, out string statusName)
{
OrderStatus status;
double orderSum = 0;
using (DbManager db = new DbManager())
{
try
{
db.BeginTransaction();
db.SetSpCommand("SaveOrder",
db.Parameter("@OrderId", order.Id),
db.Parameter("@FIO", order.FIO),
db.Parameter("@Phone", order.Phone),
db.Parameter("@Address", order.Address),
db.Parameter("@CardNo", order.CardNo),
db.Parameter("@CardCVV2", order.CardCVV2),
db.Parameter("@Date", order.Date),
db.Parameter("@Status", db.MappingSchema.MapEnumToValue(OrderStatus.Registered)));
db.ExecuteNonQuery();
foreach (OrderedGood orderedGood in order.Goods)
{
db.SetSpCommand("AddOrderGood",
db.Parameter("@OrderId", order.Id),
db.Parameter("@GoodId", orderedGood.Id),
db.Parameter("@Count",orderedGood.Count));
db.ExecuteNonQuery();
}
db.CommitTransaction();
db.SetSpCommand("GetOrderSum",
db.Parameter("@OrderId", order.Id));
object sum = db.ExecuteScalar();
orderSum = Convert.ToDouble(sum);
if (orderSum < 10000)
{
status = OrderStatus.Registered;
}
else
{
status = OrderStatus.NotEnoughGoods;
db.SetSpCommand("UpdateOrderStatus",
db.Parameter("@OrderId", order.Id),
db.Parameter("@Status",
db.MappingSchema.MapEnumToValue(status)));
db.ExecuteNonQuery();
}
}
catch
{
// Товар не найден в базе
db.Transaction.Rollback();
status = OrderStatus.WrongGoodId;
}
}
statusName = status.ToString();
return orderSum;
}
/// <summary>
/// Получает статус заказа
/// </summary>
/// <param name="orderId">Ид заказа</param>
/// <returns>Статус</returns>
[WebMethod(Description = "Получить статус заказа")]
public OrderStatus GetOrderStatus(Guid orderId)
{
using (DbManager db = new DbManager())
{
db.SetSpCommand("GetOrderStatus",
db.Parameter("@OrderId", orderId));
object res = db.ExecuteScalar();
return (OrderStatus)db.MappingSchema.MapValueToEnum(res,
typeof(OrderStatus));
}
}
/// <summary>
/// Возвращает номер карты в платежной системе, на которую нужно
/// переводить деньги для оплаты в магазине
/// </summary>
/// <returns>Номер карты</returns>
[WebMethod(Description = "Возвращает номер карты магазина в платежной системе")]
public string GetCardNumber()
{
return "1234567812345678";
}
}
}
Для работы с базой данных в приведенном примере используется набор из пяти хранимых процедур:
· Получение списка каталогов
ALTER PROCEDURE dbo.GetCatalogs
AS
SELECT *
FROM Catalog
· Получение списка товаров в каталоге
ALTER PROCEDURE dbo.GetGoods
@CatalogId int
AS
SELECT *
FROM Good
WHERE CatalogId = @CatalogId
· Сохранение заказа
ALTER PROCEDURE [dbo].[SaveOrder]
@OrderId uniqueidentifier,
@FIO nvarchar(100),
@Phone nvarchar(100),
@Address nvarchar(200),
@CardNo char(16),
@CardCVV2 char(3),
@Date datetime,
@Status char(1)
AS
INSERT INTO [Order] (OrderId, FIO, Phone, Address,
CardNo, CardCVV2, [Date], Status)
VALUES (@OrderId, @FIO, @Phone, @Address,
@CardNo, @CardCVV2, @Date, @Status)
· Добавление товара в сохраненный заказ
ALTER PROCEDURE dbo.AddOrderGood
@OrderId int,
@GoodId int,
@Count int
AS
INSERT INTO OrderedGoods (OrderId, GoodId, [Count])
VALUES (@OrderId, @GoodId, @Count)
· Получение статуса сохраненного заказа
ALTER PROCEDURE [dbo].[GetOrderStatus]
@OrderId uniqueidentifier
AS
SELECT Status
FROM [Order]
WHERE OrderId = @OrderId
· Получение суммы сохраненного заказа
ALTER PROCEDURE [dbo].[GetOrderSum]
@OrderId uniqueidentifier
AS
SELECT SUM(o.Count * g.Price)
FROM OrderedGoods o
JOIN Good g
ON (o.GoodId = g.GoodId)
WHERE o.OrderId = @OrderId
· Изменение статуса сохраненного заказа
ALTER PROCEDURE [dbo].[UpdateOrderStatus]
@OrderId uniqueidentifier,
@Status char(1)
AS
UPDATE [Order]
SET Status = @Status
WHERE OrderId = @OrderId
По своей сути весь код созданного веб-сервиса сводится к заданию параметров вызова хранимых процедур в базе данных, их последующему исполнению и возвращению полученных результатов клиентскому приложению. Таким образом, полученных веб-сервис представляет собой лишь некоторую прослойку между клиентским приложением и СУБД. Такая архитектура была избранна исключительно для упрощения кода веб-сервиса. В общем случае веб-сервис может выполнять различные операции бизнес логики, приводящие к взаимодействию с базой данных, другими веб-сервисами или прочими источниками данных.
Стоит отметить также атрибуты из пространства имен System.Web.Services, управляющие созданием веб-сервиса:
[WebService(Namespace = "http://www.mephi.ru/order",
Description = "Интернет магазин")]
Этот атрибут задает целевое пространство имен, в котором будет определены все типы данных созданного веб-сервиса. Вторая строка задает описание всего веб-сервиса на естественном языке.
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
Этот атрибут определяет, что создаваемый веб-сервис должен соответствовать стандарту Web Services Interoperability (WSI) Basic Profile версии 1.1 (http://www.ws-i.org/Profiles/BasicProfile-1.1.html).
[WebMethod(Description = "Получить список каталогов",
CacheDuration = 300)]
Этот атрибут задает описание операции веб-сервиса на естественном языке. Во второй строке задается длительность хранения кэша результатов работы операции в секундах. Так как веб-сервисы на платформе .NET являются подклассом веб-приложений ASP.NET, то для них также доступна вся базовая функциональность этих приложений. К примеру, кэширование результатов работы операции осуществляется на уровне веб-сервера, на котором исполняется веб-сервис. При этом кэшируются пары HTTP запросов и соответствующих им ответов.
Кэширование позволяет оптимизировать работу веб-сервисов. Оно может быть использовано при ограниченном множестве входных данных, как, например, в используемом примере, где входных данных нет. Также немаловажным фактором должна быть статичность результатов выполнения операции. В противном случае вместо актуальных данных клиент может получить устаревшие данные из кэша, что в свою очередь может привести к неверному функционированию приложений. Если перечисленные выше условия не выполняются, то использование кэширования результатов работы веб-сервиса может привести к чрезмерному расходованию памяти веб-сервера и лишь замедлит его работу.
Дата добавления: 2015-02-28; просмотров: 996;