IHttpActionResult - How to use? - asp.net

I've seen examples of using some of the new IHttpActionResults for OK, NotFound.
I haven't seen anything using Unauthorized().
my existing code looks like:
catch (SecurityException ex)
{
request.CreateResponse(HttpStatusCode.Unauthorized, ex.Message);
}
I'd like to replace it with this:
catch (SecurityException ex)
{
response = Unauthorized();
}
but I don't see any overload to pass exception details.
Also, what is the IHttpActionResult equivalent of returning a 500 error?
catch (Exception ex)
{
response = request.CreateErrorResponse(HttpStatusCode.InternalServerError,
ex.Message);
}

Per the code for ApiController, there are only two overloads for Unauthorized():
/// <summary>
/// Creates an <see cref="UnauthorizedResult"/> (401 Unauthorized) with the specified values.
/// </summary>
/// <param name="challenges">The WWW-Authenticate challenges.</param>
/// <returns>An <see cref="UnauthorizedResult"/> with the specified values.</returns>
protected internal UnauthorizedResult Unauthorized(params AuthenticationHeaderValue[] challenges)
{
return Unauthorized((IEnumerable<AuthenticationHeaderValue>)challenges);
}
/// <summary>
/// Creates an <see cref="UnauthorizedResult"/> (401 Unauthorized) with the specified values.
/// </summary>
/// <param name="challenges">The WWW-Authenticate challenges.</param>
/// <returns>An <see cref="UnauthorizedResult"/> with the specified values.</returns>
protected internal virtual UnauthorizedResult Unauthorized(IEnumerable<AuthenticationHeaderValue> challenges)
{
return new UnauthorizedResult(challenges, this);
}
So it looks like you are out of luck, unless you want to make the change yourself (fork your own version of WebAPI or do a pull request to attempt to get it into the main branch).
The IHttpActionResult equivalent of returning a 500 error is this:
/// <summary>
/// Creates an <see cref="ExceptionResult"/> (500 Internal Server Error) with the specified exception.
/// </summary>
/// <param name="exception">The exception to include in the error.</param>
/// <returns>An <see cref="ExceptionResult"/> with the specified exception.</returns>
protected internal virtual ExceptionResult InternalServerError(Exception exception)
{
return new ExceptionResult(exception, this);
}

You can read more about Unathorized() here
How do you return status 401 from WebAPI to AngularJS and also include a custom message?
If found another way - without throwing an exception and using HttpResponseMessage as return type of your controller , you can do it like this:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "I am unauthorized IHttpActionResult custom message!"));
More about ResponseMessage() here
http://msdn.microsoft.com/en-us/library/system.web.http.apicontroller.responsemessage%28v=vs.118%29.aspx

This is my solution for a 500 exception with custom message (expanded on from here: https://stackoverflow.com/a/10734690/654708)
Base class for all Web API controllers
public abstract class ApiBaseController : ApiController
{
protected internal virtual IHttpActionResult InternalServerError(Exception ex, string message = null)
{
var customMsg = String.IsNullOrWhiteSpace(message) ? "" : String.Format("Custom error message : {0}. ", message);
var errorMsg = String.Format("{0}{1}", customMsg, ex);
return new InternalServerErrorWithMessageResult(errorMsg);
}
}
public class InternalServerErrorWithMessageResult : IHttpActionResult
{
private readonly string message;
public InternalServerErrorWithMessageResult(string message)
{
this.message = message;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(message)
};
return Task.FromResult(response);
}
}
Sample Web API controllers
public class ProductController : ApiBaseController
{
public IHttpActionResult GetProduct(int id)
{
try
{
var product = productService.GetProduct(id);
return Ok(product); // 200 w/ product
} catch(Exception ex)
{
//return InternalServerError(ex); // Uses default InternalServerError
return InternalServerError(ex, "My custom message here"); // Uses custom InternalServerError
}
}
}

Related

Is there a way to add multiple entity sets to a single controller with OData endpoints?

Is there a way to have multiple OData endpoints/functions return different types mapped to a single ODataController? The normal crud operations map to an endpoint odata/foo but if I wanted to add more methods to the controller can I define a function that returns a different type under odata/foo/foosummaries?
Could I add more entity sets that would map to the same controller class? For example if I wanted to have an endpoint under my FooController that returned FooSummaries that had the endpoint ~/odata/Foo/FooSummaries?
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix)
{
builder.EntitySet<FooResponse>("Foos");
builder.EntityType<FooSummaries>()
.Function(nameof(SubmissionsController.GetFooSummaries))
.ReturnsCollection<IEnumerable<FooSummaryREsponse>>();
}
Would I need to decorate the OData controller differently with ODataPrefix or ODataRoute?
public class SubmissionsController : ODataController
{
#region Private Fields
private ISubmissionManager _manager;
private ILogger _logger;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="manager">A SubmissionManager instance.</param>
/// <param name="logger">A Logger instance.</param>
public SubmissionsController(ISubmissionManager manager, ILogger logger)
{
_manager = manager;
_logger = logger;
}
#endregion
#region HttpGet Endpoints
/// <summary>
/// Odata get all foos
/// </summary>
/// <returns></returns>
[HttpGet()]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IQueryable<FooResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery(PageSize = 20)]
public IActionResult Get()
{
try
{
IQueryable<FooResponse> result = _manager.GetFoos();
return Ok(result);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
/// <summary>
/// Get an odata queryable foo summary
/// </summary>
/// <param name="query">The query arguments</param>
/// <returns></returns>
[HttpGet("api/v{version:apiVersion}/odata/Foos/summaries")]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IEnumerable<FooSummaryResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery()]
public IActionResult GetFooSummaries()
{
try
{
IQueryable<FooSummaryResponse> results = _manager.GetFooSummaryResponse();
results);
return Ok(results);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
}

Protobuf-net v3 DateTimeOffset Surrogate

I've seen some other SO's where people are using DateTimeOffset surrogates to handle deserializing those properties, however when I try to copy those, I continue to get a System.InvalidOperationException: No serializer defined for type: System.DateTimeOffset error.
[ProtoContract]
public TestClass
{
[ProtoMember(1)]
public DateTimeOffset Time { get; set; }
}
Surrogate class
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public long DateTimeTicks { get; set; }
[ProtoMember(2)]
public short OffsetMinutes { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate
{
DateTimeTicks = value.Ticks,
OffsetMinutes = (short)value.Offset.TotalMinutes
};
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));
}
}
Then I'm registering it right before the http call. I've tried moving this registration into a few different places but it doesn't seem to make a difference. Did this change in v3 or something or am I doing something wrong? Sorry - new to protobuf-net :)
public async Task<Response<IEnumerable<TestClass>>> GetData()
{
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
var request = new HttpRequestMessage(HttpMethod.Get, "my-url");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
var result = await _httpClient.SendAsync(request);
var items= ProtoBuf.Serializer.Deserialize<Response<IEnumerable<TestClass>>>(await result.Content.ReadAsStreamAsync());
return items;
}
I am using version 3.1.22 (Currently the latest) of protobuf-net right now with the following setup.
// Needs to be only called at application startup, e.g. in the Startup.cs calls of your web API.
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset?), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
The surrogate handler I use successfully, can be found below. No black magic happening here, just serializing / deserializing the unix timestamp to long and vice versa:
namespace Something
{
using System;
using ProtoBuf;
/// <summary>
/// A surrogate handler for the <see cref="DateTimeOffset"/> class.
/// </summary>
[ProtoContract(Name = nameof(DateTimeOffset))]
public class DateTimeOffsetSurrogate
{
/// <summary>
/// Gets or sets the value.
/// </summary>
[ProtoMember(1)]
public long? Value { get; set; }
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate surrogate)
{
if (surrogate?.Value is null)
{
throw new ArgumentNullException(nameof(surrogate));
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset?(DateTimeOffsetSurrogate? surrogate)
{
if (surrogate?.Value is null)
{
return null;
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset source)
{
return new DateTimeOffsetSurrogate
{
Value = source.ToUnixTimeMilliseconds()
};
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset? source)
{
return new DateTimeOffsetSurrogate
{
Value = source?.ToUnixTimeMilliseconds()
};
}
}
}
Maybe, you want to give it a try. Maybe [ProtoContract(Name = nameof(DateTimeOffset))] is what you're missing, but I'm not sure.

Web APIs methods with Warnings

I have this method in an API that does POST for creating a record, but before inserting that record in DB there are some validations that i must do, I might come back with warnings and I need to return back these warnings to the client to confirm back.
what is the best way to do that in Web API's? or should i split the method into 2, one for validation and one for saving?
Set up your Web Api the following way.
[HttpPost]
public IHttpActionResult DoStuff(object item)
{
if(!validate(item))
{
return this.BadRequest("Validation failed blablabla");
}
else
{
//insert logic
}
return this.Ok();
}
What happens is you validate the object you send to the API. When it fails to validate you return the request was not correct, with the message you specified.
When the validation succeeds the insert logic is called and you return a OK result when it succeeds.
I create and use a wrapper which would contain object to be bound to ui, messages (error or validation or warning) and total.
/// <summary>
/// Class for the Repository response object
/// </summary>
/// <typeparam name="T"></typeparam>
public class CLSResponse<T>
{
/// <summary>
/// Gets or sets the messages to be returned in the response.
/// </summary>
/// <value>The messages.</value>
public IEnumerable<KeyValuePair<string, string>> Messages {
get { return m_Messages; }
set { m_Messages = value; }
}
private IEnumerable<KeyValuePair<string, string>> m_Messages;
/// <summary>
/// Gets or sets the service model to be returned in the response.
/// </summary>
/// <value>The service model.</value>
public T ServiceModel {
get { return m_ServiceModel; }
set { m_ServiceModel = value; }
}
private T m_ServiceModel;
/// <summary>
/// Gets or sets the totalitems.
/// </summary>
/// <value>The TotalItems.</value>
public int TotalItems {
get { return m_TotalItems; }
set { m_TotalItems = value; }
}
private int m_TotalItems;
/// <summary>
/// Gets and Sets the Message Type based on the MessageType Struct
/// </summary>
public string MessagesType;
}
/// <summary>
/// Struct for MessageTypes to be returned with messages, in the response object
/// </summary>
public struct MessagesType
{
/// <summary>
/// Validation
/// </summary>
public static string Validation = "Validation";
/// <summary>
/// Warning
/// </summary>
public static string Warning = "Warning";
/// <summary>
/// Error
/// </summary>
public static string Error = "Error";
/// <summary>
/// Unauthorized
/// </summary>
public static string UnAuthorized = "Unauthorized";
}
Then in your repository layer or logic layer
public CLSResponse<bool> CreateUser(string request)
{
var messages = new List<KeyValuePair<string, string>>();
try
{
//Do something
if (!validation)
{
messages.Add(MessagesType.Validation, "Invalid");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Validation
};
}
else {
return new CLSResponse<bool> {
ServiceModel = true,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
catch (Exception ex)
{
messages.Add(MessagesType.Error, "UpdateFailed");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
Now on controller,
[HttpPost]
public HttpResponseMessage<CLSResponse<bool>> CreateUser(string input)
{
var res = LogicLayer.CreateUser(input);
//Check for res.MessageType and set the status code
if (res.MessagesType = MessagesType.Validation)
{
return Request.CreateResponse<CLSResponse<bool>>(HttpStatusCode.PreconditionFailed, res);
}
}
This way your response object still has the warning you added in logic layer and depending on the status code returned, you can handle those messages on client side.

Testing ASP.NET Web API POST and Parameterless GET Routes with WebApiContrib.Testing

I am trying to set up some route tests using the WebApiContrib.Testing library. My get tests (like this) work fine...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"~/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>(c => c.Get("sjkfhiuehfkshjksdfh"));
}
I am rather lost on the post test - I currently have the following which fails with a NotImplementedException...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"~/auth".ShouldMapTo<AuthController>(c => c.Post(new AuthenticationCredentialsModel()), "POST");
}
Here's the setup and teardown for completeness...
[SetUp]
public void SetUpTest()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
[TearDown]
public void TearDownTest()
{
RouteTable.Routes.Clear();
GlobalConfiguration.Configuration.Routes.Clear();
}
The route I am trying to test is the default POST route, which maps to this method call...
[AllowAnonymous]
public HttpResponseMessage Post([FromBody] AuthenticationCredentialsModel model)
{ *** Some code here that doesn't really matter *** }
I am also getting a failure on this test that tests the standard GET route without parameters returns all of the items...
[Test]
[Category("VersionInfo Api Tests")]
public void TheVersionInfoControllerAcceptsAMultipleItemGetRouteForAllItems()
{
"~/versioninfo".ShouldMapTo<VersionInfoController>(c => c.Get());
}
Which is testing this method...
public HttpResponseMessage Get()
{ *** Some code here that doesn't really matter *** }
This library was recommended by several articles I read, but I'm not sure now if I'm doing something wrong or if it's just quite limited and I'm better off rolling my own.
There seems to be some nasty issues here - after debugging through the WebApiContrib.Testing library, I've found the following...
In the route mapping code used for the libraries own tests, the route mappings look like this...
// GET /api/{resource}
routes.MapHttpRoute(
name: "Web API Get All",
routeTemplate: "api/{controller}",
defaults: new {action = "Get"},
constraints: new {httpMethod = new HttpMethodConstraint("GET")}
);
My route mapping looks like this for essentially the same route...
config.Routes.MapHttpRoute("DefaultApiGet", "{controller}",
new { action = "Get" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
Notice the different syntax for constraint building. My route mapping code won't allow strings. Maybe this is something that's changed in the WebAPI version since the library was written, I'm not sure, but it seems that any route that I'm trying to test with an HttpMethodConstraint will fail.
I'm continuing to investigate this one; I now have a 'why', but not a 'how to fix' yet.
UPDATE: The WebAPI routing takes two forms of constraint, one of which is just a string (more details here - http://forums.asp.net/p/1777747/4904556.aspx/1?Re+System+Web+Http+Routing+IHttpRouteConstraint+vs+System+Web+Routing+IRouteConstraint). I tried changing the routing table to see if that would make any difference, but it didn't - just to let you know!
I fixed this in the end by writing my own, after reading a post by whyleee on another question here - Testing route configuration in ASP.NET WebApi (WebApiContrib.Testing didn't seem to work for me)
I merged his post with some of the elements I liked syntactically from the WebApiContrib.Testing library to generate the following helper class.
This allows me to write really lightweight tests like this...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"http://api.siansplan.com/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>("Get", "hash");
}
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"http://api.siansplan.com/auth".ShouldMapTo<AuthController>("Post", HttpMethod.Post);
}
The helper class looks like this...
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Hosting;
using System.Web.Http.Routing;
namespace SiansPlan.Api.Tests.Helpers
{
public static class RoutingTestHelper
{
/// <summary>
/// Routes the request.
/// </summary>
/// <param name="config">The config.</param>
/// <param name="request">The request.</param>
/// <returns>Inbformation about the route.</returns>
public static RouteInfo RouteRequest(HttpConfiguration config, HttpRequestMessage request)
{
// create context
var controllerContext = new HttpControllerContext(config, new Mock<IHttpRouteData>().Object, request);
// get route data
var routeData = config.Routes.GetRouteData(request);
RemoveOptionalRoutingParameters(routeData.Values);
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
controllerContext.RouteData = routeData;
// get controller type
var controllerDescriptor = new DefaultHttpControllerSelector(config).SelectController(request);
controllerContext.ControllerDescriptor = controllerDescriptor;
// get action name
var actionMapping = new ApiControllerActionSelector().SelectAction(controllerContext);
var info = new RouteInfo(controllerDescriptor.ControllerType, actionMapping.ActionName);
foreach (var param in actionMapping.GetParameters())
{
info.Parameters.Add(param.ParameterName);
}
return info;
}
#region | Extensions |
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, params string[] parameterNames)
{
return ShouldMapTo<TController>(fullDummyUrl, action, HttpMethod.Get, parameterNames);
}
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
/// <exception cref="System.Exception"></exception>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, HttpMethod httpMethod, params string[] parameterNames)
{
var request = new HttpRequestMessage(httpMethod, fullDummyUrl);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var route = RouteRequest(config, request);
var controllerName = typeof(TController).Name;
if (route.Controller.Name != controllerName)
throw new Exception(String.Format("The specified route '{0}' does not match the expected controller '{1}'", fullDummyUrl, controllerName));
if (route.Action.ToLowerInvariant() != action.ToLowerInvariant())
throw new Exception(String.Format("The specified route '{0}' does not match the expected action '{1}'", fullDummyUrl, action));
if (parameterNames.Any())
{
if (route.Parameters.Count != parameterNames.Count())
throw new Exception(
String.Format(
"The specified route '{0}' does not have the expected number of parameters - expected '{1}' but was '{2}'",
fullDummyUrl, parameterNames.Count(), route.Parameters.Count));
foreach (var param in parameterNames)
{
if (!route.Parameters.Contains(param))
throw new Exception(
String.Format("The specified route '{0}' does not contain the expected parameter '{1}'",
fullDummyUrl, param));
}
}
return true;
}
#endregion
#region | Private Methods |
/// <summary>
/// Removes the optional routing parameters.
/// </summary>
/// <param name="routeValues">The route values.</param>
private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValues)
{
var optionalParams = routeValues
.Where(x => x.Value == RouteParameter.Optional)
.Select(x => x.Key)
.ToList();
foreach (var key in optionalParams)
{
routeValues.Remove(key);
}
}
#endregion
}
/// <summary>
/// Route information
/// </summary>
public class RouteInfo
{
#region | Construction |
/// <summary>
/// Initializes a new instance of the <see cref="RouteInfo"/> class.
/// </summary>
/// <param name="controller">The controller.</param>
/// <param name="action">The action.</param>
public RouteInfo(Type controller, string action)
{
Controller = controller;
Action = action;
Parameters = new List<string>();
}
#endregion
public Type Controller { get; private set; }
public string Action { get; private set; }
public List<string> Parameters { get; private set; }
}
}

dynamic load recive activity wf

I'm trying to load and invoke activityes from custom activity as follows:
Imagine I have a xamlx like this:
--Sequence
|----- LoadActiviy
|--Initialize dictionary with input data of activitity
|----- Invoke
This works when activity NOT CONTAINS receive/send messages. But when i try with activity wich contains receive/send messages the result is a exception
WorkflowApplicationUnhandledExceptionEventArgs: Only registered bookmark scopes can be used for creating scoped bookmarks.
The code:
1-Load xaml: (Load activity)
public sealed class LoadActivity : CodeActivity<Activity>
{
#region Properties
/// <summary>
/// Gets or sets Path.
/// </summary>
[RequiredArgument]
public InArgument<string> Path { get; set; }
#endregion
#region Methods
/// <summary>
/// The execute method.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// An activity loaded from a file
/// </returns>
protected override Activity Execute(CodeActivityContext context)
{
return ActivityXamlServices.Load(this.Path.Get(context));
}
#endregion
}
2- Run activity:
public class SynchronousSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
public sealed class Invoke : CodeActivity
{
#region Properties
/// <summary>
/// Gets or sets Activity.
/// </summary>
/// <remarks>
/// The activity that will be invoked. Can be loaded from XAML.
/// </remarks>
[RequiredArgument]
public InArgument<Activity> Activity { get; set; }
public OutArgument<IDictionary<string, object>> Output { get; set; }
/// <summary>
/// Gets or sets Input.
/// </summary>
/// <remarks>
/// The input arguments you want to pass to the other workflow
/// </remarks>
public InArgument<IDictionary<string, object>> Input { get; set; }
#endregion
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
try
{
IDictionary<string,object> _input= this.Input.Get(context);
foreach (KeyValuePair<string,object> item in _input )
{
Debug.WriteLine(string.Format("{0} {1}", item.Key, item.Value));
}
// AutoResetEvent idleEvent = new AutoResetEvent(false);
WorkflowApplication app = new WorkflowApplication(this.Activity.Get(context),this.Input.Get(context));
app.SynchronizationContext = new SynchronousSynchronizationContext();
app.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
// idleEvent.Set();
};
app.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
e.InstanceId, e.UnhandledException.Message);
Console.WriteLine("ExceptionSource: {0} - {1}",
e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
app.Idle = e => Console.WriteLine("WorkflowApplication.Idle called");
Console.WriteLine("Before WorkflowApplication.Run()");
app.Run();
}
catch
{
throw;
}
}
}
Any ideas?
You can only use a Receive activity in a workflow hosted in a WorkflowServiceHost. Even if your main workflow is hosted in a WorkflowServiceHost the child workflow is hosted in a WorkflowApplication and can't contain a Receive activity because it isn't running as part of the WCF infrastructure.

Resources