Как правильно реализовывать класс который может быть транзакционным..
Например у меня есть некоторый Dal класс.. который что то куда то записывает... Допустим у него есть метод Add(T entity)
так вот я хочу поместить в него логику что если он выполняется в окружении TransactionScope то запись делать только если у TransactionScope вызвали Complete.
Как такое сделать правильно?
Да в принципе, не особо сложно.
Вот тут можно почитать.
Вот только действительно ли оно вам надо?
Здравствуйте, Аноним, Вы писали:
А>Как правильно реализовывать класс который может быть транзакционным..
Без особой причины тру-транзакционностью лучше не заморачиваться.
Неаккуратное использование System.Transactions.* слишком легко (и внезапно
) создаёт тормоза или падения (при отключенном DTC) на машине клиента. Куда проще реализовать легковесную disposable-обёртку и использовать её примерно так:
using (var operation = MyCommitableOperation(someResource))
{
// ...
op.Commit();
}
Впрочем, этот вариант тоже следует использовать с осторожностью, т к. он
разворачивается в
var operation = MyCommitableOperation(someResource);
// TreadAbortException goes gere.
try
{
// ...
и не гарантирует вызов Dispose().
Если всё-таки припёрло — вот готовая обёртка для transacted resource (вырвано с мясом — допилите напильником)
| Скрытый текст |
| [Flags]
public enum TransactionSources
{
/// <summary>
/// Does nothing.
/// </summary>
None = 0x0,
/// <summary>
/// Uses transaction passed as an argument to the Enlist method.
/// </summary>
Explicit = 0x1,
/// <summary>
/// Uses transaction scope.
/// </summary>
TransactionScope = 0x2,
/// <summary>
/// Automatically creates transaction.
/// </summary>
Implicit = 0x4,
/// <summary>
/// Uses first available source.
/// </summary>
All = Explicit
| TransactionScope
| Implicit
}
public abstract class TransactedOperation: CriticalFinalizerObject, IDisposable
{
private sealed class ResourceManager: ISinglePhaseNotification
{
private readonly TransactedOperation resource;
public ResourceManager(TransactedOperation resource)
{
Code.NotNull(resource, "resource");
this.resource = resource;
}
#region ISinglePhaseNotification Members
void ISinglePhaseNotification.SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
{
if (resource.TryPrepareCommit(true))
{
try
{
resource.DoCommit();
}
finally
{
singlePhaseEnlistment.Committed();
resource.Unenlist();
}
}
else
{
try
{
resource.DoRollback();
}
finally
{
singlePhaseEnlistment.Aborted();
resource.Unenlist();
}
}
}
#endregion
#region IEnlistmentNotification Members
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
try
{
resource.DoCommit();
}
finally
{
enlistment.Done();
resource.Unenlist();
}
}
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
try
{
resource.InDoubt();
}
finally
{
enlistment.Done();
resource.Unenlist();
}
}
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
{
if (resource.TryPrepareCommit(false))
{
preparingEnlistment.Prepared();
}
else
{
try
{
resource.DoRollback();
}
finally
{
preparingEnlistment.ForceRollback();
resource.Unenlist();
}
}
}
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
try
{
resource.DoRollback();
}
finally
{
enlistment.Done();
resource.Unenlist();
}
}
#endregion
}
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields",
Justification = CodeAnalysisConstants.CA1823Conditional)]
private static readonly string unenlistBugMessage = Resources.TransactedOperation_UnenlistBug;
private static readonly string transactionEnlistedMessage = Resources.TransactedOperation_TransactionEnlisted;
private static void InitTransaction(
ref Transaction transaction, ref TransactionSources sources)
{
Transaction resultingTransaction = null;
TransactionSources resultingSource = TransactionSources.None;
Transaction scopeTransaction = Transaction.Current;
if (transaction != null && sources.Includes(TransactionSources.Explicit))
{
resultingTransaction = transaction;
resultingSource = TransactionSources.Explicit;
}
else if (scopeTransaction != null && sources.Includes(TransactionSources.TransactionScope))
{
resultingTransaction = scopeTransaction;
resultingSource = TransactionSources.TransactionScope;
}
else if (sources.Includes(TransactionSources.Implicit))
{
resultingTransaction = new CommittableTransaction();
resultingSource = TransactionSources.Implicit;
}
transaction = resultingTransaction;
sources = resultingSource;
}
#region Fields & .ctor()
private Transaction currentTransaction;
private TransactionSources transactionSource;
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
Justification = CodeAnalysisConstants.CA1816ExplicitSuppressFinalize)]
protected TransactedOperation()
{
GC.SuppressFinalize(this);
}
#endregion
#region Public properties
public Transaction Transaction
{
get
{
return currentTransaction;
}
set
{
if (currentTransaction != value)
{
Enlist(value);
}
}
}
public TransactionSources TransactionSource
{
get
{
return transactionSource;
}
}
public bool TransactionEnlisted
{
get
{
return transactionSource != TransactionSources.None;
}
}
#endregion
#region Public methods
public bool Enlist(TransactionSources sources)
{
return Enlist(null, sources);
}
public bool Enlist(Transaction transaction)
{
Code.NotNull(transaction, "transaction");
return Enlist(transaction, TransactionSources.Explicit);
}
public bool Enlist(Transaction transaction, TransactionSources sources)
{
Code.AssertState(!TransactionEnlisted, transactionEnlistedMessage);
InitTransaction(ref transaction, ref sources);
bool result = sources != TransactionSources.None;
if (result)
{
PrepareEnlist(transaction, sources);
transaction.EnlistVolatile(new ResourceManager(this), EnlistmentOptions.None);
currentTransaction = transaction;
transactionSource = sources;
if (sources == TransactionSources.Implicit)
{
GC.ReRegisterForFinalize(this);
}
}
return result;
}
public CommittableTransaction BeginTransaction()
{
Enlist(TransactionSources.Implicit);
return (CommittableTransaction)currentTransaction;
}
public bool CommitIfImplicitTransaction()
{
bool result = transactionSource == TransactionSources.Implicit;
if (result)
{
((CommittableTransaction)currentTransaction).Commit();
}
return result;
}
#endregion
#region Overridable members
protected abstract void PrepareEnlist(Transaction newTransaction, TransactionSources source);
protected abstract bool TryPrepareCommit(bool singlePhase);
protected abstract void DoCommit();
protected abstract void DoRollback();
protected virtual void InDoubt()
{
DoRollback();
}
protected virtual void Unenlisted()
{
}
#endregion
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
Justification = CodeAnalysisConstants.CA1816ExplicitSuppressFinalize)]
private void Unenlist()
{
if (!TransactionEnlisted)
{
Code.Bug(unenlistBugMessage);
}
if (transactionSource == TransactionSources.Implicit)
{
GC.SuppressFinalize(this);
}
transactionSource = TransactionSources.None;
currentTransaction = null;
Unenlisted();
}
#region IDisposable Members
protected virtual void Dispose(bool disposing)
{
if (transactionSource == TransactionSources.Implicit)
{
currentTransaction.Dispose();
}
}
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
Justification = CodeAnalysisConstants.CA1816ExplicitSuppressFinalize)]
public void Dispose()
{
Dispose(true);
}
~TransactedOperation()
{
Dispose(false);
}
#endregion
}
public class TransactedValue<T>: TransactedOperation where T: struct
{
private T? oldValue;
private T value;
public TransactedValue(T value)
{
this.value = value;
Enlist(TransactionSources.Implicit);
}
public TransactedValue(T value, Transaction transaction)
{
this.value = value;
Enlist(transaction);
}
public TransactedValue(T value, TransactionSources sources)
{
this.value = value;
Enlist(sources);
}
public T Value
{
get
{
return this.value;
}
set
{
this.value = value;
}
}
protected override void PrepareEnlist(Transaction newTransaction, TransactionSources source)
{
oldValue = value;
}
protected override bool TryPrepareCommit(bool singlePhase)
{
return oldValue.HasValue;
}
protected override void DoCommit()
{
oldValue = null;
}
protected override void DoRollback()
{
value = oldValue.Value;
}
}
public static void Main(string[] args)
{
TransactedValue<int> transacted = new TransactedValue<int>(100);
try
{
transacted.Value = 200;
//transacted.CommitIfImplicitTransaction();
}
finally
{
transacted.Dispose();
}
Console.WriteLine(transacted.Value);
}
|
| |
Здравствуйте, Timur, Вы писали:
T>Да в принципе, не особо сложно.
T>Вот тут можно почитать.
T>Вот только действительно ли оно вам надо?
Не надо. Правильно сделать resource manager (особенно с 2-phase-commit довольно сложно, надо внимательно изучать документацию к каждому методу.
Здравствуйте, Sinix, Вы писали:
Упс, ступил с примером использования:
| Скрытый текст |
| public class TransactedValue<T>: TransactedOperation where T: struct
{
private T? oldValue;
private T value;
public TransactedValue(T value)
{
this.value = value;
}
public T Value
{
get
{
return this.value;
}
set
{
this.value = value;
}
}
protected override void PrepareEnlist(Transaction newTransaction, TransactionSources source)
{
oldValue = value;
}
protected override bool TryPrepareCommit(bool singlePhase)
{
return oldValue.HasValue;
}
protected override void DoCommit()
{
oldValue = null;
}
protected override void DoRollback()
{
value = oldValue.Value;
}
}
[STAThread]
public static void Main(string[] args)
{
TransactedValue<int> transacted = new TransactedValue<int>(100);
using (CommittableTransaction transaction = transacted.BeginTransaction())
{
transacted.Value = 200;
//transaction.Commit()
}
Console.WriteLine(transacted.Value);
}
|
| |