Task.FromResult with state?
-
2012年2月7日 10:47
Hello Parallel team,
I find the Task.FromResult method a great little helper method for use in mock or designer implementations of interfaces with methods returning Tasks (note I am not using async / await syntax. I simply make use of the Task support added to Silverlight 5). I have a continuation that requires the correct state to be set on the task. Could the Task.FromResult method take an optional state parameter? For now I have created my own 'extension' method as follows:
public static Task<TResult> FromResult<TResult>(TResult result, object state = null) { var source = new TaskCompletionSource<TResult>(state ?? result); source.TrySetResult(result); return source.Task; }
but it would be great to see this added to the (Silverlight) framework.
cheers
Remco
- 已编辑 Remco Blok 2012年2月7日 10:51
全部回复
-
2012年2月7日 15:51版主
Hi Remco-
Thanks for the request. As you know, such an overload of Task.FromResult doesn't currently exist in the Framework, but it's something that could be considered for the future. Under what circumstances do you need this? Typically we've seen that the object state for Tasks is used either to pass data into a delegate in order to avoid extra allocations, or it's used when implementing the APM pattern, and I'm curious to understand your scenario as well.
-
2012年2月7日 16:31
Hi Stephen,
In my scenario it is no so much to avoid extra allocations as performance is not as issue, but I simply wanted to use a named method instead of a lambda and hence the need to pass state around. I use a TaskCompletionSource to manage a WCF RIA Services load operation. The WCF RIA Services domain context is abstracted behind an interface with a method like so:
Task<ServiceLoadResult<EntryPresentationModel>> LoadEntriesByLogbookNameAsync(string logbookName, QueryBuilder<EntryPresentationModel> queryBuilder, object state = null);
My viewmodel looks as follows:
public class EntriesViewModel : NotificationObject
{
private readonly IAppService appService;
private readonly ILogbookViewerContext context;
private readonly ExceptionManager exceptionManager;
private static TaskContinuationOptions continuationOptions = TaskContinuationOptions.None;public EntriesViewModel(ILogbookViewerContext context, IAppService appService, ExceptionManager exceptionManager)
{
this.context = context;
this.appService = appService;
this.exceptionManager = exceptionManager;this.Entries = new VirtualQueryableCollectionView<EntryPresentationModel> { LoadSize = this.appService.LoadSize, VirtualItemCount = this.appService.LoadSize };
this.Entries.ItemsLoading += this.OnEntriesItemsLoading;
}public VirtualQueryableCollectionView<EntryPresentationModel> Entries { get; private set; }
internal static TaskContinuationOptions ContinuationOptions
{
set
{
continuationOptions = value;
}
}private void OnEntriesItemsLoading(object sender, VirtualQueryableCollectionViewItemsLoadingEventArgs e)
{
var queryBuilder = new QueryBuilder<EntryPresentationModel>(true).SortBy(this.Entries).Skip(e.StartIndex).Take(e.ItemCount);
this.context.LoadEntriesByLogbookNameAsync(this.appService.LogbookName, queryBuilder, e.StartIndex).ContinueWith(
this.OnLoadEntriesComplete, CancellationToken.None, continuationOptions, TaskScheduler.FromCurrentSynchronizationContext());
}private void OnLoadEntriesComplete(Task<ServiceLoadResult<EntryPresentationModel>> task)
{
if (task.IsFaulted)
{
Exception exceptionToThrow;
var rethrow = this.exceptionManager.HandleException(task.Exception.InnerException, ExceptionHandlingPolicies.ServiceLayer, out exceptionToThrow);
if (rethrow)
{
throw exceptionToThrow ?? task.Exception.InnerException;
}
}
else if (!task.IsCanceled)
{
if (task.Result.TotalEntityCount != -1 && task.Result.TotalEntityCount != this.Entries.VirtualItemCount)
{
this.Entries.VirtualItemCount = task.Result.TotalEntityCount;
}var startIndex = (int)task.AsyncState;
this.Entries.Load(startIndex, task.Result.Entities);
}
}
}In a unit test of this viewmodel I use Moq to create a mock of the domain context interface like so:
var contextMock = new Mock<ILogbookViewerContext>();
contextMock.Setup(c => c.LoadEntriesByLogbookNameAsync(It.IsAny<string>(), It.IsAny<QueryBuilder<EntryPresentationModel>>(), It.IsAny<object>())).Returns(TaskExtensions.FromResult(expectedEntriesResult, 0));This is where I used my FromResult method that takes a state parameter.
I also set
EntriesViewModel.ContinuationOptions = TaskContinuationOptions.ExecuteSynchronously;
in my unit test to make sure the continuation in the viewmodel is run synchronously when running in my unit test. Otherwise it will run asynchronously.
- 已编辑 Remco Blok 2012年2月7日 16:41
-
2012年2月8日 8:35
Hi Stephen,
Thanks very much for writing your article on when ExecuteSynchronously does not execute synchronously (http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx). That was brilliantly timed! I had previously tried Stephen Cleary's AsyncContext (http://nitoasyncex.codeplex.com/, http://nuget.org/packages/Nito.AsyncEx) but it does not yet support Silverlight 5. It insists on loading the SL 4 version of AsyncCtpLibrary. Perhaps I should prod Stephen Cleary. I'll also see if http://nuget.org/packages/AsyncUnitTests-MSTest is any help, but I do not use async-await syntax.
cheers
Remco
- 已编辑 Remco Blok 2012年2月8日 8:37
-
2012年2月8日 19:25
Perhaps I should prod Stephen Cleary.
I'll also see if http://nuget.org/packages/AsyncUnitTests-MSTest is any help, but I do not use async-await syntax.
I don't think so. AsyncUnitTests is designed to work with the desktop-oriented MSTest unit tests. I was under the impression that Silverlight had its own testing system - and I assumed it would provide a proper context.
-Steve
Programming blog: http://nitoprograms.blogspot.com/
Including my TCP/IP .NET Sockets FAQ
and How to Implement IDisposable and Finalizers: 3 Easy Rules
Microsoft Certified Professional Developer
How to get to Heaven according to the Bible -
2012年2月9日 9:43
Hi Stephen,
Thank you very much! That was very quick. I've tried it out so instead of setting
EntriesViewModel.ContinuationOptions = TaskContinuationOptions.ExecuteSynchronously;
I now execute the test in AsyncContext.Run. However, I get the following error message:
System.TypeLoadException occurred
Message=Inheritance security rules violated while overriding member: 'Nito.AsyncEx.AsyncContext+AsyncContextTaskScheduler.GetScheduledTasks()'. Security accessibility of the overriding method must match the security accessibility of the method being overriden.
StackTrace:
at Nito.AsyncEx.AsyncContext..ctor(Action action)
at Nito.AsyncEx.AsyncContext.Run(Action action)
at OpralogLogbookViewer.Tests.Views.EntriesViewModelTest.GivenAnEntryWhenViewingDetailsThenShouldLoadInformationGroups()
InnerException:Any idea what I can do?
cheers
Remco
-
2012年2月10日 20:35
I uploaded a sample project to http://code.msdn.microsoft.com/A-Task-based-CollectionView-09ba7c96. In the SampleViewModelTest when you replace the lines
ServiceCollectionViewLoader<SampleEntity>.ContinuationOptions = TaskContinuationOptions.ExecuteSynchronously; var target = new SampleViewModel(contextMock.Object);
with
//ServiceCollectionViewLoader<SampleEntity>.ContinuationOptions = TaskContinuationOptions.ExecuteSynchronously; SampleViewModel target = null; AsyncContext.Run(() => target = new SampleViewModel(contextMock.Object));
then you get the exception
System.TypeLoadException was unhandled by user code
Message=Inheritance security rules violated while overriding member: 'Nito.AsyncEx.AsyncContext+AsyncContextTaskScheduler.GetScheduledTasks()'. Security accessibility of the overriding method must match the security accessibility of the method being overriden.
StackTrace:
at Nito.AsyncEx.AsyncContext..ctor(Action action)
at Nito.AsyncEx.AsyncContext.Run(Action action)
at DomainCollectionViewSample.Tests.SampleViewModelTest.TestMethod1()
InnerException:cheers
Remco
-
2012年2月10日 20:39
Thanks, Remco -
I'll take a look when I get the chance. I suspected there may be some security issues with the Silverlight port.
-Steve
Programming blog: http://nitoprograms.blogspot.com/
Including my TCP/IP .NET Sockets FAQ
and How to Implement IDisposable and Finalizers: 3 Easy Rules
Microsoft Certified Professional Developer
How to get to Heaven according to the Bible -
2012年2月12日 11:07
Hi Stephen Cleary and Stephen Toub,
I must have been in need of the weekend at the end of last week :-). It was good to take a break, read some of your blog posts again and let things sink in again. I now think that for the particular example I posted on code.msdn the Nito.AsyncEx.AsyncContext did not really apply as the 'asynchronous' task was handled internally in the CollectionViewLoader and not on the SampleViewModel public interface so that Nito.AsyncEx.AsyncContext could wait on the task and in any case I wanted the task to execute synchronously in the unit test. And I think that in the case of handling a WCF RIA Services load operation it is quite alright to execute the continuation synchronously at all times, as the completion callback of the load operation is already dispatched on the UI thread. That way I managed to avoid having to set the continuation options on a static property setter (ServiceCollectionViewLoader<SampleEntity>.ContinuationOptions = TaskContinuationOptions.ExecuteSynchronously;), which was a bit off a hack.
Still, I am curious about the security issues when using Nito.AsyncEx.AsyncContext in Silverlight as I may have a need for it later. I also came across a security issue earlier trying to handle the TaskScheduler.UnobservedTaskException in Silverlight, which you are basically not permitted to handle in Silverlight. I would love to understand that one. I am somewhat concerned at calling MarkErrorAsHandled on the load operation when it is possible for the exception to remain unobserved.
One more day of the weekend, yes....
cheers
Remco
- 已编辑 Remco Blok 2012年2月12日 11:08
- 已标记为答案 Stephen Toub - MSFTMicrosoft Employee, Moderator 2012年4月9日 1:30

