I'm using Visual Studio Code to develop an Azure Durable Function, following some online examples. I'm completely new to Azure and adapting to C# from VB.NET. I already developed a working durable function that stores data in a blob table. This is a slight adaptation of that which aims to put the information into a queue.
I'm following the instructions here, and have code that I think should work, except that it tells me this:
Cannot apply attribute class 'Queue<T>' because it is generic
However I've looked at some other examples, including the one from Microsoft, and don't anything special being done for typing of queue. Clearly, though, I'm missing something.
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
namespace Test.Groove
{
public static class GrooveWebhook
{
[FunctionName("GrooveWebhook")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
GrooveItem data = context.GetInput<GrooveItem>();
string res = await context.CallActivityAsync<string>("AddToQueue", data);
return outputs;
}
[FunctionName("AddToQueue")]
public static async Task<String> Run(
[ActivityTrigger] GrooveItem trans,
[Queue("incoming-groove-webhooks")] IAsyncCollector<GrooveItem> GrooveData,
ILogger log)
{
await GrooveData.AddAsync (trans);
return $"Added Groove transaction for {trans.firstname} {trans.lastname}: {trans.email}";
}
[FunctionName("GrooveWebhook_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
var data = await req.Content.ReadAsAsync<GrooveItem>();
string instanceId = await starter.StartNewAsync("GrooveWebhook", data);
log.LogInformation($"Started Groove webhook orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
}
Now, here's the really weird part. I followed along literally step by step with their http trigger example, code as follows, and yet don't have that same error:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace My.Functions
{
public static class HttpExample
{
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
So then, as if that wasn't weird enough, I went back to my original project that added data to a table, and just added this little queue binding statement, and I don't have the error! This is so crazy. Code begins:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
namespace Company.Function
{
public static class GrooveOrchestrationTest
{
[FunctionName("GrooveOrchestrationTest")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context,
[Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData,
[Queue ("GrooveQueue")] IAsyncCollector<GrooveItem> GrooveQueue)
There are no additional using directives in either of the latter solutions that successfully bind the queue for output.
One Additional Thought
I created another version from scratch with the exact same problem, even though my other one is still fine. I have no explanation for this. However, I will note that they are connected to DIFFERENT Azure accounts. Both are on the free tier, both just signed up in the last couple of days. I can't fathom why that should matter, but it's all I can think of.
This problem is caused by the following reference added automatically by VS Code into the .csproj file:
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
This is confirmed by changing it to match the reference from the other solution where the exact same code works perfectly:
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.3" />
With this change in reference, it works as expected.
What lead me to this is the fact that my queue binding worked fine when pasted into my older solution from just a few days ago, but the exact same code didn't work in the new solution. To ensure all things were equal, I pasted the entire contents of the .cs file so there would be no difference. I.e. the old solution and the new one had identical .cs files, but one worked and one didn't.
Since my code was identical in both solutions, it seemed clear that there must be something that VS Code had done behind the scenes. I did a stare-and-compare on all its automatically generated files between the old solution and the new one. I discovered that this one reference is literally the only difference between the two solutions, and when I change it to the older version, things work beautifully.
I'll reference the older package for now so I can get this done, and then when I get some free time figure out how to make the new SDK.Functions happy.
You might be lacking reference to Microsoft.Azure.WebJobs.Extensions.Storage.
Related
I have an issue with my http trigger in azure functions. When I use the http trigger local the trigger is correctly getting the picture from the online azure storage container. Once azure function is deployed it no longer works.
Here is my code for the http trigger that work locally but not once deployed:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Azure.Storage.Blobs;
namespace PlaygroundAzureFunctions
{
public static class HttpFromStorage
{
[FunctionName("GetSnapsnot")]
public static async Task<IActionResult> GetSnapsnot(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "Snapshot")] HttpRequest req,
ILogger log)
{
log.LogInformation($"Snapsnot requested at: {DateTime.Now}");
string Connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
string containerName = Environment.GetEnvironmentVariable("ContainerName");
var blobClient = new BlobContainerClient(Connection, containerName);
var blob = blobClient.GetBlobClient("TestPicture.jpeg");
var image = await blob.OpenReadAsync();
log.LogInformation($"Snapsnot request returned at: {DateTime.Now}");
return new OkObjectResult(image);
}
}
}
Here is my local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "StringTakenFromCorrectStorageAccessKeysJustHiddenHere",
"ContainerName": "file-upload", // container name
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
I was aiming for once deployed i could use the public to the internet app and use its url to call the api to trigger that specific picture to be shown.
The errors in the function monitor is as follows:
local.settings.json is used only when you run locally and when the function is deployed the settings are missing.
You generally don't want to store your production secrets in your git repository and you should set them separately (as part of a deployment pipeline) but if this is for testing or if the resources are publicly available you can just put the settings in
settings.json (no local).
You can also go to portal.azure.com->Your_App->Configuration=>Application Settings in Azure and set ContainerName directly there.
I'm in the middle of migrating from .NET Core 3.1 to .NET 5.0 for my Azure Functions project. I have the following function decleration:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
[Function("Test")]
public static async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestMessage req,
ILogger log,
Microsoft.Azure.WebJobs.ExecutionContext context)
{
...
}
However, I receive the following error message:
System.Private.CoreLib: Exception while executing function: Functions.SaveBlob. System.Private.CoreLib: Result: Failure
Exception: Microsoft.Azure.Functions.Worker.Diagnostics.Exceptions.FunctionInputConverterException: Error converting 1 input parameters for Function 'Test': Cannot convert input parameter 'req' to type 'System.Net.Http.HttpRequestMessage' from type 'Microsoft.Azure.Functions.Worker.GrpcHttpRequestData'.
I need to keep the ExecutionContext for my application.
How can I fix this error?
After reading the Microsoft Documentation about the Execution Context, I see the class was renamed to FunctionContext
The fix to the issue was simple, rename ExecutionContext to FunctionContext. I realize that the Microsoft.Azure.WebJobs is not needed for .NET 5 Azure Functions. Next, I needed to change the type from System.Net.Http.HttpRequestMessage to Microsoft.Azure.Functions.Worker.Http.HttpRequestData. Lastly, I removed the ILogger in favor of the GetLogger Method.
The final function looks like this.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
[Function("Test")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req
FunctionContext context)
{
...
}
When upgrading, be sure to thoroughly read all their documentation about the new changes. It also helped to create a new project through Visual Studio Code using .NET 5 to see how a new project is structured.
I'm new in web api and I wrote a code in which I return a datatable in an IHttpActionResult and I want to know what is the difference between return a datatable and a list or dictionary.
this is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebApplication2.BLL;
namespace WebApplication2.Controllers
{
public class HomeController : ApiController
{
public IHttpActionResult getpro()
{
var tabla= new Conexion().table();
tabla.TableName = "tablilla";
return Ok(tabla);
}
}
}
I am testing this in post man and is runing well, but I want to know if should I sent a list in the response or another object.
There might not be an apparent difference right now, but as your application grows, you might run into maintainability issues.
The issue is that right now, the signature of your API endpoint (i.e. what data it returns) is hard coupled to your database, so if you change your database, so does the result returned by your endpoint. That's usually a bad thing for anything but the smallest/simplest applications, since it might break consumers of your API.
What you'd do instead, is create models specifically for returning from your API endpoints and then convert (also called map) your datatable to a list of these "view"-models. This way, you can change the backend of your web API without it (immediately) having an impact on your frontend (or other consumers of your API).
I have added a web reference (WSDL) to a class library and then referenced that dll in a timer-triggered c# Azure function (read more about azure functions). The class library has a class EmployeeService which calls a method from the web service (sort of a webservice wrapper). When I call the class lib method (GetEmployees) from a console application, it authenticates to the web service and returns result but when I run the azure function for the same code and creds it returns 401. Not sure what I am doing wrong here :
#r "MyConsult.Service.dll"
#r "Newtonsoft.Json.dll"
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Newtonsoft.Json;
using System.Net;
using MyConsult.Service.Service;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
try
{
EmployeeService _empService = new EmployeeService();
var emps = _empService.GetEmployees();
int count = emps.Where(x => !string.IsNullOrEmpty(x.Email)).Select(x => x.Email).Distinct().Count();
log.Info($"employee count : {count}");
}
catch (Exception ex)
{
log.Info($"Exception Message: {ex.Message}");
log.Info($"Exception Stack Trace: { ex.StackTrace}");
}
}
Your console application is likely authenticating based on service configuration settings in your app.config. For your function, you'll need to programmatically apply those settings when constructing the client/proxy.
There is an application which is used for taking surveys. I want to use REST api and add some questions to the survey. So basically I want to update some pages of the survey. Here are the links of that application.
My surveys name is trial and the page number that I want to update is 2.
fields#updateobject
Here is my code to do. But i think the http call I have used is not correct. and I am not able to figure out how to make the request.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
namespace ConsoleApplication1
{
class Program
{
private static object post;
private static object response;
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
string webReq = "https://restapi.survey.com/v4/survey/trial/surveypage/2?_method=POST?api_token="/*token goes here*/;
// $.post{webReq};
}
}
}
Use System.Net.Http.HttpClient class for sending requests to any API. Tutorial and examples on sending various requests (GET, POST, etc.) can be found here.