I just started using xamarin android with MVVMcross platform. I need to cancel an IMvxAsyncCommand but I try a lot of way, it still not work. Does anyone have any ideas or sample of this?
You can actually cancel a MvxAsyncCommand directly without creating any additional CancellationTokenSource.
Just call Cancel() on the object itself.
So instead of having the property being ICommand or IMvxCommand use MvxAsyncCommand or IMvxAsyncCommand.
This way when you have a CancellationToken as argument in your Task and you call Cancel() on the command, it will cancel that token.
private async Task MyAsyncStuff(CancellationToken ct)
{
await abc.AwesomeAsyncMethod(ct);
// and/or
ct.ThrowIfCancellationRequested();
}
So if you modify #fmaccaroni's answer, you will get this:
public class MyClass
{
public MyClass()
{
MyAsyncCommand = new MvxAsyncCommand(MyAsyncStuff);
CancelMyTaskCommand = new MvxCommand(() => MyAsyncCommand.Cancel());
}
public IMvxAsyncCommand MyAsyncCommand {get; private set; }
public ICommand CancelMyTaskCommand { get; private set; }
private async Task MyAsyncStuff(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await foo.BlablaAsync();
cancellationToken.ThrowIfCancellationRequested();
await bar.WhateverAsync();
cancellationToken.ThrowIfCancellationRequested();
await foo2.AnotherAsync();
}
}
EDIT: or even better if your async methods also allow passing along the CancellationToken cancelling them directly, the MyAsyncStuff method could be written like:
private async Task MyAsyncStuff(CancellationToken cancellationToken)
{
await foo.BlablaAsync(cancellationToken);
await bar.WhateverAsync(cancellationToken);
await foo2.AnotherAsync(cancellationToken);
}
Or if you don't care which order they get started and complete in:
private Task MyAsyncStuff(CancellationToken cancellationToken)
{
return Task.WhenAll(
foo.BlablaAsync(cancellationToken),
bar.WhateverAsync(cancellationToken),
foo2.AnotherAsync(cancellationToken));
}
So basically a IMvxAsyncCommand just calls a Task like this:
MyCommand = new MvxAsyncCommand(MyTask);
...
private async Task MyTask()
{
// do async stuff
}
In order to cancel a Task you need to use a CancellationToken, which you can get it from a CancellationTokenSource and use another command or whatever you want to call the Cancel() on the CancellationTokenSource, so one way you could do it would be:
public class MyClass
{
private CancellationTokenSource cts;
public MyClass()
{
MyAsyncCommand = new MvxAsyncCommand(MyTask);
CancelMyTaskCommand = new MvxCommand(() => cts.Cancel());
}
public ICommand MyAsyncCommand {get; private set; }
public ICommand CancelMyTaskCommand { get; private set; }
private async Task MyTask()
{
cts = new CancellationTokenSource();
await MyAsyncStuff(cts.CancellationToken);
cts.Dispose();
cts = null;
}
private async Task MyAsyncStuff(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await foo.BlablaAsync();
cancellationToken.ThrowIfCancellationRequested();
await bar.WhateverAsync();
cancellationToken.ThrowIfCancellationRequested();
await foo2.AnotherAsync();
}
}
You can then improve the code to avoid duplicate calls to the Task, handle the cancellation exception, use an MvxNotifyTask and many other things but this is the basics.
More info in Cancellation in Managed Threads
HIH
Related
I am trying to understand how MediatR works. Never used this library before. Below code is not actual production code. It is only for understanding purpose.
Lets say I have two RequestHandlers. Each handler takes ProductModel as request but returns different type of response.
public class GetOrdersHandler : IRequestHandler<ProductModel, IEnumerable<Order>>
{
private readonly FakeDataStore _fakeDataStore;
public GetOrdersHandler(FakeDataStore fakeDataStore)
{
_fakeDataStore = fakeDataStore;
}
public async Task<IEnumerable<Order>> Handle(ProductModel request,CancellationToken cancellationToken
{
return await _fakeDataStore.GetAllOrdersForProduct(request);
}
}
public class SaveProductHandler : IRequestHandler<ProductModel, Product>
{
private readonly FakeDataStore _fakeDataStore;
public SaveProductHandler(FakeDataStore fakeDataStore)
{
_fakeDataStore = fakeDataStore;
}
public async Task<Product> Handle(ProductModel request,CancellationToken cancellationToken)
{
return await _fakeDataStore.SaveProduct(request);
}
}
Then in the same controller I have two action methods
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator) => _mediator = mediator;
[HttpPost]
public async Task<ActionResult> GetAllOrders(ProductModel model)
{
var product = await _mediator.Send(model);
return Ok(product);
}
[HttpPost]
public async Task<ActionResult> SaveProduct(ProductModel model)
{
var product = await _mediator.Send(model);
return Ok(product);
}
}
Based on the MediatR code this may not work. Looks like the Send method creates instance of handler based on Request type. It keeps dictionary of RequestType and corresponding handler.
If my assumption is correct then does that mean I will have to create unique request model for each action method that will be using Send method?
I have a standard .Net core Api and want to use a Open Generic IReposistory and decorate that with a DomainEventPublisher for pushing out events to servicsBus after persisting.
However, I have used Simple Injector a lot earlier which I'm a big fan of. But now when using MediatR Im trying to simplify DI by using just .net Core DI together with Scrutor package for decorating.
Problem is an error I get:
"The number of generic arguments provided doesn't equal the arity of the generic type definition." from Scrutor when trying to register decorator in Startup (2nd line below).
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>));
services.Decorate(typeof(IRepository<>), typeof(DomainEventPublisher<>));
I have closed these generic classes/interfaces and then it works. But Im not good with that. I would to i the right way like I used to do in Simpleinjector, and register open generic decorator.
Any suggestions what might be the problem?
public class Repository<TEntity> : IRepository<TEntity>
{
private readonly CosmosClient _client;
private readonly IDataContext<TEntity> _context;
private readonly Container _container;
public Repository(CosmosClient client, IDataContext<TEntity> context)
{
_client = client;
_context = context ?? throw new ArgumentNullException(nameof(context));
_container = _client.GetContainer(_context.GetDatabase(), _context.GetContainer());
}
public virtual async Task Add(TEntity entity)
{
try
{
var response = await _container.CreateItemAsync(entity, new PartitionKey(_context.GetPartitionKey(entity)));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public virtual async Task<TEntity> Get(string id)
{
var response = await _container.ReadItemAsync<TEntity>(id, new PartitionKey(_context.GetPartitionKey(id)));
return response.Resource;
}
public virtual async Task<TEntity> Update(TEntity entity)
{
var response = await _container.UpsertItemAsync(entity, new PartitionKey(_context.GetPartitionKey(entity)));
return response.Resource;
}
public async Task Remove(string id)
{
var response = await _container.DeleteItemAsync<TEntity>(id, new PartitionKey(_context.GetPartitionKey(id)));
}
public class DomainEventPublisher<TEntity> : IRepository<TEntity>
{
private readonly IRepository<TEntity> _decoratedRepository;
private readonly ITopicAdapter _bus;
private readonly IMapper _mapper;
private List<IDomainEvent> _eventsToProcess = new List<IDomainEvent>();
public DomainEventPublisher(IRepository<TEntity> decoratedRepository, ITopicAdapter bus, IMapper mapper)
{
_decoratedRepository = decoratedRepository ?? throw new ArgumentNullException(nameof(decoratedRepository));
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public async Task Add(TEntity entity)
{
// Get all domain events raised by source entity
var events = CollectEvents(entity);
await _decoratedRepository.Add(entity);
await HandleEvents(events);
}
public async Task<TEntity> Get(string id)
{
return await _decoratedRepository.Get(id);
}
public async Task<TEntity> Update(TEntity entity)
{
// Get all domain events raised by source entity
var events = CollectEvents(entity);
var result = await _decoratedRepository.Update(entity);
await HandleEvents(events);
return result;
}
public async Task Remove(string id)
{
await _decoratedRepository.Remove(id);
}
private List<IDomainEvent> CollectEvents(TEntity entity)
{
if (entity is IEntity entityWithEvents)
return entityWithEvents.Events;
return new List<IDomainEvent>();
}
private async Task HandleEvents(List<IDomainEvent> events)
{
// if we ended up on this line we know that repository persisted changes and now send events to bus
foreach (var domainEvent in events)
{
await _bus.Send(_mapper.MapTo(domainEvent));
}
}
}
It's impossible to apply decorators to open-generic registration with Scrutor. This is discussed here on the Scrutor forum. This is due to a limitation of the underlying Microsoft DI Container. This is a limitation that can't be circumvented by Scrutor.
Instead, switch to one of the mature DI Containers that do support this.
I have an API controller with an action function. This function makes an external call for another API to get some data. this external call is made by simply creating a client with a URL. I want to create a test using WebApplicationFactory to test this action function.
I would like to know how to configure this external call. To say if the server calls this URL return this response.
May be it should be somewhere in overriding ConfigureWebHost to tell the server that if you call this URL (The external API url) return this response.
Here is the controller action I want to test.
namespace MyAppAPI.Controllers
{
public class MyController : ControllerBase
{
[HttpPost("MyAction")]
public async Task MyAction([FromBody] int inputParam)
{
var externalApiURL = "http://www.external.com?param=inputParam";
var client = new HttpClient();
var externalResponse = await client.GetAsync(externalApiURL);
//more work with the externalResponse
}
}
}
Here is the Test class I want to use
public class MyAppAPITests : IClassFixture<WebApplicationFactory<MyAppAPI.Startup>>
{
private readonly WebApplicationFactory<MyAppAPI.Startup> _factory;
public MyAppAPITests(WebApplicationFactory<MyAppAPI.Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task Test_MyActionReturnsExpectedResponse()
{
//Arrange Code
//Act
//Here I would like to have something like this or a similar fashion
_factory.ConfigureReponseForURL("http://www.external.com?param=inputParam",
response => {
response.Response = "ExpectedResponse";
});
//Assert Code
}
}
The code in Test_MyActionReturnsExpectedResponse does not exist anywhere, it is just what I would like to have either by inheriting WebApplicationFactory or by configuring it. I would like to know how that can be achieved. i.e. configuring a response when an API controller makes an external call.
Thank you for your help.
The issue is that you have a hidden dependency, namely HttpClient. Because you're newing this up in your action, it's impossible to mock. Instead, you should be injecting this dependency into your controller. With ASP.NET Core 2.1+ that's possible with HttpClient thanks to IHttpClientFactory. However, out of the box, you cannot inject HttpClient directly into a controller, because controllers are not registered in the service collection. While you can change that, the recommended approach is to instead create a "service" class. This is actually better anyways as it abstract the knowledge of interacting with this API out of your controller entirely. Long and short, you should do something like:
public class ExternalApiService
{
private readonly HttpClient _httpClient;
public ExternalApiService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public Task<ExternalReponseType> GetExternalResponseAsync(int inputParam) =>
_httpClient.GetAsync($"/endpoint?param={inputParam}");
}
Then, register this in ConfigureServices:
services.AddHttpClient<ExternalApiService>(c =>
{
c.BaseAddress = new Uri("http://www.external.com");
});
And finally, inject it into your controller:
public class MyController : ControllerBase
{
private readonly ExternalApiService _externalApi;
public MyController(ExternalApiService externalApi)
{
_externalApi = externalApi;
}
[HttpPost("MyAction")]
public async Task MyAction([FromBody] int inputParam)
{
var externalResponse = await _externalApi.GetExternalResponseAsync(inputParam);
//more work with the externalResponse
}
}
Now, the logic for working with this API is abstracted from your controller and you have a dependency you can easily mock. Since you're wanting to do integration testing, you'll need to sub in a different service implementation when testing. For that, I'd actually do a little further abstraction. First, create an interface for the ExternalApiService and make the service implement that. Then, in your test project you can create an alternate implementation that bypasses HttpClient entirely and just returns pre-made responses. Then, while not strictly necessary, I'd create an IServiceCollection extension to abstract the AddHttpClient call, allowing you to reuse this logic without repeating yourself:
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddExternalApiService<TImplementation>(this IServiceCollection services, string baseAddress)
where TImplementation : class, IExternalApiService
{
services.AddHttpClient<IExternalApiService, TImplementation>(c =>
{
c.BaseAddress = new Uri(baseAddress)
});
return services;
}
}
Which you would then use like:
services.AddExternalApiService<ExternalApiService>("http://www.external.com");
The base address could (and probably should) be provided via config, for an extra layer of abstraction/testability. Finally, you should be use a TestStartup with WebApplicationFactory. It makes it far easier to switch out services and other implementations without rewriting all your ConfigureServices logic in Startup, which of course adds variables to your test: e.g. is it not working because I forgot to register something the same way as in my real Startup?
Simply add some virtual methods to your Startup class and then use these for things like adding your databases, and here, adding your service:
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
AddExternalApiService(services);
}
protected virtual void AddExternalApiService(IServiceCollection services)
{
services.AddExternalApiService<ExternalApiService>("http://www.external.com");
}
}
Then, in your test project, you can derive from Startup and override this and similar methods:
public class TestStartup : MyAppAPI.Startup
{
protected override void AddExternalApiService(IServiceCollection services)
{
// sub in your test `IExternalApiService` implementation
services.AddExternalApiService<TestExternalApiService>("http://www.external.com");
}
}
Finally, when getting your test client:
var client = _factory.WithWebHostBuilder(b => b.UseStartup<TestStartup>()).CreateClient();
The actual WebApplicationFactory still uses MyAppAPI.Startup, since that generic type param corresponds to the app entry point, not actually what Startup class is being used.
I think best way - i to use interfaces and MOCK's. Implement interface by inheritance of HttpClient and at the test mock this interface:
public interface IHttpClientMockable
{
Task<string> GetStringAsync(string requestUri);
Task<string> GetStringAsync(Uri requestUri);
Task<byte[]> GetByteArrayAsync(string requestUri);
Task<byte[]> GetByteArrayAsync(Uri requestUri);
Task<Stream> GetStreamAsync(string requestUri);
Task<Stream> GetStreamAsync(Uri requestUri);
Task<HttpResponseMessage> GetAsync(string requestUri);
Task<HttpResponseMessage> GetAsync(Uri requestUri);
Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption);
Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption);
Task<HttpResponseMessage> GetAsync(string requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(Uri requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content);
Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content);
Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> DeleteAsync(string requestUri);
Task<HttpResponseMessage> DeleteAsync(Uri requestUri);
Task<HttpResponseMessage> DeleteAsync(string requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> DeleteAsync(Uri requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken);
void CancelPendingRequests();
HttpRequestHeaders DefaultRequestHeaders { get; }
Uri BaseAddress { get; set; }
TimeSpan Timeout { get; set; }
long MaxResponseContentBufferSize { get; set; }
void Dispose();
}
public class HttpClientMockable: HttpClient, IHttpClientMockable
{
}
I want to check if this possible, I have an MVC Controller Action method (POST) which should have 2 parallel tasks, completing one of them should return the view based on the result from task that has completed.
Task is not aweb service which has asynchronous options. Assume its more like creating a bunch of file. I really want my task as simple as this.
private static string DoStuff(int sleepTime, string input)
{
Thread.Sleep(sleepTime);
return input;
}
Task would be like - but i dont if this is correct or not because there is not 'await'
public async Task<string> DelayTimerCallAsync()
{
string msgs = DoStuff(Convert.ToInt32(
ConfigurationManager.AppSettings["DelayTimer"]),
"Timeout");
return msgs;
}
Let me know if my question is not clear or if you need more information.
From below update my 2 tasks are like this
public Task<String> DelayTimerCallAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
String messages = null;
return DelayTimer();
}, cancellationToken);
}
public Task<String> BlazeCallAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
String messages = null;
return BlazeCall();
}, cancellationToken);
}
How can I call them and use Task.WhenAny(task1, task2) ?
Not sure if I understand the question, however I'll take a stab at it.
Assuming you have "work" to do (that would normally run synchronously) but would like to wrap that in an async method, it would look sort of like the following:
public Task<String> DelayTimerCallAsync(CancellationToken cancellationToken)
{
return Task.Run(() => {
String messages = null;
/* do work here (assign messages a value) */
return messages;
}, cancellationToken);
}
If you're looking to do the opposite (run a Task synchronously) you can do so easily as well. To help, here's a simple class that makes things a bit easier:
public static class AsyncHelper
{
private static readonly TaskFactory taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
taskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return taskFactory
.StartNew<Task<TResult>>(func)
.Unwrap<TResult>()
.GetAwaiter()
.GetResult();
}
}
You then have the ability to execute Task methods without await. Something like:
// (referencing DelayTimeCallAsync above:
String messages = AsynHelper.RunSync(DelayTimeCallAsync(CancellationToken.None));
I’m trying to log out Web API Request Content – i.e. the json string. I implemented an ITraceWriter class (example) and configured it so that Web API calls it in the pipeline. But if I read the request.Content or copy into to a stream to read it is not available for the method resulting in a null model. This post talks about that issue a little. Anyone have experience logging out inbound Web API request content and know what the best approach is?
Thanks
Update A
I created a simple sample Web API project to rule out anything in my project and I still see that the model will be null because of logging. I simply test a few times in a row by posting via Fidder and see my model comes in null. With breakpoints in place, it might work which is why I think there is a sync/timing issue. Any thoughts on how to get this to work?
Header:
User-Agent: Fiddler
Host: localhost:56824
Content-Type: application/json
Content-Length: 22
Body:
{
"A":1,"B":"test"
}
Here's the code:
Controller:
public class ValuesController : ApiController
{
[HttpPost]
public void Post(ValuesModel model)
{
if (model == null)
{
Debug.WriteLine("model was null!");
}
else
{
Debug.WriteLine("model was NOT null!");
}
}
}
Model:
public class ValuesModel
{
public int A { get; set; }
public string B { get; set; }
}
Logger:
public class APITraceLogger : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Content != null)
{
// This can cause model to be null
request.Content.ReadAsStringAsync().ContinueWith(s =>
{
string requestText = s.Result;
Debug.WriteLine(requestText);
});
// and so can this
//request.Content.ReadAsByteArrayAsync()
// .ContinueWith((task) =>
// {
// string requestText = System.Text.UTF8Encoding.UTF8.GetString(task.Result);
// Debug.WriteLine(requestText);
// });
}
// Execute the request, this does not block
var response = base.SendAsync(request, cancellationToken);
// TODO:
// Once the response is processed asynchronously, log the response data
// to the database
return response;
}
}
Wiring up logger in WebApiConfig class:
config.MessageHandlers.Add(new APITraceLogger());
Update B
It seems like it is now working if I change the logger to the following code adding the await, async and returning the result. Seems like something I'm not understanding in the async code or truly a timing issue or something.
public class APITraceLogger : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Content != null)
{
// This does seem to work - is it because it is synchronous? Is this a potential problem?
var requestText = await request.Content.ReadAsStringAsync();
Debug.WriteLine(requestText);
}
// Execute the request, this does not block
var response = base.SendAsync(request, cancellationToken);
// TODO:
// Once the response is processed asynchronously, log the response data
// to the database
return response.Result;
}
}
As Filip mentions in that post ReadAsStringAsync or ReadAsByteArrayAsync methods buffer the request content internally. This means that even if your incoming request's stream type is a non-buffered stream, you could safely do a ReadAsStringAsync/ReadAsByteArrayAsync at a message handler for example, and also expect the model binding to work fine.
By default, a request's stream is buffered in both webhost and selfhost cases. But if you would like to check if using ReadAsStringAsync/ReadAsByteArrayAsync and model biding works fine even in non-buffered mode, you can do the following to force non-buffered mode:
public class CustomBufferPolicySelector : WebHostBufferPolicySelector
{
public override bool UseBufferedInputStream(object hostContext)
{
//NOTE: by default, the request stream is always in buffered mode.
//return base.UseBufferedInputStream(hostContext);
return false;
}
}
config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomBufferPolicySelector());
Just FYI...the above policy selector works only for Web Host currently. If you would like to do a similar test in SelfHost, then do the following:
//NOTE: by default, the transfer mode is TransferMode.Buffered
config.TransferMode = System.ServiceModel.TransferMode.StreamedRequest;
After Update B of the post above:
You could modify your handler like below:
public class LoggingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content != null)
{
string requestContent = await request.Content.ReadAsStringAsync();
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.Content != null)
{
string responseContent = await response.Content.ReadAsStringAsync();
}
return response;
}
}