arr
теперь является зависимым именем. Это зависит от T
. Что, если есть T
, для которого Base<T>
специализирован, чтобы не иметь arr
? В частности, из [temp.dep]:
В определении шаблона класса или класса область зависимого базового класса (14.6.2.1) не рассматривается при неквалифицированном поиске имени либо в точка определения шаблона класса или члена или во время создания шаблона или члена класса.
blockquote>
Base<T>
- зависимый базовый класс - он зависит от параметра шаблонаT
, поэтому его область не рассматривается во время поиска unqualified . Для этого нужно использовать квалифицированный поиск . То есть либо имя класса:parr = &Base<T>::arr[0];
, либо просто с помощью
this
:parr = &this->arr[0];
Я сейчас сижу с той же проблемой и мне, кажется, существует два решения:
Редактирование: я создал CodePlex-проект для этого теперь: http://legendtransactions.codeplex.com/
я склоняюсь к созданию ряда интерфейсов для работы с транзакциями и реализации по умолчанию, которая делегирует к Системе. Реализации транзакции, что-то как:
public interface ITransactionManager
{
ITransaction CurrentTransaction { get; }
ITransactionScope CreateScope(TransactionScopeOption options);
}
public interface ITransactionScope : IDisposable
{
void Complete();
}
public interface ITransaction
{
void EnlistVolatile(IEnlistmentNotification enlistmentNotification);
}
public interface IEnlistment
{
void Done();
}
public interface IPreparingEnlistment
{
void Prepared();
}
public interface IEnlistable // The same as IEnlistmentNotification but it has
// to be redefined since the Enlistment-class
// has no public constructor so it's not mockable.
{
void Commit(IEnlistment enlistment);
void Rollback(IEnlistment enlistment);
void Prepare(IPreparingEnlistment enlistment);
void InDoubt(IEnlistment enlistment);
}
Это походит на большую работу, но с другой стороны это является допускающим повторное использование, и это делает все это очень легко тестируемым.
Примечание, что это не полное определение интерфейсов как раз, чтобы дать Вам большое изображение.
Редактирование: я просто сделал некоторую быструю и грязную реализацию как подтверждение концепции, я думаю, что это - направление, которое я возьму, вот то, что я придумал до сих пор. Я думаю, что, возможно, должен создать проект CodePlex для этого так, проблема может быть решена раз и навсегда. Это не первый раз, когда я столкнулся с этим.
public interface ITransactionManager
{
ITransaction CurrentTransaction { get; }
ITransactionScope CreateScope(TransactionScopeOption options);
}
public class TransactionManager : ITransactionManager
{
public ITransaction CurrentTransaction
{
get { return new DefaultTransaction(Transaction.Current); }
}
public ITransactionScope CreateScope(TransactionScopeOption options)
{
return new DefaultTransactionScope(new TransactionScope());
}
}
public interface ITransactionScope : IDisposable
{
void Complete();
}
public class DefaultTransactionScope : ITransactionScope
{
private TransactionScope scope;
public DefaultTransactionScope(TransactionScope scope)
{
this.scope = scope;
}
public void Complete()
{
this.scope.Complete();
}
public void Dispose()
{
this.scope.Dispose();
}
}
public interface ITransaction
{
void EnlistVolatile(Enlistable enlistmentNotification, EnlistmentOptions enlistmentOptions);
}
public class DefaultTransaction : ITransaction
{
private Transaction transaction;
public DefaultTransaction(Transaction transaction)
{
this.transaction = transaction;
}
public void EnlistVolatile(Enlistable enlistmentNotification, EnlistmentOptions enlistmentOptions)
{
this.transaction.EnlistVolatile(enlistmentNotification, enlistmentOptions);
}
}
public interface IEnlistment
{
void Done();
}
public interface IPreparingEnlistment
{
void Prepared();
}
public abstract class Enlistable : IEnlistmentNotification
{
public abstract void Commit(IEnlistment enlistment);
public abstract void Rollback(IEnlistment enlistment);
public abstract void Prepare(IPreparingEnlistment enlistment);
public abstract void InDoubt(IEnlistment enlistment);
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
this.Commit(new DefaultEnlistment(enlistment));
}
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
this.InDoubt(new DefaultEnlistment(enlistment));
}
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
{
this.Prepare(new DefaultPreparingEnlistment(preparingEnlistment));
}
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
this.Rollback(new DefaultEnlistment(enlistment));
}
private class DefaultEnlistment : IEnlistment
{
private Enlistment enlistment;
public DefaultEnlistment(Enlistment enlistment)
{
this.enlistment = enlistment;
}
public void Done()
{
this.enlistment.Done();
}
}
private class DefaultPreparingEnlistment : DefaultEnlistment, IPreparingEnlistment
{
private PreparingEnlistment enlistment;
public DefaultPreparingEnlistment(PreparingEnlistment enlistment) : base(enlistment)
{
this.enlistment = enlistment;
}
public void Prepared()
{
this.enlistment.Prepared();
}
}
}
Вот пример класса, который зависит от ITransactionManager для обработки, это - транзакционная работа:
public class Foo
{
private ITransactionManager transactionManager;
public Foo(ITransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
public void DoSomethingTransactional()
{
var command = new TransactionalCommand();
using (var scope = this.transactionManager.CreateScope(TransactionScopeOption.Required))
{
this.transactionManager.CurrentTransaction.EnlistVolatile(command, EnlistmentOptions.None);
command.Execute();
scope.Complete();
}
}
private class TransactionalCommand : Enlistable
{
public void Execute()
{
// Do some work here...
}
public override void Commit(IEnlistment enlistment)
{
enlistment.Done();
}
public override void Rollback(IEnlistment enlistment)
{
// Do rollback work...
enlistment.Done();
}
public override void Prepare(IPreparingEnlistment enlistment)
{
enlistment.Prepared();
}
public override void InDoubt(IEnlistment enlistment)
{
enlistment.Done();
}
}
}
Я - Java-разработчик, таким образом, я не уверен в деталях C#, но мне необходимостью в двух модульных тестах здесь кажется.
первый должен быть тестом "голубого неба", который успешно выполняется. Ваш модульный тест должен гарантировать, чтобы все записи, которые являются ACID, появились в базе данных после того, как транзакция фиксируется.
второй должен быть "wonky" версией, которая делает деятельность InsertFoo и затем выдает исключение прежде, чем делать попытку InsertBar. Успешный тест покажет, что исключение было выдано и что ни объекты Нечто ни Панели не посвятили себя базе данных.
, Если бы оба из них передают, я сказал бы, что Ваш TransactionScope работает, как он должен.
Игнорирование, является ли этот тест хорошей вещью или нет....
Очень грязный взлом должен проверить ту Транзакцию. Текущий не является пустым.
Это не 100%-й тест, так как кто-то мог использовать что-то другое, чем TransactionScope для достижения этого, но он должен принять меры против очевидного, 'не потрудился иметь транзакцию' части.
Другая опция состоит в том, чтобы сознательно попытаться создать новый TransactionScope с несовместимым уровнем изоляции к тому, что/должно использоваться и TransactionScopeOption.Required
. Если это успешно выполняется вместо того, чтобы бросить ArgumentException не было транзакции. Это требует, чтобы Вы знали, что конкретный IsolationLevel не использован (что-то как Chaos - потенциальный выбор)
, Ни одна из этих двух опций не особенно приятна, последний очень хрупок и подвергается семантике TransactionScope, остающегося постоянным. Я протестировал бы первого, а не последнего, так как это несколько более устойчиво (и ясно читать/отлаживать).