2025-11-28 00:35:46 +09:00

252 lines
8.6 KiB
C#

// <copyright file="TransactedComment.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
// </copyright>
// DISCLAIMER OF WARRANTY: The software is licensed “as-is.” You
// bear the risk of using it. Microsoft gives no express warranties,
// guarantees or conditions. You may have additional consumer rights
// under your local laws which this agreement cannot change. To the extent
// permitted under your local laws, Microsoft excludes the implied warranties
// of merchantability, fitness for a particular purpose and non-infringement.
namespace TransactedComment
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
/// <summary>
/// A transaction-aware store for building strings. It supports appending
/// and inspecting.
/// </summary>
/// <remarks>
/// The <see cref="System.Transactions.IEnlistmentNotification"/> interface
/// provides callbacks for the transaction system. This is a simple
/// example of a transactional resource manager. If your current resource
/// manager already implements <see cref="System.Transactions.IEnlistmentNotification"/>,
/// you probably do not need to write one yourself.
/// </remarks>
public class TransactedComment : IEnlistmentNotification
{
/// <summary>
/// Stores the official value of the comment.
/// </summary>
private StringBuilder commentValue;
/// <summary>
/// Stores the temprary value of the comment inside the transaction.
/// </summary>
private StringBuilder temporaryValue;
/// <summary>
/// enlistedTransaction is the one that has the right to affect
/// this TransactedComment, and to see the temporary value. If
/// it is set, then attempting to alter the TransactedComment from
/// outside a transaction or inside a different transaction is an error.
/// </summary>
private Transaction enlistedTransaction = null;
/// <summary>
/// Initializes a new instance of the TransactedComment class. This is
/// the default constructor.
/// </summary>
public TransactedComment()
: this(string.Empty)
{
}
/// <summary>
/// Initializes a new instance of the TransactedComment class.
/// </summary>
/// <param name="value">The initial value of the comment.</param>
public TransactedComment(string value)
{
this.commentValue = new StringBuilder(value);
this.temporaryValue = null;
}
/// <summary>
/// Gets the length of the transacted string. If this is
/// called within the transaction, it returns the length of
/// the transacted value. Otherwise, it returns the length of
/// the original value.
/// </summary>
public int Length
{
get
{
// If not in a transaction, or in a different transaction
// than the one we are enlisted in, return the publicly
// visible state.
if (
(Transaction.Current == null) ||
(this.enlistedTransaction != Transaction.Current))
{
return this.commentValue.Length;
}
else
{
return this.temporaryValue.Length;
}
}
}
/// <summary>
/// Make the transacted changes permanent.
/// </summary>
/// <param name="enlistment">The enlistment for the open tranacstion.</param>
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
// This copies the value to the permanent version and wipes out
// any temporary information, then informs the enlistment that we are
// all set.
this.commentValue = new StringBuilder(this.temporaryValue.ToString());
this.temporaryValue = null;
this.enlistedTransaction = null;
enlistment.Done();
}
/// <summary>
/// Discard the transacted changes.
/// </summary>
/// <param name="enlistment">The enlistment for the open tranacstion.</param>
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
this.temporaryValue = null;
this.enlistedTransaction = null;
enlistment.Done();
}
/// <summary>
/// Discard the transacted changes.
/// </summary>
/// <param name="enlistment">The enlistment for the open tranacstion.</param>
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
/// <summary>
/// Determine if the transaction can be committed.
/// </summary>
/// <param name="preparingEnlistment">A PreparingEnlistment object used
/// to send a response to the transaction manager.</param>
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
/// <summary>
/// Append text to the transacted string.
/// </summary>
/// <param name="text">The text to append.</param>
public void Append(string text)
{
// Make sure that we are in a sensible transaction context.
this.ValidateTransactionOrEnlist();
if (this.enlistedTransaction != null)
{
// If ValidateTransactionOrEnlist did not throw an error and
// left a value in enlistedTransaction, then we are in the right
// transaction and should act on the temporary value.
this.temporaryValue.Append(text);
}
else
{
// If we are not in a transaction and this TransactedComment has
// never been manipulated inside a transaction since its last Commit,
// just act on the permanent value.
this.commentValue.Append(text);
}
}
/// <summary>
/// Remove all text from the string.
/// </summary>
public void Clear()
{
// Make sure that we are in a sensible transaction context.
this.ValidateTransactionOrEnlist();
if (this.enlistedTransaction != null)
{
// If ValidateTransactionOrEnlist did not throw an error and left
// a value in enlistedTransaction, then we are in the right
// transaction and should act on the temporary value.
this.temporaryValue = new StringBuilder();
}
else
{
// If we are not in a transaction and this TransactedComment has
// never been manipulated inside a transaction since its last Commit,
// act on the permanent value.
this.commentValue = new StringBuilder();
}
}
/// <summary>
/// Gets the System.String that represents the transacted
/// transacted string. If this is called within the
/// transaction, it returns the transacted value.
/// Otherwise, it returns the original value.
/// </summary>
/// <returns>A System.String that represents the transacted
/// transacted string</returns>
public override string ToString()
{
// If we are not in a transaction, or we are in a different transaction
// than the one we enlisted in, return the publicly visible state.
if (
(Transaction.Current == null) ||
(this.enlistedTransaction != Transaction.Current))
{
return this.commentValue.ToString();
}
else
{
return this.temporaryValue.ToString();
}
}
/// <summary>
/// Test if we are in a transaction and enlist if needed.
/// </summary>
private void ValidateTransactionOrEnlist()
{
// We are in a transaction. The using (CurrentPSTransaction) block
// in the cmdlet caused Transaction.Current to be set correctly, so
// we can test against it here.
if (Transaction.Current != null)
{
// We have not yet been called inside of a transaction. So enlist
// in the transaction, and store our save point.
if (this.enlistedTransaction == null)
{
Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
this.enlistedTransaction = Transaction.Current;
this.temporaryValue = new StringBuilder(this.commentValue.ToString());
}
else
{
// We are already enlisted in a transaction.
if (Transaction.Current != this.enlistedTransaction)
{
throw new InvalidOperationException("Cannot modify string. It has been modified by another transaction.");
}
}
}
else
{
// We are not in a transaction.
// If we are not subscribed to a transaction, modify the underlying value.
if (this.enlistedTransaction != null)
{
throw new InvalidOperationException("Cannot modify string. It has been modified by another transaction.");
}
}
}
}
}