451 lines
14 KiB
C#
451 lines
14 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Activities
|
||
|
{
|
||
|
using System;
|
||
|
using System.Activities.Runtime;
|
||
|
using System.Activities.Tracking;
|
||
|
using System.Diagnostics.CodeAnalysis;
|
||
|
using System.Globalization;
|
||
|
using System.Runtime;
|
||
|
|
||
|
[Fx.Tag.XamlVisible(false)]
|
||
|
public class ActivityContext
|
||
|
{
|
||
|
ActivityInstance instance;
|
||
|
ActivityExecutor executor;
|
||
|
bool isDisposed;
|
||
|
long instanceId;
|
||
|
|
||
|
// Used by subclasses that are pooled.
|
||
|
internal ActivityContext()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// these can only be created by the WF Runtime
|
||
|
internal ActivityContext(ActivityInstance instance, ActivityExecutor executor)
|
||
|
{
|
||
|
Fx.Assert(instance != null, "valid activity instance is required");
|
||
|
|
||
|
this.instance = instance;
|
||
|
this.executor = executor;
|
||
|
this.Activity = this.instance.Activity;
|
||
|
this.instanceId = instance.InternalId;
|
||
|
}
|
||
|
|
||
|
internal LocationEnvironment Environment
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
return this.instance.Environment;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool AllowChainedEnvironmentAccess
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
internal Activity Activity
|
||
|
{
|
||
|
get;
|
||
|
private set;
|
||
|
}
|
||
|
|
||
|
internal ActivityInstance CurrentInstance
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.instance;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal ActivityExecutor CurrentExecutor
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.executor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string ActivityInstanceId
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
return this.instanceId.ToString(CultureInfo.InvariantCulture);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Guid WorkflowInstanceId
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
return this.executor.WorkflowInstanceId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public WorkflowDataContext DataContext
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
// Argument expressions don't have visbility into public variables at the same scope.
|
||
|
// However fast-path expressions use the parent's ActivityInstance instead of
|
||
|
// creating their own, so we need to give them a DataContext without variables
|
||
|
bool includeLocalVariables = !this.instance.IsResolvingArguments;
|
||
|
|
||
|
if (this.instance.DataContext == null ||
|
||
|
this.instance.DataContext.IncludesLocalVariables != includeLocalVariables)
|
||
|
{
|
||
|
this.instance.DataContext
|
||
|
= new WorkflowDataContext(this.executor, this.instance, includeLocalVariables);
|
||
|
}
|
||
|
|
||
|
return this.instance.DataContext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool IsDisposed
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.isDisposed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public T GetExtension<T>()
|
||
|
where T : class
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
return this.executor.GetExtension<T>();
|
||
|
}
|
||
|
|
||
|
internal Location GetIgnorableResultLocation(RuntimeArgument resultArgument)
|
||
|
{
|
||
|
return this.executor.GetIgnorableResultLocation(resultArgument);
|
||
|
}
|
||
|
|
||
|
internal void Reinitialize(ActivityInstance instance, ActivityExecutor executor)
|
||
|
{
|
||
|
Reinitialize(instance, executor, instance.Activity, instance.InternalId);
|
||
|
}
|
||
|
|
||
|
internal void Reinitialize(ActivityInstance instance, ActivityExecutor executor, Activity activity, long instanceId)
|
||
|
{
|
||
|
this.isDisposed = false;
|
||
|
this.instance = instance;
|
||
|
this.executor = executor;
|
||
|
this.Activity = activity;
|
||
|
this.instanceId = instanceId;
|
||
|
}
|
||
|
|
||
|
// extra insurance against misuse (if someone stashes away the execution context to use later)
|
||
|
internal void Dispose()
|
||
|
{
|
||
|
this.isDisposed = true;
|
||
|
this.instance = null;
|
||
|
this.executor = null;
|
||
|
this.Activity = null;
|
||
|
this.instanceId = 0;
|
||
|
}
|
||
|
|
||
|
internal void DisposeDataContext()
|
||
|
{
|
||
|
if (this.instance.DataContext != null)
|
||
|
{
|
||
|
this.instance.DataContext.DisposeEnvironment();
|
||
|
this.instance.DataContext = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
public Location<T> GetLocation<T>(LocationReference locationReference)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (locationReference == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("locationReference");
|
||
|
}
|
||
|
|
||
|
Location location = locationReference.GetLocation(this);
|
||
|
|
||
|
Location<T> typedLocation = location as Location<T>;
|
||
|
|
||
|
if (typedLocation != null)
|
||
|
{
|
||
|
return typedLocation;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Fx.Assert(location != null, "The contract of LocationReference is that GetLocation never returns null.");
|
||
|
|
||
|
if (locationReference.Type == typeof(T))
|
||
|
{
|
||
|
return new TypedLocationWrapper<T>(location);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.LocationTypeMismatch(locationReference.Name, typeof(T), locationReference.Type)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
public T GetValue<T>(LocationReference locationReference)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (locationReference == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("locationReference");
|
||
|
}
|
||
|
|
||
|
return GetValueCore<T>(locationReference);
|
||
|
}
|
||
|
|
||
|
internal T GetValueCore<T>(LocationReference locationReference)
|
||
|
{
|
||
|
Location location = locationReference.GetLocationForRead(this);
|
||
|
|
||
|
Location<T> typedLocation = location as Location<T>;
|
||
|
|
||
|
if (typedLocation != null)
|
||
|
{
|
||
|
// If we hit this path we can avoid boxing value types
|
||
|
return typedLocation.Value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Fx.Assert(location != null, "The contract of LocationReference is that GetLocation never returns null.");
|
||
|
|
||
|
return TypeHelper.Convert<T>(location.Value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void SetValue<T>(LocationReference locationReference, T value)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (locationReference == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("locationReference");
|
||
|
}
|
||
|
|
||
|
SetValueCore<T>(locationReference, value);
|
||
|
}
|
||
|
|
||
|
internal void SetValueCore<T>(LocationReference locationReference, T value)
|
||
|
{
|
||
|
Location location = locationReference.GetLocationForWrite(this);
|
||
|
|
||
|
Location<T> typedLocation = location as Location<T>;
|
||
|
|
||
|
if (typedLocation != null)
|
||
|
{
|
||
|
// If we hit this path we can avoid boxing value types
|
||
|
typedLocation.Value = value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
if (!TypeHelper.AreTypesCompatible(value, locationReference.Type))
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotSetValueToLocation(value != null ? value.GetType() : typeof(T), locationReference.Name, locationReference.Type)));
|
||
|
}
|
||
|
|
||
|
location.Value = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public T GetValue<T>(OutArgument<T> argument)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("argument");
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
return GetValueCore<T>(argument.RuntimeArgument);
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public T GetValue<T>(InOutArgument<T> argument)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("argument");
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
return GetValueCore<T>(argument.RuntimeArgument);
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public T GetValue<T>(InArgument<T> argument)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("argument");
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
return GetValueCore<T>(argument.RuntimeArgument);
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
public object GetValue(Argument argument)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("argument");
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
return GetValueCore<object>(argument.RuntimeArgument);
|
||
|
}
|
||
|
|
||
|
// Soft-Link: This method is referenced through reflection by
|
||
|
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
|
||
|
// file if the signature changes.
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "We explicitly provide a RuntimeArgument overload to avoid requiring the object type parameter.")]
|
||
|
public object GetValue(RuntimeArgument runtimeArgument)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (runtimeArgument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("runtimeArgument");
|
||
|
}
|
||
|
|
||
|
return GetValueCore<object>(runtimeArgument);
|
||
|
}
|
||
|
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public void SetValue<T>(OutArgument<T> argument, T value)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
// We want to shortcut if the argument is null
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
SetValueCore(argument.RuntimeArgument, value);
|
||
|
}
|
||
|
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public void SetValue<T>(InOutArgument<T> argument, T value)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
// We want to shortcut if the argument is null
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
SetValueCore(argument.RuntimeArgument, value);
|
||
|
}
|
||
|
|
||
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
||
|
Justification = "Generic needed for type inference")]
|
||
|
public void SetValue<T>(InArgument<T> argument, T value)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
// We want to shortcut if the argument is null
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
SetValueCore(argument.RuntimeArgument, value);
|
||
|
}
|
||
|
|
||
|
public void SetValue(Argument argument, object value)
|
||
|
{
|
||
|
ThrowIfDisposed();
|
||
|
|
||
|
if (argument == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.ArgumentNull("argument");
|
||
|
}
|
||
|
|
||
|
argument.ThrowIfNotInTree();
|
||
|
|
||
|
SetValueCore(argument.RuntimeArgument, value);
|
||
|
}
|
||
|
|
||
|
internal void TrackCore(CustomTrackingRecord record)
|
||
|
{
|
||
|
Fx.Assert(!this.isDisposed, "not usable if disposed");
|
||
|
Fx.Assert(record != null, "expect non-null record");
|
||
|
|
||
|
if (this.executor.ShouldTrack)
|
||
|
{
|
||
|
record.Activity = new ActivityInfo(this.instance);
|
||
|
record.InstanceId = this.WorkflowInstanceId;
|
||
|
this.executor.AddTrackingRecord(record);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void ThrowIfDisposed()
|
||
|
{
|
||
|
if (this.isDisposed)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(
|
||
|
new ObjectDisposedException(this.GetType().FullName, SR.AECDisposed));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|