I am new to aws lambda functions with c#. I have two lambda functions and I want to call one lambda function from second function, I am using code as below:
public string Function1(JObject input)
{
string param = input["param"].ToString();
string param1 = input["param1"].ToString();
return param.ToUpper()+" "+param1.ToUpper();
}
public string Function2()
{
try
{
using (AmazonLambdaClient client = new AmazonLambdaClient(some region))
{
JObject ob = new JObject();
ob.Add("param", "hello");
ob.Add("param1", "Lambda");
var request = new InvokeRequest
{
FunctionName = "Function1",
Payload = ob.ToString()
};
var response = client.Invoke(request);
string result;
using (var sr = new StreamReader(response.Payload))
{
return result = sr.ReadToEnd();
}
}
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
And I am getting an error as below:
{
"errorType": "TypeLoadException",
"errorMessage": "Could not load type 'System.Net.HttpStatusCode' from assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes'.",
"stackTrace": [
"at AWSLambdaApp.Function.Function2()",
"at lambda_method(Closure , Stream , Stream , ContextInfo )"
]
}
And in function 2 application I have added a reference of "AWSSDK.Core" and "AWSSDK.Lambda" dlls.
Can any one tell me what I am doing wrong?
I don't have 'netcoreapp1.0' installed on my PC. So I have tried on 'netcoreapp2.0'.
And my code looks like:
public string FunctionHandler(JObject input, ILambdaContext context)
{
param = input["param"].Value<string>();
param1 = input["param1"].Value<string>();
param.ToUpper() + " " + param1.ToUpper();
}
and for Function2:
public async Task<string> FunctionHandler(string input, ILambdaContext context)
{
try
{
using (AmazonLambdaClient client = new AmazonLambdaClient(RegionEndpoint.USEast1))
{
JObject ob = new JObject { { "param", "hello" }, { "param1", "Lambda" } };
var request = new InvokeRequest
{
FunctionName = "Function1",//Function1:v1 if you use alias
Payload = ob.ToString()
};
var response = await client.InvokeAsync(request);
using (var sr = new StreamReader(response.Payload))
{
return await sr.ReadToEndAsync();
}
}
}
catch (Exception ex)
{
return ex.Message;
}
}
And the result of execution is "\"HELLO LAMBDA\"".
The reason for work of my code could be:
How I get parameter from input in Function1. It is most probably.
My async code. I can't use the synchronous code for call lambda with the latest SDK.
The version of .net core.
Also, I need send a simple string as the parameter of Function2.
I think you have to rethink your architecture. Keep in mind that the lambda function has limitations (time of execution & CPU usage).
It is better to use aws step function for such cases. In this case, you could use the Chain of Responsibility pattern. Lambda functions will be decoupled, and it is easier to support them. You will have something like as a result:
Moreover, aws step-functions has a lot of features like condition, parallel execution, waiters so on. It gives you powerful tools for improving your application.
Related
I'm working on a c# application and using AWS lambda functions in the backend. The lambda function is working correctly and i'm able to call it from the application. The part I'm having trouble with is getting the code to wait for the result from the lambda function to be returned before continuing. I've looked into using the async/await pattern but i'm getting compile errors because AmazonLambda.InvokeAsync returns null.
This is the code what is correctly invoking the function and prints out the response but I'd like to instead return the response to the calling method. I've also tried changing the return from void to string and adding a return to the callback function but I get this error: "Anonymous function converted to a void returning delegate cannot return a value"
Any help is appreciated.
public void Invoke() {
InvokeRequest invokeRequest = new InvokeRequest() {
FunctionName = FunctionName,
Payload = Payload
};
Client.InvokeAsync(invokeRequest, responseObject => {
if (responseObject.Exception == null) {
Debug.Log("LAMBDA SUCCESS: " + Encoding.ASCII.GetString(responseObject.Response.Payload.ToArray()));
} else {
Debug.Log("LAMBDA ERR: " + responseObject.Exception);
}
});
}
Try...
var result = await Client.InvokeAsync(...
or
var result = Client.InvokeAsync(...).Result;
Try
public async Task Invoke() {
InvokeRequest invokeRequest = new InvokeRequest() {
FunctionName = FunctionName,
Payload = Payload
};
await Client.InvokeAsync(invokeRequest, responseObject => {
if (responseObject.Exception == null) {
Debug.Log("LAMBDA SUCCESS: " + Encoding.ASCII.GetString(responseObject.Response.Payload.ToArray()));
} else {
Debug.Log("LAMBDA ERR: " + responseObject.Exception);
}
});
}
AmazonLambdaClient.InvokeAsync function have multiple overloads. The one which you are using accepts two parameters -
InvokeRequest request
AmazonServiceCallback
The second parameter is a callback, think of it as an event(just like a click event of a button). Which means it is called when an event has happened unlike async/await of C# which return values to caller.
This is an asynchronous operation using the standard naming convention for .NET 4.5 or higher. For .NET 3.5 the operation is implemented as a pair of methods using the standard naming convention of BeginInvoke and EndInvoke. For Unity the operation does not take CancellationToken as a parameter, and instead takes AmazonServiceCallback and AsyncOptions as additional parameters.
Here is the link to official documentation AmazonLambdaClient.InvokeAsync
To solve your problem -
public void Invoke()
{
InvokeRequest invokeRequest = new InvokeRequest
{
FunctionName = FunctionName,
Payload = Payload
};
Client.InvokeAsync(invokeRequest, responseObject =>
{
if (responseObject.Exception == null)
{
Debug.Log("LAMBDA SUCCESS: " + Encoding.ASCII.GetString(responseObject.Response.Payload.ToArray()));
// Call function on success and pass in the returned value
}
else
{
Debug.Log("LAMBDA ERR: " + responseObject.Exception);
// Call function on failure and pass exception data
}
});
}
In the above code you can explicitly call methods for success/failure and pass in corresponding parameters. So whatever code you were planning to write after getting result from your Invoke function, write that in success/failure methods instead.
You can also create a separate callback function and use it instead of writing everything in Invoke function.
I am new to TAP and async/await in practice in C# so I may have some bad code smells here, so be gentle. :-)
I have a service method that looks as follows:
public OzCpAddOrUpdateEmailAddressToListOutput AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Task<Member> mailChimpResult =
mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
//Poll async task until it completes.
//Give it at most 8 seconds to do what it needs to do
var outOfTime = DateTime.Now.AddSeconds(8);
while (!mailChimpResult.IsCompleted)
{
if (DateTime.Now > outOfTime)
{
throw new Exception("Timed out waiting for MailChimp API.");
}
}
//Should there have been a problem with the call then we raise an exception
if (mailChimpResult.IsFaulted)
{
throw new Exception(
mailChimpResult.Exception?.Message ??
"Unknown mail chimp library error.",
mailChimpResult.Exception);
}
else
{
//Call to api returned without failing but unless we have
//the email address subscribed we have an issue
if (mailChimpResult.Result.Status != Status.Subscribed)
{
throw new Exception(
$"There was a problem subscribing the email address
{aParams.EmailAddress} to the mailchimp list id
{aParams.Listid}");
}
}
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
But when I call in from MVC Controller action mailChimpResult.IsCompleted always returns false and eventually I hit the timeout.
I realise this is because I am not chaining the async calls as per HttpClient IsComplete always return false and because of different threads this behaviour is "expected".
However I want my service method to hide the complexity of the async nature of what it is doing and merely do what appears to be a synchronous call in my action method namely:
var mailChimpResult =
_PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
Ideally you should avoid the .Result and .IsFaulted properties of the Task and Task<T> objects, that was code smell number one. When you're using these objects you should use async and await through the entire stack. Consider your service written this way instead:
public async Task<OzCpAddOrUpdateEmailAddressToListOutput>
AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Member mailChimpResult =
await mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
Notice that I was able to remove all of that unnecessary polling and examining of properties. We mark the method as Task<OzCpAddOrUpdateEmailAddressToListOutput> returning and decorate it with the async keyword. This allows us to use the await keyword in the method body. We await the .AddOrUpdateAsync which yields the Member.
The consuming call into the service looks similar, following the same paradigm of async and await keywords with Task or Task<T> return types:
var mailChimpResult =
await _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
It is considered best practice to postfix the "Async" word to the method, to signify that it is asynchronous, i.e.; AddOrUpdateEmailAddressToListAsync.
I am working out how best to consume my C# dlls using Edgejs for Node.
One proxy function in Node looks like this (a class method in Typescript):
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
When my complex task in c# is synchronous, everything works as expected, but when I move the core of the c# function into a task:
return await Task.Run<object>( () => { do some stuff });
When I use the above pattern, the resolve(result) line is hit, the result is correct in the watch window, but any .then or Q.all structures that are composed do not respond to resolve(result) having been executed.
I have found that if I console.log("x"); before the proxy callback returns, then my composed .then and Q.all structures fire as expected. i.e. this version works:
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
console.log("GLReportProxy.readSettings proxy returns, Err = " + (!!error));
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
My c# routine in this case reads an xml file, deserializes it and returns it:
public Task<object> readSettings(object args)
{
if (args != null && typeof(System.Dynamic.ExpandoObject).IsAssignableFrom(args.GetType()))
{
var argdict = (IDictionary<string, object>)args;
if (argdict.ContainsKey("report"))
{
reportsettingsfile = argdict["report"].ToString();
}
return Task.Run<object>(
() => {
if (File.Exists(reportsettingsfile))
{
var xser = new XmlSerializer(typeof(ReportSettings.report));
string settingstext = File.ReadAllText(reportsettingsfile);
using (var tre = new StringReader(settingstext))
{
reportSettings = (ReportSettings.report)xser.Deserialize(tre);
}
return new { result = true, model = reportSettings };
}
else
{
return new { result = false, error = "File not found" };
}
});
} else
{
return Task.FromResult<object>(new { result = false, error = "Expected (input) object but can't read it." });
}
}
This is fairly simple. I figure that I've got problems in my use of async/await, that causes problems in Node. To be fair though, I had expected promises to be robust. There could be issues with my use of Typescript, Node, Edge, Promises, Q.
If anybody knows what's happening, and why logging to the console fixes the problem. I'd appreciate any help!
Mark
I found that this is an issue when there is a http.listener and a c# edge proxy callback in the same process. There could be other combinations, but some interplay between libraries is the root cause.
Since this issue is unresolved according to edgejs, the console.log method is a good workaround, particularly if you don't mind writing some helpful message to the log window.
console.log is a good workaround, but I wanted something silent. I notice that console internally has _stdout. If you execute console._stdout.write(''); you get a silent unlock. in Typescript you have to do (console as any)._stdout.write('');.
I do this on entry to the edge proxy callback. e.g.
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
(console as any)._stdout.write('');
//console.log("GLReportProxy.readSettings proxy returns, Err = " + (!!error));
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
Clearly you could log something as indicated in the comment.
Using RestSharp I'm building an API to perform CRUD operations given a datatype/object.
My CrudAbstract class is generic and has the following:
public virtual async Task<keyType> Post(dto item)
{
try
{
var request = await _client.GetRequestAsync(_path);
request.Method = RestSharp.Method.POST;
request.AddJsonBody(item);
var result = await _client.ExecuteRequestAsync<keyType>(request);
return result;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
throw new Exception("Was not able to process crud post operation.");
}
My WebClient class has the following:
Entities = new CrudAbstract<DtoEntity, int>("/entities", this); // in constructor
// So the keyType from definition above is int (Int32)
The post method in this class is
public async Task<T> ExecuteRequestAsync<T>(IRestRequest request)
{
try
{
var response = await GetClient().ExecuteTaskAsync<T>(request);
// Exception occurs here. The above statement is unable to finish.
var data = response.Data;
return data;
}
catch (Exception e)
{
// Log exception
}
throw new Exception("Was not able to process restclient execute async method.");
}
My Api EntitiesController has the following:
public int Post(DtoEntity value)
{
using (var db = // some database...)
{
try
{
// Upsert object into database here
//... value.Id no longer null at this point
/*
The problem occurs here. I only want to return the ID of the object
(value.Id). I do not want to return the whole object (value)
*/
return value.Id;
}
catch (Exception e)
{
// Log exception
}
}
throw new Exception("Was not able to process entities post method.");
}
The exception I get is:
Unable to cast object of type 'System.Int64' to type
'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
This is basically saying it is unable to cast the object int (which I have returned in the post with value.Id) to a DtoEntity object (which is the actual object on which CRUD operations were performed).
What am I doing wrong?
I have placed typeof and .getType() onto each keyType, T, and value.Id, and all of them are Int32. Is it in the RestSharp library that the problem occurs? At what stage is there a casting of int to a DtoEntity in the line:
var response = await GetClient().ExecuteTaskAsync<T>(request);
Note: when I change the return type of the post method in my controller to DtoEntity and change the value.Id to just value it works. The response is received, and the response.Data is the DtoEntity object.
I've seen similar questions here but not found a solution yet.
I believe you've spotted a bug in RestSharp. RestSharp internally uses a JSON parser called SimpleJson (borrowed from the Facebook .NET SDK). It appears that this parser is correctly deserializing the response body to a number (since JSON is untyped it uses Int64 to be safe), but the RestSharp's JsonDeserializer class attempts to cast this result to an IDictionary in the first line of this method:
private object FindRoot(string content)
{
var data = (IDictionary<string, object>)SimpleJson.DeserializeObject(content);
if (RootElement.HasValue() && data.ContainsKey(RootElement))
{
return data[RootElement];
}
return data;
}
I think your options are:
File an issue with the RestSharp team.
Get the raw string body and cast it to an int yourself (may be the path of least resistance).
Try a different library.
I am trying to parse data from a json file but I am getting variable outputs(Sometimes right, othertimes nothing). I am pretty much sure it is related with the time needed to parse the file, but having trouble finding out where. Here it is-
public class HspitalVM
{
List<Hspital> hspitalList=null;
public List<KeyedList<string, Hspital>> GroupedHospitals
{
get
{
getJson();
var groupedHospital =
from hspital in hspitalList
group hspital by hspital.Type into hspitalByType
select new KeyedList<string, Hspital>(hspitalByType);
return new List<KeyedList<string, Hspital>>(groupedHospital);
}
}
public async void getJson()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
try
{
StorageFile textFile = await localFolder.GetFileAsync(m_HospFileName);
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
using (DataReader textReader = new DataReader(textStream))
{
uint textLength = (uint)textStream.Size;
await textReader.LoadAsync(textLength);
string jsonContents = textReader.ReadString(textLength);
hspitalList = JsonConvert.DeserializeObject<IList<Hspital>>(jsonContents) as List<Hspital>;
}
}
}
catch (Exception ex)
{
string err = "Exception: " + ex.Message;
MessageBox.Show(err);
}
}
}
You are not await-ing result of getJson() call, so as expected most of the times you'll get no information because actual call to GetFileAsync is not completed yet.
Now since getJson method returns void you can't really await on it. Potential fix by using Result to turn asynchronous code into synchronous for get:
public List<KeyedList<string, Hspital>> GroupedHospitals
{
get
{
hspitalList= getJson().Result;
...
}
}
...
public async Task<IList<Hspital>> getJson()
{
....
return JsonConvert.DeserializeObject<IList<Hspital>>(jsonContents) as List<Hspital>;
}
Note: It is generally bad idea to make get methods to perform long operations, also calling async method in synchronous way via Wait/Result can cause deadlocks in your code -await vs Task.Wait - Deadlock?.