Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / System.Runtime.DurableInstancing / System / Runtime / DurableInstancing / InstancePersistenceContext.cs / 1305376 / InstancePersistenceContext.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------
namespace System.Runtime.DurableInstancing
{
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security;
using System.Threading;
using System.Transactions;
using System.Xml.Linq;
[Fx.Tag.XamlVisible(false)]
public sealed class InstancePersistenceContext
{
readonly TimeSpan timeout;
Transaction transaction;
bool freezeTransaction;
CommittableTransaction myTransaction;
int cancellationHandlerCalled;
internal InstancePersistenceContext(InstanceHandle handle, Transaction transaction)
: this(handle)
{
Fx.Assert(transaction != null, "Null Transaction passed to InstancePersistenceContext.");
// Let's take our own clone of the transaction. We need to do this because we might need to
// create a TransactionScope using the transaction and in cases where we are dealing with a
// transaction that is flowed into the workflow on a message, the DependentTransaction that the
// dispatcher creates and sets to Transaction.Current may already be Completed by the time a
// Save operation is done. And since TransactionScope creates a DependentTransaction, it won't
// be able to.
// We don't create another DependentClone because we are going to do a EnlistVolatile on the
// transaction ourselves.
this.transaction = transaction.Clone();
IsHostTransaction = true;
}
internal InstancePersistenceContext(InstanceHandle handle, TimeSpan timeout)
: this(handle)
{
this.timeout = timeout;
}
InstancePersistenceContext(InstanceHandle handle)
{
Fx.Assert(handle != null, "Null handle passed to InstancePersistenceContext.");
InstanceHandle = handle;
// Fork a copy of the current view to be the new working view. It starts with no query results.
InstanceView newView = handle.View.Clone();
newView.InstanceStoreQueryResults = null;
InstanceView = newView;
this.cancellationHandlerCalled = 0;
}
public InstanceHandle InstanceHandle { get; private set; }
public InstanceView InstanceView { get; private set; }
public long InstanceVersion
{
get
{
return InstanceHandle.Version;
}
}
public Guid LockToken
{
get
{
Fx.Assert(InstanceHandle.Owner == null || InstanceHandle.Owner.OwnerToken == InstanceView.InstanceOwner.OwnerToken, "Mismatched lock tokens.");
// If the handle doesn't own the lock yet, return the owner LockToken, which is needed to check whether this owner already owns locks.
return InstanceHandle.Owner == null ? Guid.Empty : InstanceHandle.Owner.OwnerToken;
}
}
public object UserContext
{
get
{
return InstanceHandle.ProviderObject;
}
}
bool CancelRequested { get; set; }
ExecuteAsyncResult RootAsyncResult { get; set; }
ExecuteAsyncResult LastAsyncResult { get; set; }
bool IsHostTransaction { get; set; }
bool Active
{
get
{
return RootAsyncResult != null;
}
}
public void SetCancellationHandler(Action cancellationHandler)
{
ThrowIfNotActive("SetCancellationHandler");
LastAsyncResult.CancellationHandler = cancellationHandler;
if (CancelRequested && (cancellationHandler != null))
{
try
{
if (Interlocked.CompareExchange(ref this.cancellationHandlerCalled, 0, 1) == 0)
{
cancellationHandler(this);
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
throw Fx.Exception.AsError(new CallbackException(SRCore.OnCancelRequestedThrew, exception));
}
}
}
public void BindInstanceOwner(Guid instanceOwnerId, Guid lockToken)
{
if (instanceOwnerId == Guid.Empty)
{
throw Fx.Exception.Argument("instanceOwnerId", SRCore.GuidCannotBeEmpty);
}
if (lockToken == Guid.Empty)
{
throw Fx.Exception.Argument("lockToken", SRCore.GuidCannotBeEmpty);
}
ThrowIfNotActive("BindInstanceOwner");
InstanceOwner owner = InstanceHandle.Store.GetOrCreateOwner(instanceOwnerId, lockToken);
InstanceView.BindOwner(owner);
IsHandleDoomedByRollback = true;
InstanceHandle.BindOwner(owner);
}
public void BindInstance(Guid instanceId)
{
if (instanceId == Guid.Empty)
{
throw Fx.Exception.Argument("instanceId", SRCore.GuidCannotBeEmpty);
}
ThrowIfNotActive("BindInstance");
InstanceView.BindInstance(instanceId);
IsHandleDoomedByRollback = true;
InstanceHandle.BindInstance(instanceId);
}
public void BindEvent(InstancePersistenceEvent persistenceEvent)
{
if (persistenceEvent == null)
{
throw Fx.Exception.ArgumentNull("persistenceEvent");
}
ThrowIfNotActive("BindEvent");
if (!InstanceView.IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToOwner));
}
IsHandleDoomedByRollback = true;
InstanceHandle.BindOwnerEvent(persistenceEvent);
}
public void BindAcquiredLock(long instanceVersion)
{
if (instanceVersion < 0)
{
throw Fx.Exception.ArgumentOutOfRange("instanceVersion", instanceVersion, SRCore.InvalidLockToken);
}
ThrowIfNotActive("BindAcquiredLock");
// This call has a synchronization, so we are guaranteed it is only successful once.
InstanceView.BindLock(instanceVersion);
IsHandleDoomedByRollback = true;
InstanceHandle.Bind(instanceVersion);
}
public void BindReclaimedLock(long instanceVersion, TimeSpan timeout)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("BindReclaimedLock", instanceVersion, timeout);
if (!wait.Wait(timeout))
{
InstanceHandle.CancelReclaim(new TimeoutException(SRCore.TimedOutWaitingForLockResolution));
}
ConcludeBindReclaimedLockHelper();
}
public IAsyncResult BeginBindReclaimedLock(long instanceVersion, TimeSpan timeout, AsyncCallback callback, object state)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("BeginBindReclaimedLock", instanceVersion, timeout);
return new BindReclaimedLockAsyncResult(this, wait, timeout, callback, state);
}
public void EndBindReclaimedLock(IAsyncResult result)
{
BindReclaimedLockAsyncResult.End(result);
}
public Exception CreateBindReclaimedLockException(long instanceVersion)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("CreateBindReclaimedLockException", instanceVersion, TimeSpan.MaxValue);
return new BindReclaimedLockException(wait);
}
AsyncWaitHandle InitiateBindReclaimedLockHelper(string methodName, long instanceVersion, TimeSpan timeout)
{
if (instanceVersion < 0)
{
throw Fx.Exception.ArgumentOutOfRange("instanceVersion", instanceVersion, SRCore.InvalidLockToken);
}
TimeoutHelper.ThrowIfNegativeArgument(timeout);
ThrowIfNotActive(methodName);
// This call has a synchronization, so we are guaranteed it is only successful once.
InstanceView.StartBindLock(instanceVersion);
IsHandleDoomedByRollback = true;
AsyncWaitHandle wait = InstanceHandle.StartReclaim(instanceVersion);
if (wait == null)
{
InstanceHandle.Free();
throw Fx.Exception.AsError(new InstanceHandleConflictException(LastAsyncResult.CurrentCommand.Name, InstanceView.InstanceId));
}
return wait;
}
void ConcludeBindReclaimedLockHelper()
{
// If FinishReclaim doesn't throw an exception, we are done - the reclaim was successful.
// The Try / Finally makes up for the reverse order of setting the handle, then the view.
long instanceVersion = -1;
try
{
if (!InstanceHandle.FinishReclaim(ref instanceVersion))
{
InstanceHandle.Free();
throw Fx.Exception.AsError(new InstanceHandleConflictException(LastAsyncResult.CurrentCommand.Name, InstanceView.InstanceId));
}
Fx.Assert(instanceVersion >= 0, "Where did the instance version go?");
}
finally
{
if (instanceVersion >= 0)
{
InstanceView.FinishBindLock(instanceVersion);
}
}
}
public void PersistedInstance(IDictionary data)
{
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("PersistedInstance");
InstanceView.InstanceData = data.ReadOnlyCopy(true);
InstanceView.InstanceDataConsistency = InstanceValueConsistency.None;
InstanceView.InstanceState = InstanceState.Initialized;
}
public void LoadedInstance(InstanceState state, IDictionary instanceData, IDictionary instanceMetadata, IDictionary> associatedInstanceKeyMetadata, IDictionary> completedInstanceKeyMetadata)
{
if (state == InstanceState.Uninitialized)
{
if (instanceData != null && instanceData.Count > 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.UninitializedCannotHaveData));
}
}
else if (state == InstanceState.Completed)
{
if (associatedInstanceKeyMetadata != null && associatedInstanceKeyMetadata.Count > 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CompletedMustNotHaveAssociatedKeys));
}
}
else if (state != InstanceState.Initialized)
{
throw Fx.Exception.Argument("state", SRCore.InvalidInstanceState);
}
ThrowIfNoInstance();
ThrowIfNotActive("PersistedInstance");
InstanceValueConsistency consistency = InstanceView.IsBoundToLock || state == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
ReadOnlyDictionary instanceDataCopy = instanceData.ReadOnlyCopy(false);
ReadOnlyDictionary instanceMetadataCopy = instanceMetadata.ReadOnlyCopy(false);
Dictionary keysCopy = null;
int totalKeys = (associatedInstanceKeyMetadata != null ? associatedInstanceKeyMetadata.Count : 0) + (completedInstanceKeyMetadata != null ? completedInstanceKeyMetadata.Count : 0);
if (totalKeys > 0)
{
keysCopy = new Dictionary(totalKeys);
}
if (associatedInstanceKeyMetadata != null && associatedInstanceKeyMetadata.Count > 0)
{
foreach (KeyValuePair> keyMetadata in associatedInstanceKeyMetadata)
{
InstanceKeyView view = new InstanceKeyView(keyMetadata.Key);
view.InstanceKeyState = InstanceKeyState.Associated;
view.InstanceKeyMetadata = keyMetadata.Value.ReadOnlyCopy(false);
view.InstanceKeyMetadataConsistency = InstanceView.IsBoundToLock ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
keysCopy.Add(view.InstanceKey, view);
}
}
if (completedInstanceKeyMetadata != null && completedInstanceKeyMetadata.Count > 0)
{
foreach (KeyValuePair> keyMetadata in completedInstanceKeyMetadata)
{
InstanceKeyView view = new InstanceKeyView(keyMetadata.Key);
view.InstanceKeyState = InstanceKeyState.Completed;
view.InstanceKeyMetadata = keyMetadata.Value.ReadOnlyCopy(false);
view.InstanceKeyMetadataConsistency = consistency;
keysCopy.Add(view.InstanceKey, view);
}
}
InstanceView.InstanceState = state;
InstanceView.InstanceData = instanceDataCopy;
InstanceView.InstanceDataConsistency = consistency;
InstanceView.InstanceMetadata = instanceMetadataCopy;
InstanceView.InstanceMetadataConsistency = consistency;
InstanceView.InstanceKeys = keysCopy == null ? null : new ReadOnlyDictionary(keysCopy, false);
InstanceView.InstanceKeysConsistency = consistency;
}
public void CompletedInstance()
{
ThrowIfNotLocked();
ThrowIfUninitialized();
ThrowIfCompleted();
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
foreach (KeyValuePair key in InstanceView.InstanceKeys)
{
if (key.Value.InstanceKeyState == InstanceKeyState.Associated)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotCompleteWithKeys));
}
}
}
ThrowIfNotTransactional("CompletedInstance");
InstanceView.InstanceState = InstanceState.Completed;
}
public void ReadInstanceMetadata(IDictionary metadata, bool complete)
{
ThrowIfNoInstance();
ThrowIfNotActive("ReadInstanceMetadata");
if (InstanceView.InstanceMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
InstanceView.InstanceMetadata = metadata.ReadOnlyCopy(false);
InstanceView.InstanceMetadataConsistency = InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
}
else
{
if ((InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed) && (InstanceView.InstanceMetadataConsistency & InstanceValueConsistency.InDoubt) != 0)
{
// In this case, prefer throwing out old data and keeping only authoritative data.
InstanceView.InstanceMetadata = metadata.ReadOnlyMergeInto(null, false);
InstanceView.InstanceMetadataConsistency = InstanceValueConsistency.Partial;
}
else
{
InstanceView.InstanceMetadata = metadata.ReadOnlyMergeInto(InstanceView.InstanceMetadata, false);
InstanceView.InstanceMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
}
public void WroteInstanceMetadataValue(XName name, InstanceValue value)
{
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("WroteInstanceMetadataValue");
InstanceView.AccumulatedMetadataWrites[name] = value;
}
public void AssociatedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("AssociatedInstanceKey");
Dictionary copy = new Dictionary(InstanceView.InstanceKeys);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0 && copy.ContainsKey(key))
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyAssociated));
}
InstanceKeyView keyView = new InstanceKeyView(key);
keyView.InstanceKeyState = InstanceKeyState.Associated;
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.None;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionary(copy, false);
}
public void CompletedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("CompletedInstanceKey");
InstanceKeyView existingKeyView;
InstanceView.InstanceKeys.TryGetValue(key, out existingKeyView);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
if (existingKeyView != null)
{
if (existingKeyView.InstanceKeyState == InstanceKeyState.Completed)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyCompleted));
}
}
else if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.Partial) == 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
}
if (existingKeyView != null)
{
existingKeyView.InstanceKeyState = InstanceKeyState.Completed;
}
else
{
Dictionary copy = new Dictionary(InstanceView.InstanceKeys);
InstanceKeyView keyView = new InstanceKeyView(key);
keyView.InstanceKeyState = InstanceKeyState.Completed;
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionary(copy, false);
}
}
public void UnassociatedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("UnassociatedInstanceKey");
InstanceKeyView existingKeyView;
InstanceView.InstanceKeys.TryGetValue(key, out existingKeyView);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
if (existingKeyView != null)
{
if (existingKeyView.InstanceKeyState == InstanceKeyState.Associated)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotCompleted));
}
}
else if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.Partial) == 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyUnassociated));
}
}
if (existingKeyView != null)
{
Dictionary copy = new Dictionary(InstanceView.InstanceKeys);
copy.Remove(key);
InstanceView.InstanceKeys = new ReadOnlyDictionary(copy, false);
}
}
public void ReadInstanceKeyMetadata(Guid key, IDictionary metadata, bool complete)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNoInstance();
ThrowIfNotActive("ReadInstanceKeyMetadata");
InstanceKeyView keyView;
if (!InstanceView.InstanceKeys.TryGetValue(key, out keyView))
{
if (InstanceView.InstanceKeysConsistency == InstanceValueConsistency.None)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
Dictionary copy = new Dictionary(InstanceView.InstanceKeys);
keyView = new InstanceKeyView(key);
if (complete)
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyCopy(false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.None;
}
else
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(null, false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
}
if (!InstanceView.IsBoundToLock && InstanceView.InstanceState != InstanceState.Completed)
{
keyView.InstanceKeyMetadataConsistency |= InstanceValueConsistency.InDoubt;
}
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionary(copy, false);
}
else
{
if (keyView.InstanceKeyMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyCopy(false);
keyView.InstanceKeyMetadataConsistency = InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
}
else
{
if ((InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed) && (keyView.InstanceKeyMetadataConsistency & InstanceValueConsistency.InDoubt) != 0)
{
// In this case, prefer throwing out old data and keeping only authoritative data.
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(null, false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
}
else
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(keyView.InstanceKeyMetadata, false);
keyView.InstanceKeyMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
}
}
public void WroteInstanceKeyMetadataValue(Guid key, XName name, InstanceValue value)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("WroteInstanceKeyMetadataValue");
InstanceKeyView keyView;
if (!InstanceView.InstanceKeys.TryGetValue(key, out keyView))
{
if (InstanceView.InstanceKeysConsistency == InstanceValueConsistency.None)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
if (!value.IsWriteOnly() && !value.IsDeletedValue)
{
Dictionary copy = new Dictionary(InstanceView.InstanceKeys);
keyView = new InstanceKeyView(key);
keyView.AccumulatedMetadataWrites.Add(name, value);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionary(copy, false);
InstanceView.InstanceKeysConsistency |= InstanceValueConsistency.Partial;
}
}
else
{
keyView.AccumulatedMetadataWrites.Add(name, value);
}
}
public void ReadInstanceOwnerMetadata(IDictionary metadata, bool complete)
{
ThrowIfNoOwner();
ThrowIfNotActive("ReadInstanceOwnerMetadata");
if (InstanceView.InstanceOwnerMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
InstanceView.InstanceOwnerMetadata = metadata.ReadOnlyCopy(false);
InstanceView.InstanceOwnerMetadataConsistency = InstanceValueConsistency.InDoubt;
}
else
{
InstanceView.InstanceOwnerMetadata = metadata.ReadOnlyMergeInto(InstanceView.InstanceOwnerMetadata, false);
InstanceView.InstanceOwnerMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
public void WroteInstanceOwnerMetadataValue(XName name, InstanceValue value)
{
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNoOwner();
ThrowIfNotTransactional("WroteInstanceOwnerMetadataValue");
InstanceView.AccumulatedOwnerMetadataWrites.Add(name, value);
}
public void QueriedInstanceStore(InstanceStoreQueryResult queryResult)
{
if (queryResult == null)
{
throw Fx.Exception.ArgumentNull("queryResult");
}
ThrowIfNotActive("QueriedInstanceStore");
InstanceView.QueryResultsBacking.Add(queryResult);
}
[Fx.Tag.Throws.Timeout("The operation timed out.")]
[Fx.Tag.Throws(typeof(OperationCanceledException), "The operation was canceled because the InstanceHandle has been freed.")]
[Fx.Tag.Throws(typeof(InstancePersistenceException), "A command failed.")]
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree")]
public void Execute(InstancePersistenceCommand command, TimeSpan timeout)
{
if (command == null)
{
throw Fx.Exception.ArgumentNull("command");
}
ThrowIfNotActive("Execute");
try
{
ReconcileTransaction();
ExecuteAsyncResult.End(new ExecuteAsyncResult(this, command, timeout));
}
catch (TimeoutException)
{
InstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
InstanceHandle.Free();
throw;
}
}
// For each level of hierarchy of command execution, only one BeginExecute may be pending at a time.
[Fx.Tag.InheritThrows(From = "Execute")]
public IAsyncResult BeginExecute(InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
{
if (command == null)
{
throw Fx.Exception.ArgumentNull("command");
}
ThrowIfNotActive("BeginExecute");
try
{
ReconcileTransaction();
return new ExecuteAsyncResult(this, command, timeout, callback, state);
}
catch (TimeoutException)
{
InstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
InstanceHandle.Free();
throw;
}
}
[Fx.Tag.InheritThrows(From = "Execute")]
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", Conditional = "!result.IsCompleted")]
public void EndExecute(IAsyncResult result)
{
ExecuteAsyncResult.End(result);
}
internal Transaction Transaction
{
get
{
return this.transaction;
}
}
internal bool IsHandleDoomedByRollback { get; private set; }
internal void RequireTransaction()
{
if (this.transaction != null)
{
return;
}
Fx.AssertAndThrow(!this.freezeTransaction, "RequireTransaction called when transaction is frozen.");
Fx.AssertAndThrow(Active, "RequireTransaction called when no command is active.");
// It's ok if some time has passed since the timeout value was acquired, it is ok to run long. This transaction is not generally responsible
// for timing out the Execute operation. The exception to this rule is during Commit.
this.myTransaction = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = this.timeout });
Transaction clone = this.myTransaction.Clone();
RootAsyncResult.SetInteriorTransaction(this.myTransaction, true);
this.transaction = clone;
}
internal void PrepareForReuse()
{
Fx.AssertAndThrow(!Active, "Prior use not yet complete!");
Fx.AssertAndThrow(IsHostTransaction, "Can only reuse contexts with host transactions.");
}
internal void NotifyHandleFree()
{
CancelRequested = true;
ExecuteAsyncResult lastAsyncResult = LastAsyncResult;
Action onCancel = lastAsyncResult == null ? null : lastAsyncResult.CancellationHandler;
if (onCancel != null)
{
try
{
if (Interlocked.CompareExchange(ref this.cancellationHandlerCalled, 0, 1) == 0)
{
onCancel(this);
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
throw Fx.Exception.AsError(new CallbackException(SRCore.OnCancelRequestedThrew, exception));
}
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree")]
internal static InstanceView OuterExecute(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout)
{
try
{
return ExecuteAsyncResult.End(new ExecuteAsyncResult(initialInstanceHandle, command, transaction, timeout));
}
catch (TimeoutException)
{
initialInstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
initialInstanceHandle.Free();
throw;
}
}
internal static IAsyncResult BeginOuterExecute(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
{
try
{
return new ExecuteAsyncResult(initialInstanceHandle, command, transaction, timeout, callback, state);
}
catch (TimeoutException)
{
initialInstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
initialInstanceHandle.Free();
throw;
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", Conditional = "!result.IsCompleted")]
internal static InstanceView EndOuterExecute(IAsyncResult result)
{
InstanceView finalState = ExecuteAsyncResult.End(result);
if (finalState == null)
{
throw Fx.Exception.Argument("result", SRCore.InvalidAsyncResult);
}
return finalState;
}
void ThrowIfNotLocked()
{
if (!InstanceView.IsBoundToLock)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresLock));
}
}
void ThrowIfNoInstance()
{
if (!InstanceView.IsBoundToInstance)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresInstance));
}
}
void ThrowIfNoOwner()
{
if (!InstanceView.IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresOwner));
}
}
void ThrowIfCompleted()
{
if (InstanceView.IsBoundToLock && InstanceView.InstanceState == InstanceState.Completed)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresNotCompleted));
}
}
void ThrowIfUninitialized()
{
if (InstanceView.IsBoundToLock && InstanceView.InstanceState == InstanceState.Uninitialized)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresNotUninitialized));
}
}
void ThrowIfNotActive(string methodName)
{
if (!Active)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.OutsideInstanceExecutionScope(methodName)));
}
}
void ThrowIfNotTransactional(string methodName)
{
ThrowIfNotActive(methodName);
if (RootAsyncResult.CurrentCommand.IsTransactionEnlistmentOptional)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.OutsideTransactionalCommand(methodName)));
}
}
void ReconcileTransaction()
{
// If the provider fails to flow the transaction, that's fine, we don't consider that a request
// not to use one.
Transaction transaction = Transaction.Current;
if (transaction != null)
{
if (this.transaction == null)
{
if (this.freezeTransaction)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.MustSetTransactionOnFirstCall));
}
RootAsyncResult.SetInteriorTransaction(transaction, false);
this.transaction = transaction;
}
else if (!transaction.Equals(this.transaction))
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotReplaceTransaction));
}
}
this.freezeTransaction = true;
}
class ExecuteAsyncResult : AsyncResult, ISinglePhaseNotification
{
static AsyncCompletion onAcquireContext = new AsyncCompletion(OnAcquireContext);
static AsyncCompletion onTryCommand = new AsyncCompletion(OnTryCommand);
static AsyncCompletion onCommit = new AsyncCompletion(OnCommit);
static Action