647 lines
27 KiB
C#
647 lines
27 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Activities.Statements
|
||
|
{
|
||
|
using System;
|
||
|
using System.Activities;
|
||
|
using System.Activities.Validation;
|
||
|
using System.Collections.Generic;
|
||
|
using System.ComponentModel;
|
||
|
using System.Runtime;
|
||
|
using System.Transactions;
|
||
|
using System.Windows.Markup;
|
||
|
using System.Activities.Expressions;
|
||
|
using System.Collections.ObjectModel;
|
||
|
|
||
|
[ContentProperty("Body")]
|
||
|
public sealed class TransactionScope : NativeActivity
|
||
|
{
|
||
|
const IsolationLevel defaultIsolationLevel = default(IsolationLevel);
|
||
|
|
||
|
InArgument<TimeSpan> timeout;
|
||
|
bool isTimeoutSetExplicitly;
|
||
|
Variable<RuntimeTransactionHandle> runtimeTransactionHandle;
|
||
|
bool abortInstanceOnTransactionFailure;
|
||
|
bool abortInstanceFlagWasExplicitlySet;
|
||
|
Delay nestedScopeTimeoutWorkflow;
|
||
|
Variable<bool> delayWasScheduled;
|
||
|
Variable<TimeSpan> nestedScopeTimeout;
|
||
|
Variable<ActivityInstance> nestedScopeTimeoutActivityInstance;
|
||
|
static string runtimeTransactionHandlePropertyName = typeof(RuntimeTransactionHandle).FullName;
|
||
|
const string AbortInstanceOnTransactionFailurePropertyName = "AbortInstanceOnTransactionFailure";
|
||
|
const string IsolationLevelPropertyName = "IsolationLevel";
|
||
|
const string BodyPropertyName = "Body";
|
||
|
|
||
|
public TransactionScope()
|
||
|
: base()
|
||
|
{
|
||
|
this.timeout = new InArgument<TimeSpan>(TimeSpan.FromMinutes(1));
|
||
|
this.runtimeTransactionHandle = new Variable<RuntimeTransactionHandle>();
|
||
|
this.abortInstanceOnTransactionFailure = true;
|
||
|
this.nestedScopeTimeout = new Variable<TimeSpan>();
|
||
|
this.delayWasScheduled = new Variable<bool>();
|
||
|
this.nestedScopeTimeoutActivityInstance = new Variable<ActivityInstance>();
|
||
|
|
||
|
base.Constraints.Add(ProcessParentChainConstraints());
|
||
|
base.Constraints.Add(ProcessChildSubtreeConstraints());
|
||
|
}
|
||
|
|
||
|
[DefaultValue(null)]
|
||
|
public Activity Body
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
[DefaultValue(true)]
|
||
|
public bool AbortInstanceOnTransactionFailure
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.abortInstanceOnTransactionFailure;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
this.abortInstanceOnTransactionFailure = value;
|
||
|
this.abortInstanceFlagWasExplicitlySet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IsolationLevel IsolationLevel
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
public InArgument<TimeSpan> Timeout
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.timeout;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
this.timeout = value;
|
||
|
this.isTimeoutSetExplicitly = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Delay NestedScopeTimeoutWorkflow
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.nestedScopeTimeoutWorkflow == null)
|
||
|
{
|
||
|
this.nestedScopeTimeoutWorkflow = new Delay
|
||
|
{
|
||
|
Duration = new InArgument<TimeSpan>(this.nestedScopeTimeout)
|
||
|
};
|
||
|
|
||
|
}
|
||
|
return this.nestedScopeTimeoutWorkflow;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override bool CanInduceIdle
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||
|
public bool ShouldSerializeIsolationLevel()
|
||
|
{
|
||
|
return this.IsolationLevel != defaultIsolationLevel;
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||
|
public bool ShouldSerializeTimeout()
|
||
|
{
|
||
|
return this.isTimeoutSetExplicitly;
|
||
|
}
|
||
|
|
||
|
protected override void CacheMetadata(NativeActivityMetadata metadata)
|
||
|
{
|
||
|
RuntimeArgument timeoutArgument = new RuntimeArgument("Timeout", typeof(TimeSpan), ArgumentDirection.In, false);
|
||
|
metadata.Bind(this.Timeout, timeoutArgument);
|
||
|
metadata.SetArgumentsCollection(new Collection<RuntimeArgument> { timeoutArgument });
|
||
|
metadata.AddImplementationChild(this.NestedScopeTimeoutWorkflow);
|
||
|
|
||
|
if (this.Body != null)
|
||
|
{
|
||
|
metadata.AddChild(this.Body);
|
||
|
}
|
||
|
|
||
|
metadata.AddImplementationVariable(this.runtimeTransactionHandle);
|
||
|
metadata.AddImplementationVariable(this.nestedScopeTimeout);
|
||
|
metadata.AddImplementationVariable(this.delayWasScheduled);
|
||
|
metadata.AddImplementationVariable(this.nestedScopeTimeoutActivityInstance);
|
||
|
}
|
||
|
|
||
|
Constraint ProcessParentChainConstraints()
|
||
|
{
|
||
|
DelegateInArgument<TransactionScope> element = new DelegateInArgument<TransactionScope> { Name = "element" };
|
||
|
DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext> { Name = "validationContext" };
|
||
|
DelegateInArgument<Activity> parent = new DelegateInArgument<Activity> { Name = "parent" };
|
||
|
|
||
|
return new Constraint<TransactionScope>
|
||
|
{
|
||
|
Body = new ActivityAction<TransactionScope, ValidationContext>
|
||
|
{
|
||
|
Argument1 = element,
|
||
|
Argument2 = validationContext,
|
||
|
Handler = new Sequence
|
||
|
{
|
||
|
Activities =
|
||
|
{
|
||
|
new ForEach<Activity>
|
||
|
{
|
||
|
Values = new GetParentChain
|
||
|
{
|
||
|
ValidationContext = validationContext,
|
||
|
},
|
||
|
Body = new ActivityAction<Activity>
|
||
|
{
|
||
|
Argument = parent,
|
||
|
Handler = new Sequence
|
||
|
{
|
||
|
Activities =
|
||
|
{
|
||
|
new If()
|
||
|
{
|
||
|
Condition = new Equal<Type, Type, bool>
|
||
|
{
|
||
|
Left = new ObtainType
|
||
|
{
|
||
|
Input = parent,
|
||
|
},
|
||
|
Right = new InArgument<Type>(context => typeof(TransactionScope))
|
||
|
},
|
||
|
Then = new Sequence
|
||
|
{
|
||
|
Activities =
|
||
|
{
|
||
|
new AssertValidation
|
||
|
{
|
||
|
IsWarning = true,
|
||
|
Assertion = new AbortInstanceFlagValidator
|
||
|
{
|
||
|
ParentActivity = parent,
|
||
|
TransactionScope = new InArgument<TransactionScope>(element)
|
||
|
},
|
||
|
Message = new InArgument<string>(SR.AbortInstanceOnTransactionFailureDoesNotMatch),
|
||
|
PropertyName = AbortInstanceOnTransactionFailurePropertyName
|
||
|
},
|
||
|
new AssertValidation
|
||
|
{
|
||
|
Assertion = new IsolationLevelValidator
|
||
|
{
|
||
|
ParentActivity = parent,
|
||
|
//CurrentIsolationLevel = new InArgument<IsolationLevel>(context => element.Get(context).IsolationLevel)
|
||
|
CurrentIsolationLevel = new InArgument<IsolationLevel>
|
||
|
{
|
||
|
Expression = new IsolationLevelValue
|
||
|
{
|
||
|
Scope = element
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
Message = new InArgument<string>(SR.IsolationLevelValidation),
|
||
|
PropertyName = IsolationLevelPropertyName
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
Constraint ProcessChildSubtreeConstraints()
|
||
|
{
|
||
|
DelegateInArgument<TransactionScope> element = new DelegateInArgument<TransactionScope> { Name = "element" };
|
||
|
DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext> { Name = "validationContext" };
|
||
|
DelegateInArgument<Activity> child = new DelegateInArgument<Activity> { Name = "child" };
|
||
|
Variable<bool> nestedCompensableActivity = new Variable<bool>();
|
||
|
|
||
|
return new Constraint<TransactionScope>
|
||
|
{
|
||
|
Body = new ActivityAction<TransactionScope, ValidationContext>
|
||
|
{
|
||
|
Argument1 = element,
|
||
|
Argument2 = validationContext,
|
||
|
Handler = new Sequence
|
||
|
{
|
||
|
Variables = { nestedCompensableActivity },
|
||
|
Activities =
|
||
|
{
|
||
|
new ForEach<Activity>
|
||
|
{
|
||
|
Values = new GetChildSubtree
|
||
|
{
|
||
|
ValidationContext = validationContext,
|
||
|
},
|
||
|
Body = new ActivityAction<Activity>
|
||
|
{
|
||
|
Argument = child,
|
||
|
Handler = new Sequence
|
||
|
{
|
||
|
Activities =
|
||
|
{
|
||
|
new If()
|
||
|
{
|
||
|
Condition = new Equal<Type, Type, bool>()
|
||
|
{
|
||
|
Left = new ObtainType
|
||
|
{
|
||
|
Input = new InArgument<Activity>(child)
|
||
|
},
|
||
|
Right = new InArgument<Type>(context => typeof(CompensableActivity))
|
||
|
},
|
||
|
Then = new Assign<bool>
|
||
|
{
|
||
|
To = new OutArgument<bool>(nestedCompensableActivity),
|
||
|
Value = new InArgument<bool>(true)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
new AssertValidation()
|
||
|
{
|
||
|
Assertion = new InArgument<bool>(new Not<bool, bool> { Operand = new VariableValue<bool> { Variable = nestedCompensableActivity } }),
|
||
|
Message = new InArgument<string>(SR.CompensableActivityInsideTransactionScopeActivity),
|
||
|
PropertyName = BodyPropertyName
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
protected override void Execute(NativeActivityContext context)
|
||
|
{
|
||
|
RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
|
||
|
Fx.Assert(transactionHandle != null, "RuntimeTransactionHandle is null");
|
||
|
|
||
|
RuntimeTransactionHandle foundHandle = context.Properties.Find(runtimeTransactionHandlePropertyName) as RuntimeTransactionHandle;
|
||
|
if (foundHandle == null)
|
||
|
{
|
||
|
//Note, once the property is registered, we cannot change the state of this flag
|
||
|
transactionHandle.AbortInstanceOnTransactionFailure = this.AbortInstanceOnTransactionFailure;
|
||
|
context.Properties.Add(transactionHandle.ExecutionPropertyName, transactionHandle);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//nested case
|
||
|
//foundHandle.IsRuntimeOwnedTransaction will be true only in the Invoke case within an ambient Sys.Tx transaction.
|
||
|
//If this TSA is nested inside the ambient transaction from Invoke, then the AbortInstanceFlag is always false since the RTH corresponding to the ambient
|
||
|
//transaction has this flag as false. In this case, we ignore if this TSA has this flag explicitly set to true.
|
||
|
if (!foundHandle.IsRuntimeOwnedTransaction && this.abortInstanceFlagWasExplicitlySet && (foundHandle.AbortInstanceOnTransactionFailure != this.AbortInstanceOnTransactionFailure))
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.AbortInstanceOnTransactionFailureDoesNotMatch));
|
||
|
}
|
||
|
|
||
|
if (foundHandle.SuppressTransaction)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotNestTransactionScopeWhenAmbientHandleIsSuppressed(this.DisplayName)));
|
||
|
}
|
||
|
transactionHandle = foundHandle;
|
||
|
}
|
||
|
|
||
|
Transaction transaction = transactionHandle.GetCurrentTransaction(context);
|
||
|
//Check if there is already a transaction (Requires Semantics)
|
||
|
if (transaction == null)
|
||
|
{
|
||
|
//If not, request one..
|
||
|
transactionHandle.RequestTransactionContext(context, OnContextAcquired, null);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Most likely, you are inside a nested TSA
|
||
|
if (transaction.IsolationLevel != this.IsolationLevel)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.IsolationLevelValidation));
|
||
|
}
|
||
|
|
||
|
//Check if the nested TSA had a timeout specified explicitly
|
||
|
if (this.isTimeoutSetExplicitly)
|
||
|
{
|
||
|
TimeSpan timeout = this.Timeout.Get(context);
|
||
|
this.delayWasScheduled.Set(context, true);
|
||
|
this.nestedScopeTimeout.Set(context, timeout);
|
||
|
|
||
|
this.nestedScopeTimeoutActivityInstance.Set(context, context.ScheduleActivity(this.NestedScopeTimeoutWorkflow, new CompletionCallback(OnDelayCompletion)));
|
||
|
}
|
||
|
|
||
|
//execute the Body under the current runtime transaction
|
||
|
ScheduleBody(context);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnContextAcquired(NativeActivityTransactionContext context, object state)
|
||
|
{
|
||
|
Fx.Assert(context != null, "ActivityTransactionContext was null");
|
||
|
|
||
|
TimeSpan transactionTimeout = this.Timeout.Get(context);
|
||
|
TransactionOptions transactionOptions = new TransactionOptions()
|
||
|
{
|
||
|
IsolationLevel = this.IsolationLevel,
|
||
|
Timeout = transactionTimeout
|
||
|
};
|
||
|
|
||
|
context.SetRuntimeTransaction(new CommittableTransaction(transactionOptions));
|
||
|
|
||
|
ScheduleBody(context);
|
||
|
}
|
||
|
|
||
|
void ScheduleBody(NativeActivityContext context)
|
||
|
{
|
||
|
if (this.Body != null)
|
||
|
{
|
||
|
context.ScheduleActivity(this.Body, new CompletionCallback(OnCompletion));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnCompletion(NativeActivityContext context, ActivityInstance instance)
|
||
|
{
|
||
|
RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
|
||
|
Fx.Assert(transactionHandle != null, "RuntimeTransactionHandle is null");
|
||
|
|
||
|
if (this.delayWasScheduled.Get(context))
|
||
|
{
|
||
|
transactionHandle.CompleteTransaction(context, new BookmarkCallback(OnTransactionComplete));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
transactionHandle.CompleteTransaction(context);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnDelayCompletion(NativeActivityContext context, ActivityInstance instance)
|
||
|
{
|
||
|
if (instance.State == ActivityInstanceState.Closed)
|
||
|
{
|
||
|
RuntimeTransactionHandle handle = context.Properties.Find(runtimeTransactionHandlePropertyName) as RuntimeTransactionHandle;
|
||
|
Fx.Assert(handle != null, "Internal error.. If we are here, there ought to be an ambient transaction handle");
|
||
|
handle.GetCurrentTransaction(context).Rollback();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnTransactionComplete(NativeActivityContext context, Bookmark bookmark, object state)
|
||
|
{
|
||
|
Fx.Assert(this.delayWasScheduled.Get(context), "Internal error..Delay should have been scheduled if we are here");
|
||
|
ActivityInstance delayActivityInstance = this.nestedScopeTimeoutActivityInstance.Get(context);
|
||
|
if (delayActivityInstance != null)
|
||
|
{
|
||
|
context.CancelChild(delayActivityInstance);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
class ObtainType : CodeActivity<Type>
|
||
|
{
|
||
|
public ObtainType()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public InArgument<Activity> Input
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
protected override void CacheMetadata(CodeActivityMetadata metadata)
|
||
|
{
|
||
|
RuntimeArgument inputArgument = new RuntimeArgument("Input", typeof(Activity), ArgumentDirection.In);
|
||
|
if (this.Input == null)
|
||
|
{
|
||
|
this.Input = new InArgument<Activity>();
|
||
|
}
|
||
|
metadata.Bind(this.Input, inputArgument);
|
||
|
|
||
|
RuntimeArgument resultArgument = new RuntimeArgument("Result", typeof(Type), ArgumentDirection.Out);
|
||
|
if (this.Result == null)
|
||
|
{
|
||
|
this.Result = new OutArgument<Type>();
|
||
|
}
|
||
|
metadata.Bind(this.Result, resultArgument);
|
||
|
|
||
|
metadata.SetArgumentsCollection(
|
||
|
new Collection<RuntimeArgument>
|
||
|
{
|
||
|
inputArgument,
|
||
|
resultArgument
|
||
|
});
|
||
|
}
|
||
|
|
||
|
protected override Type Execute(CodeActivityContext context)
|
||
|
{
|
||
|
return this.Input.Get(context).GetType();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class IsolationLevelValue : CodeActivity<IsolationLevel>
|
||
|
{
|
||
|
public InArgument<TransactionScope> Scope
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
protected override void CacheMetadata(CodeActivityMetadata metadata)
|
||
|
{
|
||
|
RuntimeArgument scopeArgument = new RuntimeArgument("Scope", typeof(TransactionScope), ArgumentDirection.In);
|
||
|
if (this.Scope == null)
|
||
|
{
|
||
|
this.Scope = new InArgument<TransactionScope>();
|
||
|
}
|
||
|
metadata.Bind(this.Scope, scopeArgument);
|
||
|
|
||
|
RuntimeArgument resultArgument = new RuntimeArgument("Result", typeof(IsolationLevel), ArgumentDirection.Out);
|
||
|
if (this.Result == null)
|
||
|
{
|
||
|
this.Result = new OutArgument<IsolationLevel>();
|
||
|
}
|
||
|
metadata.Bind(this.Result, resultArgument);
|
||
|
|
||
|
metadata.SetArgumentsCollection(
|
||
|
new Collection<RuntimeArgument>
|
||
|
{
|
||
|
scopeArgument,
|
||
|
resultArgument
|
||
|
});
|
||
|
}
|
||
|
|
||
|
protected override IsolationLevel Execute(CodeActivityContext context)
|
||
|
{
|
||
|
return this.Scope.Get(context).IsolationLevel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class IsolationLevelValidator : CodeActivity<bool>
|
||
|
{
|
||
|
public InArgument<Activity> ParentActivity
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
public InArgument<IsolationLevel> CurrentIsolationLevel
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
protected override void CacheMetadata(CodeActivityMetadata metadata)
|
||
|
{
|
||
|
RuntimeArgument parentActivityArgument = new RuntimeArgument("ParentActivity", typeof(Activity), ArgumentDirection.In);
|
||
|
if (this.ParentActivity == null)
|
||
|
{
|
||
|
this.ParentActivity = new InArgument<Activity>();
|
||
|
}
|
||
|
metadata.Bind(this.ParentActivity, parentActivityArgument);
|
||
|
|
||
|
RuntimeArgument isoLevelArgument = new RuntimeArgument("CurrentIsolationLevel", typeof(IsolationLevel), ArgumentDirection.In);
|
||
|
if (this.CurrentIsolationLevel == null)
|
||
|
{
|
||
|
this.CurrentIsolationLevel = new InArgument<IsolationLevel>();
|
||
|
}
|
||
|
metadata.Bind(this.CurrentIsolationLevel, isoLevelArgument);
|
||
|
|
||
|
RuntimeArgument resultArgument = new RuntimeArgument("Result", typeof(bool), ArgumentDirection.Out);
|
||
|
if (this.Result == null)
|
||
|
{
|
||
|
this.Result = new OutArgument<bool>();
|
||
|
}
|
||
|
metadata.Bind(this.Result, resultArgument);
|
||
|
|
||
|
metadata.SetArgumentsCollection(
|
||
|
new Collection<RuntimeArgument>
|
||
|
{
|
||
|
parentActivityArgument,
|
||
|
isoLevelArgument,
|
||
|
resultArgument
|
||
|
});
|
||
|
}
|
||
|
|
||
|
protected override bool Execute(CodeActivityContext context)
|
||
|
{
|
||
|
Activity parent = this.ParentActivity.Get(context);
|
||
|
|
||
|
if (parent != null)
|
||
|
{
|
||
|
TransactionScope transactionScope = parent as TransactionScope;
|
||
|
Fx.Assert(transactionScope != null, "ParentActivity was not of expected type");
|
||
|
|
||
|
if (transactionScope.IsolationLevel != this.CurrentIsolationLevel.Get(context))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class AbortInstanceFlagValidator : CodeActivity<bool>
|
||
|
{
|
||
|
public InArgument<Activity> ParentActivity
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
public InArgument<TransactionScope> TransactionScope
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
protected override void CacheMetadata(CodeActivityMetadata metadata)
|
||
|
{
|
||
|
RuntimeArgument parentActivityArgument = new RuntimeArgument("ParentActivity", typeof(Activity), ArgumentDirection.In);
|
||
|
if (this.ParentActivity == null)
|
||
|
{
|
||
|
this.ParentActivity = new InArgument<Activity>();
|
||
|
}
|
||
|
metadata.Bind(this.ParentActivity, parentActivityArgument);
|
||
|
|
||
|
RuntimeArgument txScopeArgument = new RuntimeArgument("TransactionScope", typeof(TransactionScope), ArgumentDirection.In);
|
||
|
if (this.TransactionScope == null)
|
||
|
{
|
||
|
this.TransactionScope = new InArgument<TransactionScope>();
|
||
|
}
|
||
|
metadata.Bind(this.TransactionScope, txScopeArgument);
|
||
|
|
||
|
RuntimeArgument resultArgument = new RuntimeArgument("Result", typeof(bool), ArgumentDirection.Out);
|
||
|
if (this.Result == null)
|
||
|
{
|
||
|
this.Result = new OutArgument<bool>();
|
||
|
}
|
||
|
metadata.Bind(this.Result, resultArgument);
|
||
|
|
||
|
metadata.SetArgumentsCollection(
|
||
|
new Collection<RuntimeArgument>
|
||
|
{
|
||
|
parentActivityArgument,
|
||
|
txScopeArgument,
|
||
|
resultArgument
|
||
|
});
|
||
|
}
|
||
|
|
||
|
protected override bool Execute(CodeActivityContext context)
|
||
|
{
|
||
|
Activity parent = this.ParentActivity.Get(context);
|
||
|
|
||
|
if (parent != null)
|
||
|
{
|
||
|
TransactionScope parentTransactionScope = parent as TransactionScope;
|
||
|
Fx.Assert(parentTransactionScope != null, "ParentActivity was not of expected type");
|
||
|
TransactionScope currentTransactionScope = this.TransactionScope.Get(context);
|
||
|
|
||
|
if (parentTransactionScope.AbortInstanceOnTransactionFailure != currentTransactionScope.AbortInstanceOnTransactionFailure)
|
||
|
{
|
||
|
//If the Inner TSA was default and still different from outer, we dont flag validation warning. See design spec for all variations
|
||
|
if (!currentTransactionScope.abortInstanceFlagWasExplicitlySet)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|