I write most of my backend code in F# - but my WPF project is in C# and I am wiring up progress reporting between the process and UI.
My F# Code
type DownloadProgressModel = {
mutable PercentageComplete: int
totalOrders: int
mutable orderPosition: int
}
let doMockDownload(progress: IProgress<DownloadProgressModel>) = async {
let downloadprogress: DownloadProgressModel = {
PercentageComplete = 0
totalOrders = 20
orderPosition = 0
}
for i in [0..20] do
do! Async.Sleep(2000)
downloadprogress.orderPosition <- i
downloadprogress.PercentageComplete <- (i*100) / 20
progress.Report(downloadprogress)
return "Finished"
}
My C# calling code from a WPF View
private async void SimpleButton_Click(object sender, RoutedEventArgs e)
{
Progress<DownloadProgressModel> progress = new Progress<DownloadProgressModel>();
progress.ProgressChanged += Progress_ProgressChanged;
var a = await MockDownload.doMockDownload(progress);
}
private void Progress_ProgressChanged(object sender, DownloadProgressModel e)
{
ordersProgress.Value = e.PercentageComplete;
}
I get the following Error: ( the offending line var a = await MockDownload.doMockDownload(progress); )
'FSharpAsync' does not contain a definition for 'GetAwaiter'
and no accessible extension method 'GetAwaiter' accepting a first
argument of type 'FSharpAsync' could be found (are you missing
a using directive or an assembly reference?)
This is losely based on C# Advanced Async - Getting progress reports, cancelling tasks, and more by "IamTimCorey" on youtube - but that is all C#.
The F# async computation expression is not directly compatible with C# async keyword. For code you control, that is meant to be consumed from C# you are probably best off to switch for the task computation expression instead
Alternately, you can use Async.StartAsTask to convert an Async<'T> to Task<'T>.
Related
I wanted to create a small extension to add a list of External Tools to VS2019. A quick search brought up what appeared to be perfect example code at https://learn.microsoft.com/en-us/visualstudio/extensibility/writing-to-the-user-settings-store?view=vs-2019. This adds a command to invoke Notepad, so I thought with a few edits, my work was done.
However, this example is written as a synchronous extension, which is deprecated, so I tried putting the code intended for MenuItemCallBack into the Execute method of the extension, but the line
SettingsManager settingsManager = new ShellSettingsManager(ServiceProvider);
fails to compile, because ServiceProvider is now type IAsyncServiceProvider and the ShellSettingsManager constructor wants an argument of type IServiceProvider.
As far as I can tell, ShellSettingsManager is still the way to access the Settings Store, but all the examples I could find all refer to putting code in MenuItemCallback (as well as being several years old) so are for synchronous extensions.
So, can someone point me to the recommended way to get access to the settings store in an asynchronous extension?
The ShellSettingsManager constructor takes either an IServiceProvider interface or an IVsSettings interface. Given your AsyncPackage derived object implements IServiceProvider, you should be able to just pass it in as the argument to your constructor. The following quick demo package worked for me:
using System;
using System.ComponentModel.Design;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using Task = System.Threading.Tasks.Task;
namespace UserSettingsDemo
{
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(UserSettingsDemoPackage.PackageGuidString)]
[ProvideMenuResource("Menus.ctmenu", 1)]
public sealed class UserSettingsDemoPackage : AsyncPackage
{
public const string PackageGuidString = "cff6cdea-21d1-4736-b5ea-6736624e718f";
public static readonly Guid CommandSet = new Guid("dde1417d-ae0d-46c4-8c84-31883dc1a835");
public const int ListExternalToolsCommand = 0x0100;
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
OleMenuCommandService commandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
Assumes.Present(commandService);
var menuItem = new MenuCommand(OnListExternalTools, new CommandID(CommandSet, ListExternalToolsCommand));
commandService.AddCommand(menuItem);
}
private void OnListExternalTools(object sender, EventArgs e)
{
ShellSettingsManager settingsManager = new ShellSettingsManager(this);
WritableSettingsStore userSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);
int toolCount = userSettingsStore.GetInt32("External Tools", "ToolNumKeys");
for (int i = 0; i < toolCount; i++)
{
string tool = userSettingsStore.GetString("External Tools", "ToolCmd" + i);
VsShellUtilities.ShowMessageBox(this, tool, "External Tools", OLEMSGICON.OLEMSGICON_INFO,
OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}
}
}
}
Sincerely
I was trying to verify whether my log warning message is written via NUnit mocking. I am getting this error message :
An exception of type 'System.NotSupportedException' occurred in Moq.dll but was not handled in user code
Additional information: Invalid verify on a non-virtual (overridable in VB) member: m => m.LogWarning(String.Format("comments not found for part number :{0}", (Object)0), new[] { "111" })
code:
mockLogger.Verify(m => m.LogWarning($"comments not found for part number :{0}", "111"), Times.Exactly(1));
This is happening because NUnit mocking framework does not support extension methods. A few people on stack overflow have suggested to use Log method instead of level wise methods.
What am I missing?
Firstly, you don't need the $ at the start of the string. That's for string interpolation. The LogWarning message is doing a string.format, hence the {0}
Mock frameworks cannot directly mock static methods. The problem in your case is the LogWarning method - that is the static (extension) method.
The simplest way of overcoming this issue is by using a wrapper class. Here's how I got it, in your case.
Firstly I created an interface
public interface IMyLogWarning
{
void LogWarning(string msg, params object[] args);
}
Then I created a class which implements that interface
public class MyLogWarning<T> : IMyLogWarning where T : class
{
private readonly ILogger _logger;
public MyLogWarning(ILogger<T> logger)
{
// Using constructor for DI
_logger = logger;
}
public void LogWarning(string msg, params object[] args)
{
_logger.LogWarning(msg, args);
}
}
The reason for these two is that I'll use these in my code as well as the unit test.
The constructor in the class is setup so it can be populated using dependency injection, something like this in your ConfigureServices method. Feel free to change this; was a quick stab at it on my part.
services.AddTransient<IMyLogWarning, MyLogWarning<MyViewModel>>();
You can then create a unit test that's roughly like this
[Test]
public void LoggingTest_LogAMessage_ConfirmedLogWasRun()
{
// TODO - add the rest of your test code
// Arrange
var warningMsg = "comments not found for part number :{0}";
var partNumber = "111";
var mockLogger = new Mock<IMyLogWarning>();
// Act
mockLogger.Object.LogWarning(warningMsg, partNumber);
// Assert
mockLogger.Verify(m => m.LogWarning(warningMsg, partNumber), Times.Exactly(1));
}
(This is difficult to search because results are all about "method reference")
I want to get a Method instance for a lambda expression for use with a legacy reflection-based API. The clousure should be included, so calling thatMethod.invoke(null, ...) should have the same effect as calling the lambda.
I have looked at MethodHandles.Lookup, but it only seems to be relevant for the reverse transform. But I guess the bind method may help to include the clousure?
Edit:
Say I have am lambda experssion:
Function<String, String> sayHello = name -> "Hello, " + name;
and I have a legacy framework (SpEL) that has an API like
registerFunction(String name, Method method)
which will call the given Method with no this argument (i.e. Method assumed to be static). So I'll need to get a special Method instance that includes the lambda logic + the clousure data.
In case you don't find an elegant way, here is the ugly way (Ideone). Usual warning when reflection is involved: may break in future releases etc.
public static void main(String[] args) throws Exception {
Function<String, String> sayHello = name -> "Hello, " + name;
Method m = getMethodFromLambda(sayHello);
registerFunction("World", m);
}
static void registerFunction(String name, Method method) throws Exception {
String result = (String) method.invoke(null, name);
System.out.println("result = " + result);
}
private static Method getMethodFromLambda(Function<String, String> lambda) throws Exception {
Constructor<?> c = Method.class.getDeclaredConstructors()[0];
c.setAccessible(true);
Method m = (Method) c.newInstance(null, null, null, null, null, 0, 0, null, null, null, null);
m.setAccessible(true); //sets override field to true
//m.methodAccessor = new LambdaAccessor(...)
Field ma = Method.class.getDeclaredField("methodAccessor");
ma.setAccessible(true);
ma.set(m, new LambdaAccessor(array -> lambda.apply((String) array[0])));
return m;
}
static class LambdaAccessor implements MethodAccessor {
private final Function<Object[], Object> lambda;
public LambdaAccessor(Function<Object[], Object> lambda) {
this.lambda = lambda;
}
#Override public Object invoke(Object o, Object[] os) {
return lambda.apply(os);
}
}
Well, lambda expressions are desugared into methods during compilation and as long as they don’t capture this (don’t access non-static members), these methods will be static. The tricky part is to get to these methods as there is no inspectable connection between the functional interface instance and its target method.
To illustrate this, here the simplest case:
public class LambdaToMethod {
public static void legacyCaller(Object arg, Method m) {
System.out.println("calling Method \""+m.getName()+"\" reflectively");
try {
m.invoke(null, arg);
} catch(ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws URISyntaxException
{
Consumer<String> consumer=s -> System.out.println("lambda called with "+s);
for(Method m: LambdaToMethod.class.getDeclaredMethods())
if(m.isSynthetic() && m.getName().contains("lambda")) {
legacyCaller("a string", m);
break;
}
}
}
This works smoothly as there is only one lambda expression and hence, one candidate method. The name of that method is compiler specific and may contain some serial numbers or hash codes, etc.
On kludge is to make the lambda expression serializable and inspect its serialized form:
static Method lambdaToMethod(Serializable lambda) {
for(Class<?> cl=lambda.getClass(); cl!=null; cl=cl.getSuperclass()) try {
Method m=cl.getDeclaredMethod("writeReplace");
m.setAccessible(true);
try {
SerializedLambda sl=(SerializedLambda)m.invoke(lambda);
return LambdaToMethod.class.getDeclaredMethod(sl.getImplMethodName(),
MethodType.fromMethodDescriptorString(sl.getImplMethodSignature(),
LambdaToMethod.class.getClassLoader()).parameterArray());
} catch(ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
} catch(NoSuchMethodException ex){}
throw new AssertionError();
}
public static void main(String[] args)
{
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("first lambda called with "+s)));
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("second lambda called with "+s)));
}
This works, however, serializable lambdas come at a high price.
The simplest solution would be to add an annotation to a parameter of the lambda expression to be found when iterating over the methods, however, currently, javac doesn’t store the annotation properly, see also this question about this topic.
But you may also consider just creating ordinary static methods holding the code instead of a lambda expression. Getting a Method object for a method is straight-forward and you still can create a functional interface instance out of them using method references…
Since the question mentions SpEL specifically (and I found the question when also working with SpEL), an alternative way to add a custom function to the evaluation context without using Method references is to add a custom MethodResolver (javadoc, GitHub) to the StandardEvaluationContext. A benefit of this approach is that one can add both static and non-static methods to the evaluation context using it, where only static methods could be added using the registerFunction approach.
The code to add a custom MethodResolver to the StandardEvaluationContext is fairly straightforward. Below is an executable example showing how to do so:
public static void main(String[] args) throws Exception {
Function<String, String> sayHello = name -> "Hello, " + name;
// The evaluation context must have a root object, which can be set in the StandardEvaluationContext
// constructor or in the getValue method of the Expression class. Without a root object, the custom
// MethodResolver will not be called to resolve the function.
Object rootObject = new Object();
StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext(rootObject);
// Add the custom MethodResolver to the evaluation context that will return a MethodExecutor that
// Spring can use to execute the sayHello function when an expression contains "sayHello('<any string>')".
standardEvaluationContext.addMethodResolver((context, targetObject, methodName, argumentTypes) -> {
MethodExecutor methodExecutor = null;
if (methodName.equals("sayHello")
&& argumentTypes.size() == 1
&& String.class.isAssignableFrom(argumentTypes.get(0).getObjectType())
) {
methodExecutor = (innerContext, target, arguments) -> {
final String name = arguments[0].toString();
return new TypedValue(sayHello.apply(name));
};
}
return methodExecutor;
});
// Create an expression parser, parser the expression, and get the evaluated value of the expression.
SpelExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("sayHello('World!')");
String expressionValue = expression.getValue(standardEvaluationContext, String.class);
// Output the expression value, "Hello, World!", to the console.
System.out.println(expressionValue);
}
The value of the expression that was output to the console by executing the above code was:
Hello, World!
Note that when using a MethodResolver to add a function to the evaluation conext, the function should not be prefixed with a # in the expression string. This is a major difference between using the MethodResolver and using the registerFunction method to add a function to the evaluation context.
sayHello('World!') // will work!
#sayHello('World!') // will not work!
Keep this in mind if you are considering migrating an existing solution from using the registerFunction approach to using the MethodResolver approach.
I'm trying to write unit test for my saga. I have used SagaFixture to host my Saga and using FakeBus. I have also used FakeMessageContext because, in a Saga exectution I'm using MessageContext to get return address.
Now, when I'm trying to call Handle method on Fixture, it throws exception of type NullReferenceException.
Pasting code below:
SearchSaga
public class SearchSaga : Saga<SearchSagaData>, IAmInitiatedBy<PersonRequested>, IHandleMessages<PersonSearchCompleted>
{
public void Handle(PersonRequested message)
{
Data.Id = new Guid(message.MessageId);
Data.ReturnAddress = MessageContext.GetCurrent().ReturnAddress;
Bus.Publish(message);
}
}
SagaUnitTest
[Test]
public void PublishResponseOfTypeSearchPersonRequest()
{
var bus = new FakeBus();
var saga = new SearchSaga(bus);
var fixture = new SagaFixture<SearchSagaData>(saga);
FakeMessageContext.Reset();
var fakeContext = MockRepository.GenerateMock<IMessageContext>();
fakeContext.Stub(s => s.ReturnAddress).Return("queuename");
fakeContext.Stub(s => s.Headers).Return(new Dictionary<string, object>());
// act
using (FakeMessageContext.Establish(fakeContext))
{
fixture.Handle(new PersonRequested {MessageId = Guid.NewGuid().ToString(), Query = "Abc"});
}
var sentRequests = bus.PublishedMessages.OfType<SearchPersonRequest>().ToList();
Assert.That(sentRequests.Count, Is.EqualTo(1));
}
Error Stacktrace:
at Rebus.SagaContext..ctor(Guid id)
at Rebus.Bus.Dispatcher.DispatchToHandler[TMessage](TMessage message, IHandleMessages1 handler) at Rebus.Testing.SagaFixture1.Handle[TMessage](TMessage message)
The exception is caused by the fact that your IMessageContext mock has a null value on the Items property. The thrown exception is just pretty bad, but I'll make sure that the reported error gets better in the future.
For now, you can fix the situation by setting up a an items dictionary like so:
fakeContext.Stub(s => s.Items).Return(new Dictionary<string, object>());
Moreover, instead of using MessageContext.GetCurrent() to get to the message context inside your handler, you should take advantage of the fact that all Rebus' IoC container adapters ensure that you can have an IMessageContext injected into all of your handler instances.
This way, there's no need to use FakeMessageContext in your unit tests.
Would like to try using AsyncCTP with TFS. Currently have a long running method that calls RunQuery on a TFS Query instance.
Query exposes the APM methods BeginQuery() and EndQuery(). As I understand it, the recommended approach to wrap these using AsyncCTP is something like: (example from docs)
Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, offset, count, null);
Further, have wrapped it in an extension method as in the docs so my actual method looks like:
public static Task<WorkItemCollection> RunQueryAsync(this Query query)
{
if (query== null)
throw new ArgumentNullException("Query");
return Task<WorkItemCollection>.Factory.FromAsync(query.BeginQuery, query.EndQuery, null);
}
...but this fails to compile. Getting an "invalid argument" intellisense error that, frankly, I can't really understand because the types and format look correct. One possible issue might be that the Query APM methods expect an ICanceleableAsyncResult whereas the Task factory is expecting an IAsyncResult -- but looking at the TFS API, ICanceleableAsyncResult is a specialization of IAsyncResult.
Not sure whether i'm doing it wrong or its just not possible. Would love to be able to do it the AsyncCTP way but may have to go back to the APM pattern -- ugh!
Update: My Nito.AsyncEx library now includes a TeamFoundationClientAsyncFactory type, which can be used instead of rolling your own implementation below.
The TFS API is not strictly following the APM pattern because it does not take a state parameter, and this is preventing the built-in TaskFactory.FromAsync from working.
You'll have to write your own FromAsync equivalent, which can be done using TaskCompletionSource:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
public static class TfsUtils<TResult>
{
public static Task<TResult> FromTfsApm(Func<AsyncCallback, ICancelableAsyncResult> beginMethod, Func<ICancelableAsyncResult, TResult> endMethod, CancellationToken token)
{
// Represent the asynchronous operation by a manually-controlled task.
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
try
{
// Begin the TFS asynchronous operation.
var asyncResult = beginMethod(Callback(endMethod, tcs));
// If our CancellationToken is signalled, cancel the TFS operation.
token.Register(asyncResult.Cancel, false);
}
catch (Exception ex)
{
// If there is any error starting the TFS operation, pass it to the task.
tcs.TrySetException(ex);
}
// Return the manually-controlled task.
return tcs.Task;
}
private static AsyncCallback Callback(Func<ICancelableAsyncResult, TResult> endMethod, TaskCompletionSource<TResult> tcs)
{
// This delegate will be invoked when the TFS operation completes.
return asyncResult =>
{
var cancelableAsyncResult = (ICancelableAsyncResult)asyncResult;
// First check if we were canceled, and cancel our task if we were.
if (cancelableAsyncResult.IsCanceled)
tcs.TrySetCanceled();
else
{
try
{
// Call the TFS End* method to get the result, and place it in the task.
tcs.TrySetResult(endMethod(cancelableAsyncResult));
}
catch (Exception ex)
{
// Place the TFS operation error in the task.
tcs.TrySetException(ex);
}
}
};
}
}
You can then use it in extension methods as such:
using System.Threading;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
public static class TfsExtensions
{
public static Task<WorkItemCollection> QueryAsync(this Query query, CancellationToken token = new CancellationToken())
{
return TfsUtils<WorkItemCollection>.FromTfsApm(query.BeginQuery, query.EndQuery, token);
}
}