//----------------------------------------------------------------------------- // 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() where T : class { ThrowIfDisposed(); return this.executor.GetExtension(); } 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 GetLocation(LocationReference locationReference) { ThrowIfDisposed(); if (locationReference == null) { throw FxTrace.Exception.ArgumentNull("locationReference"); } Location location = locationReference.GetLocation(this); Location typedLocation = location as Location; 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(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(LocationReference locationReference) { ThrowIfDisposed(); if (locationReference == null) { throw FxTrace.Exception.ArgumentNull("locationReference"); } return GetValueCore(locationReference); } internal T GetValueCore(LocationReference locationReference) { Location location = locationReference.GetLocationForRead(this); Location typedLocation = location as Location; 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(location.Value); } } public void SetValue(LocationReference locationReference, T value) { ThrowIfDisposed(); if (locationReference == null) { throw FxTrace.Exception.ArgumentNull("locationReference"); } SetValueCore(locationReference, value); } internal void SetValueCore(LocationReference locationReference, T value) { Location location = locationReference.GetLocationForWrite(this); Location typedLocation = location as Location; 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(OutArgument argument) { ThrowIfDisposed(); if (argument == null) { throw FxTrace.Exception.ArgumentNull("argument"); } argument.ThrowIfNotInTree(); return GetValueCore(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(InOutArgument argument) { ThrowIfDisposed(); if (argument == null) { throw FxTrace.Exception.ArgumentNull("argument"); } argument.ThrowIfNotInTree(); return GetValueCore(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(InArgument argument) { ThrowIfDisposed(); if (argument == null) { throw FxTrace.Exception.ArgumentNull("argument"); } argument.ThrowIfNotInTree(); return GetValueCore(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(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(runtimeArgument); } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters, Justification = "Generic needed for type inference")] public void SetValue(OutArgument 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(InOutArgument 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(InArgument 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)); } } } }