Начальная загрузка моего компьютера этим утром, я столкнулся с точной проблемой для проекта, я продолжаю работать. У меня были некоторые идеи, которые приводят к следующему дизайну - и комментарии были бы более, чем потрясающими. К сожалению, дизайн, предложенный Josh, не возможен, поскольку я должен работать с удаленным SQL-сервером и не могу включить Распределять Координатору Транзакции сервис, на который он полагается.
Мое решение основано на нескольких все же простых изменениях в моем существующем коде.
Первый, у меня есть вся своя реализация репозиториев простой интерфейс маркера:
/// <summary>
/// A base interface for all repositories to implement.
/// </summary>
public interface IRepository
{ }
, Во-вторых, я позволяю включенным репозиториям всей своей транзакции, реализуют следующий интерфейс:
/// <summary>
/// Provides methods to enable transaction support.
/// </summary>
public interface IHasTransactions : IRepository
{
/// <summary>
/// Initiates a transaction scope.
/// </summary>
void BeginTransaction();
/// <summary>
/// Executes the transaction.
/// </summary>
void CommitTransaction();
}
идея состоит в том, что во всех моих репозиториях я реализую этот интерфейс и добавляю код, который представляет транзакцию непосредственно в зависимости от фактического поставщика (для поддельных репозиториев, я составил список делегатов, который выполняется на фиксации). Для LINQ к SQL было бы легко сделать реализации, такие как:
#region IHasTransactions Members
public void BeginTransaction()
{
_db.Transaction = _db.Connection.BeginTransaction();
}
public void CommitTransaction()
{
_db.Transaction.Commit();
}
#endregion
Это, конечно, требует, чтобы новый класс репозитория был создан для каждого потока, но это разумно для моего проекта.
Каждый метод с помощью репозитория должен вызвать BeginTransaction()
и EndTransaction()
, если репозиторий реализует IHasTransactions
. Для совершения этого еще легче вызова я придумал следующие расширения:
/// <summary>
/// Extensions for spawning and subsequently executing a transaction.
/// </summary>
public static class TransactionExtensions
{
/// <summary>
/// Begins a transaction if the repository implements <see cref="IHasTransactions"/>.
/// </summary>
/// <param name="repository"></param>
public static void BeginTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.BeginTransaction();
}
}
public static void CommitTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.CommitTransaction();
}
}
}
Комментарии ценятся!
__ call ()
вызывается только тогда, когда функция не найдена иным образом, поэтому ваш пример в том виде, в каком он написан, невозможен.
Это невозможно сделать напрямую, но это одна из возможных альтернатив:
class SubFoo { // does not extend
function __construct() {
$this->__foo = new Foo; // sub-object instead
}
function __call($func, $args) {
echo "intercepted $func()!\n";
call_user_func_array(array($this->__foo, $func), $args);
}
}
Подобные вещи хороши для отладки и тестирования, но вы хотите избежать __ call ()
и друзья как можно больше в производственном коде, поскольку они не очень эффективны.
Если вам нужно добавить что-то дополнительное к родительской панели (), можно ли это сделать?
class SubFoo extends Foo {
function bar() {
// Do something else first
parent::bar();
}
}
или это просто вопрос из любопытства?
Для того, чтобы получить такой же эффект, можно сделать следующее:
<?php
class hooked{
public $value;
function __construct(){
$this->value = "your function";
}
// Only called when function does not exist.
function __call($name, $arguments){
$reroute = array(
"rerouted" => "hooked_function"
);
// Set the prefix to whatever you like available in function names.
$prefix = "_";
// Remove the prefix and check wether the function exists.
$function_name = substr($name, strlen($prefix));
if(method_exists($this, $function_name)){
// Handle prefix methods.
call_user_func_array(array($this, $function_name), $arguments);
}elseif(array_key_exists($name, $reroute)){
if(method_exists($this, $reroute[$name])){
call_user_func_array(array($this, $reroute[$name]), $arguments);
}else{
throw new Exception("Function <strong>{$reroute[$name]}</strong> does not exist.\n");
}
}else{
throw new Exception("Function <strong>$name</strong> does not exist.\n");
}
}
function hooked_function($one = "", $two = ""){
echo "{$this->value} $one $two";
}
}
$hooked = new hooked();
$hooked->_hooked_function("is", "hooked. ");
// Echo's: "your function is hooked."
$hooked->rerouted("is", "rerouted.");
// Echo's: "our function is rerouted."
?>