|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Statements
{
using System.Activities;
using System.Activities.Expressions;
using System.Activities.Persistence;
using System.Activities.Tracking;
using System.Activities.Validation;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Transactions;
using System.Xml.Linq;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel.Compiler;
using ValidationError = System.Activities.Validation.ValidationError;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
using System.Runtime.Serialization;
[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces",
Justification = "The type name 'Interop' conflicts in whole or in part with the namespace name 'System.Web.Services.Interop' - not common usage")]
[Obsolete("The WF3 Types are deprecated. Instead, please use the new WF4 Types from System.Activities.*")]
public sealed class Interop : NativeActivity, ICustomTypeDescriptor
{
static Func<TimerExtension> getDefaultTimerExtension = new Func<TimerExtension>(GetDefaultTimerExtension);
static Func<InteropPersistenceParticipant> getInteropPersistenceParticipant = new Func<InteropPersistenceParticipant>(GetInteropPersistenceParticipant);
Dictionary<string, Argument> properties;
Dictionary<string, object> metaProperties;
System.Workflow.ComponentModel.Activity v1Activity;
IList<PropertyInfo> outputPropertyDefinitions;
HashSet<string> extraDynamicArguments;
bool exposedBodyPropertiesCacheIsValid;
IList<InteropProperty> exposedBodyProperties;
Variable<InteropExecutor> interopActivityExecutor;
Variable<RuntimeTransactionHandle> runtimeTransactionHandle;
BookmarkCallback onResumeBookmark;
CompletionCallback onPersistComplete;
BookmarkCallback onTransactionComplete;
Type activityType;
Persist persistActivity;
internal const string InArgumentSuffix = "In";
internal const string OutArgumentSuffix = "Out";
Variable<bool> persistOnClose;
Variable<InteropEnlistment> interopEnlistment;
Variable<Exception> outstandingException;
object thisLock;
// true if the body type is a valid activity. used so we can have delayed validation support in the designer
bool hasValidBody;
// true if the V3 activity property names will conflict with our generated argument names
bool hasNameCollision;
public Interop()
: base()
{
this.interopActivityExecutor = new Variable<InteropExecutor>();
this.runtimeTransactionHandle = new Variable<RuntimeTransactionHandle>();
this.persistOnClose = new Variable<bool>();
this.interopEnlistment = new Variable<InteropEnlistment>();
this.outstandingException = new Variable<Exception>();
this.onResumeBookmark = new BookmarkCallback(this.OnResumeBookmark);
this.persistActivity = new Persist();
this.thisLock = new object();
base.Constraints.Add(ProcessAdvancedConstraints());
}
[DefaultValue(null)]
public Type ActivityType
{
get
{
return this.activityType;
}
set
{
if (value != this.activityType)
{
this.hasValidBody = false;
if (value != null)
{
if (typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(value)
&& value.GetConstructor(Type.EmptyTypes) != null)
{
this.hasValidBody = true;
}
}
this.activityType = value;
if (this.metaProperties != null)
{
this.metaProperties.Clear();
}
if (this.outputPropertyDefinitions != null)
{
this.outputPropertyDefinitions.Clear();
}
if (this.properties != null)
{
this.properties.Clear();
}
if (this.exposedBodyProperties != null)
{
for (int i = 0; i < this.exposedBodyProperties.Count; i++)
{
this.exposedBodyProperties[i].Invalidate();
}
this.exposedBodyProperties.Clear();
}
this.exposedBodyPropertiesCacheIsValid = false;
this.v1Activity = null;
}
}
}
[Browsable(false)]
public IDictionary<string, Argument> ActivityProperties
{
get
{
if (this.properties == null)
{
this.properties = new Dictionary<string, Argument>();
}
return this.properties;
}
}
[Browsable(false)]
public IDictionary<string, object> ActivityMetaProperties
{
get
{
if (this.metaProperties == null)
{
this.metaProperties = new Dictionary<string, object>();
}
return this.metaProperties;
}
}
protected override bool CanInduceIdle
{
get
{
return true;
}
}
internal System.Workflow.ComponentModel.Activity ComponentModelActivity
{
get
{
if (this.v1Activity == null && this.ActivityType != null)
{
Debug.Assert(this.hasValidBody, "should only be called when we have a valid body");
this.v1Activity = CreateActivity();
}
return this.v1Activity;
}
}
internal IList<PropertyInfo> OutputPropertyDefinitions
{
get
{
return this.outputPropertyDefinitions;
}
}
internal bool HasNameCollision
{
get
{
return this.hasNameCollision;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
if (this.extraDynamicArguments != null)
{
this.extraDynamicArguments.Clear();
}
this.v1Activity = null;
if (this.hasValidBody)
{
//Cache the output properties prop info for look up.
this.outputPropertyDefinitions = new List<PropertyInfo>();
//Cache the extra property definitions for look up in OnOpen
if (this.properties != null)
{
if (this.extraDynamicArguments == null)
{
this.extraDynamicArguments = new HashSet<string>();
}
foreach (string name in properties.Keys)
{
this.extraDynamicArguments.Add(name);
}
}
//Create matched pair of RuntimeArguments for every property: Property (InArgument) & PropertyOut (Argument)
PropertyInfo[] bodyProperties = this.ActivityType.GetProperties();
// recheck for name collisions
this.hasNameCollision = InteropEnvironment.ParameterHelper.HasPropertyNameCollision(bodyProperties);
foreach (PropertyInfo propertyInfo in bodyProperties)
{
if (InteropEnvironment.ParameterHelper.IsBindable(propertyInfo))
{
string propertyInName;
//If there are any Property/PropertyOut name pairs already extant, we fall back to renaming the InArgument half of the pair as well
if (this.hasNameCollision)
{
propertyInName = propertyInfo.Name + Interop.InArgumentSuffix;
}
else
{
propertyInName = propertyInfo.Name;
}
//We always rename the OutArgument half of the pair
string propertyOutName = propertyInfo.Name + Interop.OutArgumentSuffix;
RuntimeArgument inArgument = new RuntimeArgument(propertyInName, propertyInfo.PropertyType, ArgumentDirection.In);
RuntimeArgument outArgument = new RuntimeArgument(propertyOutName, propertyInfo.PropertyType, ArgumentDirection.Out);
if (this.properties != null)
{
Argument inBinding = null;
if (this.properties.TryGetValue(propertyInName, out inBinding))
{
if (inBinding.Direction != ArgumentDirection.In)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropArgumentDirectionMismatch, propertyInName, propertyOutName));
}
this.extraDynamicArguments.Remove(propertyInName);
metadata.Bind(inBinding, inArgument);
}
Argument outBinding = null;
if (this.properties.TryGetValue(propertyOutName, out outBinding))
{
this.extraDynamicArguments.Remove(propertyOutName);
metadata.Bind(outBinding, outArgument);
}
}
metadata.AddArgument(inArgument);
metadata.AddArgument(outArgument);
this.outputPropertyDefinitions.Add(propertyInfo);
}
}
}
metadata.SetImplementationVariablesCollection(
new Collection<Variable>
{
this.interopActivityExecutor,
this.runtimeTransactionHandle,
this.persistOnClose,
this.interopEnlistment,
this.outstandingException
});
metadata.AddImplementationChild(this.persistActivity);
if (!this.hasValidBody)
{
if (this.ActivityType == null)
{
metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyNotSet, this.DisplayName));
}
else
{
// Body needs to be a WF 3.0 activity
if (!typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(this.ActivityType))
{
metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropWrongBody, this.DisplayName));
}
// and have a default ctor
if (this.ActivityType.GetConstructor(Type.EmptyTypes) == null)
{
metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyMustHavePublicDefaultConstructor, this.DisplayName));
}
}
}
else
{
if (this.extraDynamicArguments != null && this.extraDynamicArguments.Count > 0)
{
metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.AttemptToBindUnknownProperties, this.DisplayName, this.extraDynamicArguments.First()));
}
else
{
try
{
InitializeMetaProperties(this.ComponentModelActivity);
// We call InitializeDefinitionForRuntime in the first call to execute to
// make sure it only happens once.
}
catch (InvalidOperationException e)
{
metadata.AddValidationError(e.Message);
}
}
}
metadata.AddDefaultExtensionProvider(getDefaultTimerExtension);
metadata.AddDefaultExtensionProvider(getInteropPersistenceParticipant);
}
static TimerExtension GetDefaultTimerExtension()
{
return new DurableTimerExtension();
}
static InteropPersistenceParticipant GetInteropPersistenceParticipant()
{
return new InteropPersistenceParticipant();
}
protected override void Execute(NativeActivityContext context)
{
//
WorkflowRuntimeService workflowRuntimeService = context.GetExtension<WorkflowRuntimeService>();
if (workflowRuntimeService != null && !(workflowRuntimeService is ExternalDataExchangeService))
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropWorkflowRuntimeServiceNotSupported));
}
lock (this.thisLock)
{
((System.Workflow.ComponentModel.IDependencyObjectAccessor)this.ComponentModelActivity).InitializeDefinitionForRuntime(null);
}
if (!this.ComponentModelActivity.Enabled)
{
return;
}
System.Workflow.ComponentModel.Activity activityInstance = CreateActivity();
InitializeMetaProperties(activityInstance);
activityInstance.SetValue(WorkflowExecutor.WorkflowInstanceIdProperty, context.WorkflowInstanceId);
InteropExecutor interopExecutor = new InteropExecutor(context.WorkflowInstanceId, activityInstance, this.OutputPropertyDefinitions, this.ComponentModelActivity);
if (!interopExecutor.HasCheckedForTrackingParticipant)
{
interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
interopExecutor.HasCheckedForTrackingParticipant = true;
}
this.interopActivityExecutor.Set(context, interopExecutor);
//Register the Handle as an execution property so that we can call GetCurrentTransaction or
//RequestTransactionContext on it later
RuntimeTransactionHandle runtimeTransactionHandle = this.runtimeTransactionHandle.Get(context);
context.Properties.Add(runtimeTransactionHandle.ExecutionPropertyName, runtimeTransactionHandle);
try
{
using (new ServiceEnvironment(activityInstance))
{
using (InteropEnvironment interopEnvironment = new InteropEnvironment(
interopExecutor, context,
this.onResumeBookmark,
this,
runtimeTransactionHandle.GetCurrentTransaction(context)))
{
interopEnvironment.Execute(this.ComponentModelActivity, context);
}
}
}
catch (Exception exception)
{
if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
{
throw;
}
// We are not ----ing the exception. The exception is saved in this.outstandingException.
// We will throw the exception from OnPersistComplete.
}
}
protected override void Cancel(NativeActivityContext context)
{
InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);
if (!interopExecutor.HasCheckedForTrackingParticipant)
{
interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
interopExecutor.HasCheckedForTrackingParticipant = true;
}
interopExecutor.EnsureReload(this);
try
{
using (InteropEnvironment interopEnvironment = new InteropEnvironment(
interopExecutor, context,
this.onResumeBookmark,
this,
this.runtimeTransactionHandle.Get(context).GetCurrentTransaction(context)))
{
interopEnvironment.Cancel();
}
}
catch (Exception exception)
{
if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
{
throw;
}
// We are not ----ing the exception. The exception is saved in this.outstandingException.
// We will throw the exception from OnPersistComplete.
}
}
internal void SetOutputArgumentValues(IDictionary<string, object> outputs, NativeActivityContext context)
{
if ((this.properties != null) && (outputs != null))
{
foreach (KeyValuePair<string, object> output in outputs)
{
Argument argument;
if (this.properties.TryGetValue(output.Key, out argument) && argument != null)
{
if (argument.Direction == ArgumentDirection.Out)
{
argument.Set(context, output.Value);
}
}
}
}
}
internal IDictionary<string, object> GetInputArgumentValues(NativeActivityContext context)
{
Dictionary<string, object> arguments = null;
if (this.properties != null)
{
foreach (KeyValuePair<string, Argument> parameter in this.properties)
{
Argument argument = parameter.Value;
if (argument.Direction == ArgumentDirection.In)
{
if (arguments == null)
{
arguments = new Dictionary<string, object>();
}
arguments.Add(parameter.Key, argument.Get<object>(context));
}
}
}
return arguments;
}
System.Workflow.ComponentModel.Activity CreateActivity()
{
Debug.Assert(this.ActivityType != null, "ActivityType must be set by the time we get here");
System.Workflow.ComponentModel.Activity activity = Activator.CreateInstance(this.ActivityType) as System.Workflow.ComponentModel.Activity;
Debug.Assert(activity != null, "We should have validated that the type has a default ctor() and derives from System.Workflow.ComponentModel.Activity.");
return activity;
}
void InitializeMetaProperties(System.Workflow.ComponentModel.Activity activity)
{
Debug.Assert((activity.GetType() == this.ActivityType), "activity must be the same type as this.ActivityType");
if (this.metaProperties != null && this.metaProperties.Count > 0)
{
foreach (string name in this.metaProperties.Keys)
{
PropertyInfo property = this.ActivityType.GetProperty(name);
if (property == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MetaPropertyDoesNotExist, name, this.ActivityType.FullName));
}
property.SetValue(activity, this.metaProperties[name], null);
}
}
}
void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object state)
{
InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);
if (!interopExecutor.HasCheckedForTrackingParticipant)
{
interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
interopExecutor.HasCheckedForTrackingParticipant = true;
}
interopExecutor.EnsureReload(this);
try
{
using (InteropEnvironment interopEnvironment = new InteropEnvironment(
interopExecutor, context,
this.onResumeBookmark,
this,
this.runtimeTransactionHandle.Get(context).GetCurrentTransaction(context)))
{
IComparable queueName = interopExecutor.BookmarkQueueMap[bookmark];
interopEnvironment.EnqueueEvent(queueName, state);
}
}
catch (Exception exception)
{
if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
{
throw;
}
// We are not ----ing the exception. The exception is saved in this.outstandingException.
// We will throw the exception from OnPersistComplete.
}
}
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
string ICustomTypeDescriptor.GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
string ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
PropertyDescriptorCollection interopProperties;
if (attributes != null)
{
interopProperties = TypeDescriptor.GetProperties(this, attributes, true);
}
else
{
interopProperties = TypeDescriptor.GetProperties(this, true);
}
for (int i = 0; i < interopProperties.Count; i++)
{
properties.Add(interopProperties[i]);
}
if (this.hasValidBody)
{
// First, cache the full set of body properties
if (!this.exposedBodyPropertiesCacheIsValid)
{
//Create matched pair of RuntimeArguments for every property: Property (InArgument) & PropertyOut (Argument)
PropertyInfo[] bodyProperties = this.ActivityType.GetProperties();
// recheck for name collisions
this.hasNameCollision = InteropEnvironment.ParameterHelper.HasPropertyNameCollision(bodyProperties);
for (int i = 0; i < bodyProperties.Length; i++)
{
PropertyInfo property = bodyProperties[i];
bool isMetaProperty;
if (InteropEnvironment.ParameterHelper.IsBindableOrMetaProperty(property, out isMetaProperty))
{
// Propagate the attributes to the PropertyDescriptor, appending a DesignerSerializationVisibility attribute
Attribute[] customAttributes = Attribute.GetCustomAttributes(property, true);
Attribute[] newAttributes = new Attribute[customAttributes.Length + 1];
customAttributes.CopyTo(newAttributes, 0);
newAttributes[customAttributes.Length] = new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden);
if (this.exposedBodyProperties == null)
{
this.exposedBodyProperties = new List<InteropProperty>(bodyProperties.Length);
}
if (isMetaProperty)
{
InteropProperty descriptor = new LiteralProperty(this, property.Name, property.PropertyType, newAttributes);
this.exposedBodyProperties.Add(descriptor);
}
else
{
InteropProperty inDescriptor;
//If there are any Property/PropertyOut name pairs already extant, we fall back to renaming the InArgument half of the pair as well
if (this.hasNameCollision)
{
inDescriptor = new ArgumentProperty(this, property.Name + InArgumentSuffix, Argument.Create(property.PropertyType, ArgumentDirection.In), newAttributes);
}
else
{
inDescriptor = new ArgumentProperty(this, property.Name, Argument.Create(property.PropertyType, ArgumentDirection.In), newAttributes);
}
this.exposedBodyProperties.Add(inDescriptor);
//We always rename the OutArgument half of the pair
InteropProperty outDescriptor = new ArgumentProperty(this, property.Name + OutArgumentSuffix, Argument.Create(property.PropertyType, ArgumentDirection.Out), newAttributes);
this.exposedBodyProperties.Add(outDescriptor);
}
}
}
this.exposedBodyPropertiesCacheIsValid = true;
}
// Now adds body properties, complying with the filter:
if (this.exposedBodyProperties != null)
{
for (int i = 0; i < this.exposedBodyProperties.Count; i++)
{
PropertyDescriptor descriptor = this.exposedBodyProperties[i];
if (attributes == null || !ShouldFilterProperty(descriptor, attributes))
{
properties.Add(descriptor);
}
}
}
}
return new PropertyDescriptorCollection(properties.ToArray());
}
static bool ShouldFilterProperty(PropertyDescriptor property, Attribute[] attributes)
{
if (attributes == null || attributes.Length == 0)
{
return false;
}
for (int i = 0; i < attributes.Length; i++)
{
Attribute filterAttribute = attributes[i];
Attribute propertyAttribute = property.Attributes[filterAttribute.GetType()];
if (propertyAttribute == null)
{
if (!filterAttribute.IsDefaultAttribute())
{
return true;
}
}
else
{
if (!filterAttribute.Match(propertyAttribute))
{
return true;
}
}
}
return false;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
InteropProperty intProp = pd as InteropProperty;
if (intProp != null)
{
return intProp.Owner;
}
else
{
return this;
}
}
internal void OnClose(NativeActivityContext context, Exception exception)
{
if (this.persistOnClose.Get(context))
{
if (exception == null)
{
context.ScheduleActivity(this.persistActivity);
}
else
{
// The V1 workflow faulted and there is an uncaught exception. We cannot throw
// the exception right away because we must Persist in order to process the WorkBatch.
// So we are saving the uncaught exception and scheduling the Persist activity with a completion callback.
// We will throw the exception from OnPersistComplete.
this.outstandingException.Set(context, exception);
if (this.onPersistComplete == null)
{
this.onPersistComplete = new CompletionCallback(this.OnPersistComplete);
}
context.ScheduleActivity(this.persistActivity, this.onPersistComplete);
}
}
this.interopEnlistment.Set(context, null);
}
internal void Persist(NativeActivityContext context)
{
if (this.onPersistComplete == null)
{
this.onPersistComplete = new CompletionCallback(this.OnPersistComplete);
}
// If Persist fails for any reason, the workflow aborts
context.ScheduleActivity(this.persistActivity, this.onPersistComplete);
}
internal void OnPersistComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
this.persistOnClose.Set(context, false);
Exception exception = this.outstandingException.Get(context);
if (exception != null)
{
this.outstandingException.Set(context, null);
throw exception;
}
this.Resume(context, null);
}
internal void CreateTransaction(NativeActivityContext context, TransactionOptions txOptions)
{
RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
Debug.Assert(transactionHandle != null, "RuntimeTransactionHandle is null");
transactionHandle.RequestTransactionContext(context, OnTransactionContextAcquired, txOptions);
}
void OnTransactionContextAcquired(NativeActivityTransactionContext context, object state)
{
Debug.Assert(context != null, "ActivityTransactionContext was null");
TransactionOptions txOptions = (TransactionOptions)state;
CommittableTransaction transaction = new CommittableTransaction(txOptions);
context.SetRuntimeTransaction(transaction);
this.Resume(context, transaction);
}
internal void CommitTransaction(NativeActivityContext context)
{
if (this.onTransactionComplete == null)
{
this.onTransactionComplete = new BookmarkCallback(this.OnTransactionComplete);
}
RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
transactionHandle.CompleteTransaction(context, this.onTransactionComplete);
}
void OnTransactionComplete(NativeActivityContext context, Bookmark bookmark, object state)
{
this.Resume(context, null);
}
void Resume(NativeActivityContext context, Transaction transaction)
{
InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);
if (!interopExecutor.HasCheckedForTrackingParticipant)
{
interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
interopExecutor.HasCheckedForTrackingParticipant = true;
}
interopExecutor.EnsureReload(this);
try
{
using (InteropEnvironment interopEnvironment = new InteropEnvironment(
interopExecutor, context,
this.onResumeBookmark,
this,
transaction))
{
interopEnvironment.Resume();
}
}
catch (Exception exception)
{
if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
{
throw;
}
// We are not ----ing the exception. The exception is saved in this.outstandingException.
// We will throw the exception from OnPersistComplete.
}
}
internal void AddResourceManager(NativeActivityContext context, VolatileResourceManager resourceManager)
{
if (Transaction.Current != null &&
Transaction.Current.TransactionInformation.Status == TransactionStatus.Active)
{
InteropEnlistment enlistment = this.interopEnlistment.Get(context);
if (enlistment == null || !enlistment.IsValid)
{
enlistment = new InteropEnlistment(Transaction.Current, resourceManager);
Transaction.Current.EnlistVolatile(enlistment, EnlistmentOptions.EnlistDuringPrepareRequired);
this.interopEnlistment.Set(context, enlistment);
}
}
else
{
InteropPersistenceParticipant persistenceParticipant = context.GetExtension<InteropPersistenceParticipant>();
persistenceParticipant.Add(this.Id, resourceManager);
this.persistOnClose.Set(context, true);
}
}
Constraint ProcessAdvancedConstraints()
{
DelegateInArgument<Interop> element = new DelegateInArgument<Interop>() { Name = "element" };
DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext>() { Name = "validationContext" };
DelegateInArgument<Activity> parent = new DelegateInArgument<Activity>() { Name = "parent" };
//This will accumulate all potential violations at the root level. See the use case DIRECT of the Interop spec
Variable<HashSet<InteropValidationEnum>> rootValidationDataVar = new Variable<HashSet<InteropValidationEnum>>(context => new HashSet<InteropValidationEnum>());
//This will accumulate all violations at the nested level. See the use case NESTED of the Interop spec
Variable<HashSet<InteropValidationEnum>> nestedChildrenValidationDataVar = new Variable<HashSet<InteropValidationEnum>>(context => new HashSet<InteropValidationEnum>());
return new Constraint<Interop>
{
Body = new ActivityAction<Interop, ValidationContext>
{
Argument1 = element,
Argument2 = validationContext,
Handler = new If
{
Condition = new InArgument<bool>(env => element.Get(env).hasValidBody),
Then = new Sequence
{
Variables = { rootValidationDataVar, nestedChildrenValidationDataVar },
Activities =
{
//First traverse the interop body and collect all available data for validation. This is done at all levels, DIRECT and NESTED
new WalkInteropBodyAndGatherData()
{
RootLevelValidationData = new InArgument<HashSet<InteropValidationEnum>>(rootValidationDataVar),
NestedChildrenValidationData = new InArgument<HashSet<InteropValidationEnum>>(nestedChildrenValidationDataVar),
InteropActivity = element
},
//This is based off the table in the Interop spec.
new ValidateAtRootAndNestedLevels()
{
RootLevelValidationData = rootValidationDataVar,
NestedChildrenValidationData = nestedChildrenValidationDataVar,
Interop = element,
},
//Traverse the parent chain of the Interop activity to look for specifc violations regarding composition of 3.0 activities within 4.0 activities.
//Specifically,
// - 3.0 TransactionScope within a 4.0 TransactionScope
// - 3.0 PersistOnClose within a 4.0 TransactionScope
//
new ForEach<Activity>
{
Values = new GetParentChain
{
ValidationContext = validationContext,
},
Body = new ActivityAction<Activity>
{
Argument = parent,
Handler = new Sequence
{
Activities =
{
new If()
{
Condition = new Or<bool, bool, bool>
{
Left = new Equal<Type, Type, bool>
{
Left = new ObtainType
{
Input = parent,
},
Right = new InArgument<Type>(context => typeof(System.Activities.Statements.TransactionScope))
},
Right = new Equal<string, string, bool>
{
Left = new InArgument<string>(env => parent.Get(env).GetType().FullName),
Right = "System.ServiceModel.Activities.TransactedReceiveScope"
}
},
Then = new Sequence
{
Activities =
{
new AssertValidation
{
//Here we only pass the NestedChildrenValidationData since root level use
//of TransactionScope would have already been flagged as an error
Assertion = new CheckForTransactionScope()
{
ValidationResults = nestedChildrenValidationDataVar
},
Message = new InArgument<string>(ExecutionStringManager.InteropBodyNestedTransactionScope)
},
new AssertValidation
{
Assertion = new CheckForPersistOnClose()
{
NestedChildrenValidationData = nestedChildrenValidationDataVar,
RootLevelValidationData = rootValidationDataVar
},
Message = new InArgument<string>(ExecutionStringManager.InteropBodyNestedPersistOnCloseWithinTransactionScope)
},
}
},
},
}
}
}
},
new ActivityTreeValidation()
{
Interop = element
}
}
}
}
}
};
}
class ActivityTreeValidation : NativeActivity
{
public ActivityTreeValidation()
{
}
public InArgument<Interop> Interop
{
get;
set;
}
protected override void Execute(NativeActivityContext context)
{
Interop interop = this.Interop.Get(context);
if (interop == null)
{
return;
}
if (!typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(interop.ActivityType))
{
return;
}
System.ComponentModel.Design.ServiceContainer container = new System.ComponentModel.Design.ServiceContainer();
container.AddService(typeof(ITypeProvider), CreateTypeProvider(interop.ActivityType));
ValidationManager manager = new ValidationManager(container);
System.Workflow.ComponentModel.Activity interopBody = interop.ComponentModelActivity;
using (WorkflowCompilationContext.CreateScope(manager))
{
foreach (Validator validator in manager.GetValidators(interop.ActivityType))
{
ValidationErrorCollection errors = validator.Validate(manager, interopBody);
foreach (System.Workflow.ComponentModel.Compiler.ValidationError error in errors)
{
Constraint.AddValidationError(context, new ValidationError(error.ErrorText, error.IsWarning, error.PropertyName));
}
}
}
}
static TypeProvider CreateTypeProvider(Type rootType)
{
TypeProvider typeProvider = new TypeProvider(null);
typeProvider.SetLocalAssembly(rootType.Assembly);
typeProvider.AddAssembly(rootType.Assembly);
foreach (AssemblyName assemblyName in rootType.Assembly.GetReferencedAssemblies())
{
Assembly referencedAssembly = null;
try
{
referencedAssembly = Assembly.Load(assemblyName);
if (referencedAssembly != null)
typeProvider.AddAssembly(referencedAssembly);
}
catch
{
}
if (referencedAssembly == null && assemblyName.CodeBase != null)
typeProvider.AddAssemblyReference(assemblyName.CodeBase);
}
return typeProvider;
}
}
class CheckForTransactionScope : CodeActivity<bool>
{
public InArgument<HashSet<InteropValidationEnum>> ValidationResults
{
get;
set;
}
protected override bool Execute(CodeActivityContext context)
{
HashSet<InteropValidationEnum> validationResults = this.ValidationResults.Get(context);
if (validationResults.Contains(InteropValidationEnum.TransactionScope))
{
return false;
}
return true;
}
}
class CheckForPersistOnClose : CodeActivity<bool>
{
public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
{
get;
set;
}
public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
{
get;
set;
}
protected override bool Execute(CodeActivityContext context)
{
HashSet<InteropValidationEnum> nestedValidationData = this.NestedChildrenValidationData.Get(context);
HashSet<InteropValidationEnum> rootValidationData = this.RootLevelValidationData.Get(context);
if (nestedValidationData.Contains(InteropValidationEnum.PersistOnClose) || rootValidationData.Contains(InteropValidationEnum.PersistOnClose))
{
return false;
}
return true;
}
}
class ValidateAtRootAndNestedLevels : NativeActivity
{
public ValidateAtRootAndNestedLevels()
{
}
public InArgument<Interop> Interop
{
get;
set;
}
public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
{
get;
set;
}
public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
{
get;
set;
}
protected override void Execute(NativeActivityContext context)
{
Interop activity = this.Interop.Get(context);
foreach (InteropValidationEnum validationEnum in this.RootLevelValidationData.Get(context))
{
//We care to mark PersistOnClose during the walking algorithm because we need to check if it happens under a 4.0 TransactionScopActivity and flag that
//That is done later, so skip here.
if (validationEnum != InteropValidationEnum.PersistOnClose)
{
string message = string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyRootLevelViolation, activity.DisplayName, validationEnum.ToString() + "Activity");
Constraint.AddValidationError(context, new ValidationError(message));
}
}
foreach (InteropValidationEnum validationEnum in this.NestedChildrenValidationData.Get(context))
{
//We care to mark PersistOnClose or TransactionScope during the walking algorithm because we need to check if it happens under a 4.0 TransactionScopActivity and flag that
//That is done later, so skip here.
if ((validationEnum != InteropValidationEnum.PersistOnClose) && (validationEnum != InteropValidationEnum.TransactionScope))
{
string message = string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyNestedViolation, activity.DisplayName, validationEnum.ToString() + "Activity");
Constraint.AddValidationError(context, new ValidationError(message));
}
}
}
}
class WalkInteropBodyAndGatherData : System.Activities.CodeActivity
{
public InArgument<Interop> InteropActivity
{
get;
set;
}
public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
{
get;
set;
}
public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
{
get;
set;
}
protected override void Execute(CodeActivityContext context)
{
Interop interop = this.InteropActivity.Get(context);
Debug.Assert(interop != null, "Interop activity is null");
Debug.Assert(interop.hasValidBody, "Interop activity has an invalid body");
System.Workflow.ComponentModel.Activity interopBody = interop.ComponentModelActivity;
Debug.Assert(interopBody != null, "Interop Body was null");
HashSet<InteropValidationEnum> validationResults;
validationResults = this.RootLevelValidationData.Get(context);
Debug.Assert(validationResults != null, "The RootLevelValidationData hash set was null");
//Gather data at root level first
ProcessAtRootLevel(interopBody, validationResults);
validationResults = null;
validationResults = this.NestedChildrenValidationData.Get(context);
Debug.Assert(validationResults != null, "The NestedChildrenValidationData hash set was null");
//Next, process nested children of the Body
if (interopBody is System.Workflow.ComponentModel.CompositeActivity)
{
ProcessNestedChildren(interopBody, validationResults);
}
return;
}
void ProcessAtRootLevel(System.Workflow.ComponentModel.Activity interopBody, HashSet<InteropValidationEnum> validationResults)
{
Debug.Assert(interopBody != null, "Interop Body is null");
Debug.Assert(validationResults != null, "The HashSet of validation results is null");
if (interopBody.PersistOnClose)
{
validationResults.Add(InteropValidationEnum.PersistOnClose);
}
Type interopBodyType = interopBody.GetType();
if (interopBodyType == typeof(System.Workflow.ComponentModel.TransactionScopeActivity))
{
validationResults.Add(InteropValidationEnum.TransactionScope);
}
else if (interopBodyType == typeof(System.Workflow.Activities.CodeActivity))
{
validationResults.Add(InteropValidationEnum.Code);
}
else if (interopBodyType == typeof(System.Workflow.Activities.DelayActivity))
{
validationResults.Add(InteropValidationEnum.Delay);
}
else if (interopBodyType == typeof(System.Workflow.Activities.InvokeWebServiceActivity))
{
validationResults.Add(InteropValidationEnum.InvokeWebService);
}
else if (interopBodyType == typeof(System.Workflow.Activities.InvokeWorkflowActivity))
{
validationResults.Add(InteropValidationEnum.InvokeWorkflow);
}
else if (interopBodyType == typeof(System.Workflow.Activities.PolicyActivity))
{
validationResults.Add(InteropValidationEnum.Policy);
}
else if (interopBodyType.FullName == "System.Workflow.Activities.SendActivity")
{
validationResults.Add(InteropValidationEnum.Send);
}
else if (interopBodyType == typeof(System.Workflow.Activities.SetStateActivity))
{
validationResults.Add(InteropValidationEnum.SetState);
}
else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceFaultActivity))
{
validationResults.Add(InteropValidationEnum.WebServiceFault);
}
else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceInputActivity))
{
validationResults.Add(InteropValidationEnum.WebServiceInput);
}
else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceOutputActivity))
{
validationResults.Add(InteropValidationEnum.WebServiceOutput);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensateActivity))
{
validationResults.Add(InteropValidationEnum.Compensate);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.SuspendActivity))
{
validationResults.Add(InteropValidationEnum.Suspend);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.TerminateActivity))
{
validationResults.Add(InteropValidationEnum.Terminate);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.ThrowActivity))
{
validationResults.Add(InteropValidationEnum.Throw);
}
else if (interopBodyType == typeof(System.Workflow.Activities.ConditionedActivityGroup))
{
validationResults.Add(InteropValidationEnum.ConditionedActivityGroup);
}
else if (interopBodyType == typeof(System.Workflow.Activities.EventHandlersActivity))
{
validationResults.Add(InteropValidationEnum.EventHandlers);
}
else if (interopBodyType == typeof(System.Workflow.Activities.EventHandlingScopeActivity))
{
validationResults.Add(InteropValidationEnum.EventHandlingScope);
}
else if (interopBodyType == typeof(System.Workflow.Activities.IfElseActivity))
{
validationResults.Add(InteropValidationEnum.IfElse);
}
else if (interopBodyType == typeof(System.Workflow.Activities.ListenActivity))
{
validationResults.Add(InteropValidationEnum.Listen);
}
else if (interopBodyType == typeof(System.Workflow.Activities.ParallelActivity))
{
validationResults.Add(InteropValidationEnum.Parallel);
}
else if (interopBodyType == typeof(System.Workflow.Activities.ReplicatorActivity))
{
validationResults.Add(InteropValidationEnum.Replicator);
}
else if (interopBodyType == typeof(System.Workflow.Activities.SequenceActivity))
{
validationResults.Add(InteropValidationEnum.Sequence);
}
else if (interopBodyType == typeof(System.Workflow.Activities.CompensatableSequenceActivity))
{
validationResults.Add(InteropValidationEnum.CompensatableSequence);
}
else if (interopBodyType == typeof(System.Workflow.Activities.EventDrivenActivity))
{
validationResults.Add(InteropValidationEnum.EventDriven);
}
else if (interopBodyType == typeof(System.Workflow.Activities.IfElseBranchActivity))
{
validationResults.Add(InteropValidationEnum.IfElseBranch);
}
else if (interopBodyType.FullName == "System.Workflow.Activities.ReceiveActivity")
{
validationResults.Add(InteropValidationEnum.Receive);
}
else if (interopBodyType == typeof(System.Workflow.Activities.SequentialWorkflowActivity))
{
validationResults.Add(InteropValidationEnum.SequentialWorkflow);
}
else if (interopBodyType == typeof(System.Workflow.Activities.StateFinalizationActivity))
{
validationResults.Add(InteropValidationEnum.StateFinalization);
}
else if (interopBodyType == typeof(System.Workflow.Activities.StateInitializationActivity))
{
validationResults.Add(InteropValidationEnum.StateInitialization);
}
else if (interopBodyType == typeof(System.Workflow.Activities.StateActivity))
{
validationResults.Add(InteropValidationEnum.State);
}
else if (interopBodyType == typeof(System.Workflow.Activities.StateMachineWorkflowActivity))
{
validationResults.Add(InteropValidationEnum.StateMachineWorkflow);
}
else if (interopBodyType == typeof(System.Workflow.Activities.WhileActivity))
{
validationResults.Add(InteropValidationEnum.While);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.CancellationHandlerActivity))
{
validationResults.Add(InteropValidationEnum.CancellationHandler);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensatableTransactionScopeActivity))
{
validationResults.Add(InteropValidationEnum.CompensatableTransactionScope);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensationHandlerActivity))
{
validationResults.Add(InteropValidationEnum.CompensationHandler);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.FaultHandlerActivity))
{
validationResults.Add(InteropValidationEnum.FaultHandler);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.FaultHandlersActivity))
{
validationResults.Add(InteropValidationEnum.FaultHandlers);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.SynchronizationScopeActivity))
{
validationResults.Add(InteropValidationEnum.SynchronizationScope);
}
else if (interopBodyType == typeof(System.Workflow.ComponentModel.ICompensatableActivity))
{
validationResults.Add(InteropValidationEnum.ICompensatable);
}
}
void ProcessNestedChildren(System.Workflow.ComponentModel.Activity interopBody, HashSet<InteropValidationEnum> validationResults)
{
Debug.Assert(interopBody != null, "Interop Body is null");
Debug.Assert(validationResults != null, "The HashSet of validation results is null");
bool persistOnClose = false;
foreach (System.Workflow.ComponentModel.Activity activity in interopBody.CollectNestedActivities())
{
if (activity.PersistOnClose)
{
persistOnClose = true;
}
if (activity is System.Workflow.ComponentModel.TransactionScopeActivity)
{
validationResults.Add(InteropValidationEnum.TransactionScope);
}
else if (activity is System.Workflow.Activities.InvokeWorkflowActivity)
{
validationResults.Add(InteropValidationEnum.InvokeWorkflow);
}
// SendActivity is sealed
else if (activity.GetType().FullName == "System.Workflow.Activities.SendActivity")
{
validationResults.Add(InteropValidationEnum.Send);
}
else if (activity is System.Workflow.Activities.WebServiceFaultActivity)
{
validationResults.Add(InteropValidationEnum.WebServiceFault);
}
else if (activity is System.Workflow.Activities.WebServiceInputActivity)
{
validationResults.Add(InteropValidationEnum.WebServiceInput);
}
else if (activity is System.Workflow.Activities.WebServiceOutputActivity)
{
validationResults.Add(InteropValidationEnum.WebServiceOutput);
}
else if (activity is System.Workflow.ComponentModel.CompensateActivity)
{
validationResults.Add(InteropValidationEnum.Compensate);
}
else if (activity is System.Workflow.ComponentModel.SuspendActivity)
{
validationResults.Add(InteropValidationEnum.Suspend);
}
else if (activity is System.Workflow.Activities.CompensatableSequenceActivity)
{
validationResults.Add(InteropValidationEnum.CompensatableSequence);
}
// ReceiveActivity is sealed
else if (activity.GetType().FullName == "System.Workflow.Activities.ReceiveActivity")
{
validationResults.Add(InteropValidationEnum.Receive);
}
else if (activity is System.Workflow.ComponentModel.CompensatableTransactionScopeActivity)
{
validationResults.Add(InteropValidationEnum.CompensatableTransactionScope);
}
else if (activity is System.Workflow.ComponentModel.CompensationHandlerActivity)
{
validationResults.Add(InteropValidationEnum.CompensationHandler);
}
else if (activity is System.Workflow.ComponentModel.ICompensatableActivity)
{
validationResults.Add(InteropValidationEnum.ICompensatable);
}
}
if (persistOnClose)
{
validationResults.Add(InteropValidationEnum.PersistOnClose);
}
}
}
//This needs to be in sync with the table in the spec
//We use this internally to keep a hashset of validation data
enum InteropValidationEnum
{
Code,
Delay,
InvokeWebService,
InvokeWorkflow,
Policy,
Send,
SetState,
WebServiceFault,
WebServiceInput,
WebServiceOutput,
Compensate,
Suspend,
ConditionedActivityGroup,
EventHandlers,
EventHandlingScope,
IfElse,
Listen,
Parallel,
Replicator,
Sequence,
CompensatableSequence,
EventDriven,
IfElseBranch,
Receive,
SequentialWorkflow,
StateFinalization,
StateInitialization,
State,
StateMachineWorkflow,
While,
CancellationHandler,
CompensatableTransactionScope,
CompensationHandler,
FaultHandler,
FaultHandlers,
SynchronizationScope,
TransactionScope,
ICompensatable,
PersistOnClose,
Terminate,
Throw
}
class ObtainType : CodeActivity<Type>
{
public ObtainType()
{
}
public InArgument<Activity> Input
{
get;
set;
}
protected override Type Execute(CodeActivityContext context)
{
return this.Input.Get(context).GetType();
}
}
abstract class InteropProperty : PropertyDescriptor
{
Interop owner;
bool isValid;
public InteropProperty(Interop owner, string name, Attribute[] propertyInfoAttributes)
: base(name, propertyInfoAttributes)
{
this.owner = owner;
this.isValid = true;
}
public override Type ComponentType
{
get
{
ThrowIfInvalid();
return this.owner.GetType();
}
}
protected internal Interop Owner
{
get
{
return this.owner;
}
}
public override bool CanResetValue(object component)
{
ThrowIfInvalid();
return false;
}
public override void ResetValue(object component)
{
ThrowIfInvalid();
}
public override bool ShouldSerializeValue(object component)
{
ThrowIfInvalid();
return false;
}
protected void ThrowIfInvalid()
{
if (!this.isValid)
{
throw new InvalidOperationException(ExecutionStringManager.InteropInvalidPropertyDescriptor);
}
}
internal void Invalidate()
{
this.isValid = false;
}
}
class ArgumentProperty : InteropProperty
{
string argumentName;
Argument argument;
public ArgumentProperty(Interop owner, string argumentName, Argument argument, Attribute[] attributes)
: base(owner, argumentName, attributes)
{
this.argumentName = argumentName;
this.argument = argument;
}
public override bool IsReadOnly
{
get
{
ThrowIfInvalid();
return false;
}
}
public override Type PropertyType
{
get
{
ThrowIfInvalid();
return GetArgument().GetType();
}
}
public override object GetValue(object component)
{
ThrowIfInvalid();
return GetArgument();
}
public override void SetValue(object component, object value)
{
ThrowIfInvalid();
if (value != null)
{
this.Owner.ActivityProperties[this.argumentName] = (Argument)value;
}
else
{
this.Owner.ActivityProperties.Remove(this.argumentName);
}
}
Argument GetArgument()
{
Argument argument;
if (!this.Owner.ActivityProperties.TryGetValue(this.argumentName, out argument))
{
argument = this.argument;
}
return argument;
}
}
class LiteralProperty : InteropProperty
{
string literalName;
Type literalType;
public LiteralProperty(Interop owner, string literalName, Type literalType, Attribute[] attributes)
: base(owner, literalName, attributes)
{
this.literalName = literalName;
this.literalType = literalType;
}
public override bool IsReadOnly
{
get
{
ThrowIfInvalid();
return false;
}
}
public override Type PropertyType
{
get
{
ThrowIfInvalid();
return this.literalType;
}
}
public override object GetValue(object component)
{
ThrowIfInvalid();
return GetLiteral();
}
public override void SetValue(object component, object value)
{
ThrowIfInvalid();
this.Owner.ActivityMetaProperties[this.literalName] = value;
}
object GetLiteral()
{
object literal;
if (this.Owner.ActivityMetaProperties.TryGetValue(this.literalName, out literal))
{
return literal;
}
else
{
return null;
}
}
}
class InteropPersistenceParticipant : PersistenceIOParticipant
{
public InteropPersistenceParticipant()
: base(true, false)
{
this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
this.CommittedResourceManagers = new Dictionary<Transaction, Dictionary<string, VolatileResourceManager>>();
}
Dictionary<string, VolatileResourceManager> ResourceManagers
{
get;
set;
}
Dictionary<Transaction, Dictionary<string, VolatileResourceManager>> CommittedResourceManagers
{
get;
set;
}
protected override IAsyncResult BeginOnSave(IDictionary<XName, object> readWriteValues, IDictionary<System.Xml.Linq.XName, object> writeOnlyValues, TimeSpan timeout, AsyncCallback callback, object state)
{
try
{
foreach (VolatileResourceManager rm in this.ResourceManagers.Values)
{
rm.Commit();
}
}
finally
{
this.CommittedResourceManagers.Add(Transaction.Current, this.ResourceManagers);
this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
}
return new CompletedAsyncResult(callback, state);
}
protected override void EndOnSave(IAsyncResult result)
{
CompletedAsyncResult.End(result);
}
void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
if (e.Transaction.TransactionInformation.Status == TransactionStatus.Committed)
{
foreach (VolatileResourceManager rm in this.CommittedResourceManagers[e.Transaction].Values)
{
rm.Complete();
}
}
else
{
foreach (VolatileResourceManager rm in this.CommittedResourceManagers[e.Transaction].Values)
{
rm.ClearAllBatchedWork();
}
}
this.CommittedResourceManagers.Remove(e.Transaction);
}
protected override void Abort()
{
foreach (VolatileResourceManager rm in this.ResourceManagers.Values)
{
rm.ClearAllBatchedWork();
}
this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
}
internal void Add(string activityId, VolatileResourceManager rm)
{
// Add and OnSave shouldn't be called at the same time. A lock isn't needed here.
this.ResourceManagers.Add(activityId, rm);
}
}
[DataContract]
class InteropEnlistment : IEnlistmentNotification
{
VolatileResourceManager resourceManager;
Transaction transaction;
public InteropEnlistment()
{
}
public InteropEnlistment(Transaction transaction, VolatileResourceManager resourceManager)
{
this.resourceManager = resourceManager;
this.transaction = transaction;
this.IsValid = true;
}
public bool IsValid { get; set; }
public void Commit(Enlistment enlistment)
{
this.resourceManager.Complete();
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
// Following the WF3 runtime behavior - Aborting during InDoubt
this.Rollback(enlistment);
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope(this.transaction))
{
this.resourceManager.Commit();
ts.Complete();
}
preparingEnlistment.Prepared();
}
public void Rollback(Enlistment enlistment)
{
this.resourceManager.ClearAllBatchedWork();
enlistment.Done();
}
}
class CompletedAsyncResult : IAsyncResult
{
AsyncCallback callback;
bool endCalled;
ManualResetEvent manualResetEvent;
object state;
object thisLock;
public CompletedAsyncResult(AsyncCallback callback, object state)
{
this.callback = callback;
this.state = state;
this.thisLock = new object();
if (callback != null)
{
try
{
callback(this);
}
catch (Exception e) // transfer to another thread, this is a fatal situation
{
throw new InvalidProgramException(ExecutionStringManager.AsyncCallbackThrewException, e);
}
}
}
public static void End(IAsyncResult result)
{
if (result == null)
{
throw new ArgumentNullException("result");
}
CompletedAsyncResult asyncResult = result as CompletedAsyncResult;
if (asyncResult == null)
{
throw new ArgumentException(ExecutionStringManager.InvalidAsyncResult, "result");
}
if (asyncResult.endCalled)
{
throw new InvalidOperationException(ExecutionStringManager.EndCalledTwice);
}
asyncResult.endCalled = true;
if (asyncResult.manualResetEvent != null)
{
asyncResult.manualResetEvent.Close();
}
}
public object AsyncState
{
get
{
return state;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
if (this.manualResetEvent != null)
{
return this.manualResetEvent;
}
lock (ThisLock)
{
if (this.manualResetEvent == null)
{
this.manualResetEvent = new ManualResetEvent(true);
}
}
return this.manualResetEvent;
}
}
public bool CompletedSynchronously
{
get
{
return true;
}
}
public bool IsCompleted
{
get
{
return true;
}
}
object ThisLock
{
get
{
return this.thisLock;
}
}
}
}
}
|