}
}
public void ReduceStock(int ProductID, int amount) {
string source = 'server ephemeral;uid=sa;pwd=garysql;database Northwind';
SqlConnection conn = new SqlConnection(source);
string command =
'UPDATE Products SET UnitsInStock = UnitsInStock - ' +
amount.ToString() + ' WHERE ProductID = ' + ProductID.ToString();
conn.Open;
sqlCommand cmd = new SqlCommand(command, conn);
cmd.ExecuteNonQuery();
conn.Close();
}
public void IncreaseUnits(int ProductID, int amount) {
string source = 'server=ephemeral;uid=sa;pwd=garysql;database=Northwind';
SqlConnection conn = new SqlConnection(source);
string command =
'UPDATE Products SET UnitsOnOrder = UnitsOnOrder +
' amount.ToString() + ' WHERE ProductID = ' + ProductID.ToString();
conn.Open();
SqlCommand cmd = new SqlCommand(command, conn);
cmd.ExecuteNonQuery();
conn.Close();
}
public void Restore() {
// Восстановить запас продукта ID=2
ReduceStock(2, -10);
// Восстановить единицы продукта для ID=2
IncreaseUnits(2, -10);
// Не требуется восстанавливать запас или единицы продукта для ID=5,
// так так транзакция должна быть отменена
}
}
}
Можно создать клиента для тестирования этой библиотеки классов. Здесь клиент создан так, что он ожидает, пока пользователь проверит содержимое базы данных, прежде чем восстановить базу данных в исходное состояние. Это позволяет увидеть результаты транзакций.
statiс void Main(string[] args) {
Purchase order = new Purchase();
Console.WriteLine('
This transaction should commit');
Console.WriteLine('ProductID = 2, ordering 10 items');
if (Order.PlaceOrder(true)) Console.WriteLine('Transaction Successful');
else Console.WriteLine('Transaction Unsuccessful');
Console.WriteLine('
This transaction should roll back');
Console.WriteLine('ProductID = 5, ordering 5 items');
if (Order.PlaceOrder(false)) Console.WriteLine('Transaction Successful');
else Console.WriteLine('Transaction Unsuccessful');
Console.WriteLine(
'
Take a look at the database then hit enter to
+ 'return database to original state');
Console.ReadLine();
Order.Restore();
}
Другие полезные методы ContextUtil
Рассмотрим еще пару методов класса ContextUtil
которые могут оказаться полезны при программировании на C#.
Первый метод IsCallerInRole()
предназначен для безопасности на основе ролей. В качестве входной переменной этот метод получает строковую переменную, содержащую имя определенной роли системы безопасности Windows 2000. Он возвращает булево значение, указывающее, является или нет пользователь, который в данный момент вызывает объект, членом указанной роли.
В примере кода ниже добавлена проверка, чтобы убедиться, что пользователь, пытающийся вызвать PlaceOrder()
, является авторизованным членом роли Administrators
. Если пользователь не является членом этой роли, то PlaceOrder()
порождает исключение.
[AutoComplete]
public bool PlaceOrder(bool CommitTrans) {
if (!ContextUtil.IsCallerInRole('Administrators') {
throw new AccessViolationException('User is not authorized to place' + 'orders.');
}
// Поместить код транзакции здесь
}
Вторым полезным методом класса ContextUtil
является IsInTransaction()
. Этот метод возвращает булево значение, указывающее, участвует ли объект в данный момент в транзакции.
Профессиональным программистам C# приходится иногда разрабатывать транзакционные компоненты для удаленной установки, которую они не контролируют. Чтобы убедиться, что сборки, требующие транзакционной поддержки, правильно для нее сконфигурированы, можно вызвать свойство IsInTransaction
класса ContextUtil
и инициировать ошибку, если это свойство задано как false
.
В примере кода ниже свойство IsInTransaction
используется для гарантии, что сборка правильно сконфигурирована, прежде чем ей будет разрешено ей начать какую-либо работу. Код порождает исключение, если IsInTransaction
имеет значение false
. Можно протестировать это, изменяя атрибут класса на TransactionalOptionDisabled
.
[AutoComplete]
public bool PlaceOrder(bool CommitTrans) {
if (!ContextUtil.IsInTransaction) {
throw new
ConfigurationException('This assembly needs to be configured for' + ' transactions.');
}
// Выполнить транзакцию
}