I have a simple webjob hosted on azure as a continuously running job.
I need to pass token value as a input , how can I achieve this using continuous web job?
static void Main(string[] args)
{
try
{
Console.WriteLine("Goto Login url" + kite.GetLoginURL());
Console.WriteLine("Enter request token: ");
string requestToken = Console.ReadLine();
I don't think it's possible using webjobs (continuos job) unless you have some web server running where you can fire requests against (e.g. sinatra, aspnet core, etc). You need to use Azure Functions with HTTP Trigger. Then you can pass the token in the querystring or in the body of the request and do what you need to do.
Webjob is background service, so you can not take user input using Console.ReadLine() anyway. Since WebJob does not have HTTP trigger like Azure Function, I see the only alternate way to pass message/input to a WebJob is to make it event-driven like using a queue trigger and do processing on receiving the queue message. Refer this quick-start guide for details.
Below uses .net core 3.1 with web job sdk v3. For equivalent code with older sdk, refer this.
Queue triggered job:
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
namespace WebJobsSDKSample
{
public class Functions
{
public static void ProcessQueueMessage([QueueTrigger("queue")] string message, ILogger logger)
{
// do stuffs with message
logger.LogInformation(message);
}
}
}
Program.cs
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
namespace WebJobsSDKSample
{
public class Program
{
static async Task Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
}
}
You can also explore some old samples here.But these are using v1 SDK.
Related
I have implemented Hangfire in .NET Console Application and It's working fine. After submitting Tasks(In my case downloading documents) to Hangfire I need to update the status of the Task(Document download status) in the Database, for this, I have the "UpdateDocumentStatus();" method but it never executed (Executing when entering something in the Console cmd window) as I have Console.ReadKey(); in the program. If I remove Console.ReadKey(); Hangfire not working.
Please suggest me alternative to Console.ReadKey(); for Hangfire to execute my "UpdateDocumentStatus();"
using System;
using Hangfire;
using Hangfire.MemoryStorage;
namespace HangFire
{
class Program
{
static void Main(string[] args)
{
GlobalConfiguration.Configuration.UseMemoryStorage();
BackgroundJob.Enqueue(() => DownloadDocument());
using (var server = new BackgroundJobServer())
{
Console.ReadKey(); //Need alternative to this line to execute below code
}
UpdateDocumentStatus();
}
public static void DownloadDocument()
{
Console.WriteLine("Downloading documents...");
}
public static void UpdateDocumentStatus()
{
Console.WriteLine("Update status...");
}
}
}
First, Hangfire is a Queue based system. All tasks are executed in a background process. This requires the server to be running constantly to pick the jobs up.
Next, you need to understand what using does. Using gives scope to your objects. This means that at the end of your using statement, the object gets disposed.
In your case,
BackgroundJobServer starts
Read key
dispose BackgroundJobServer
As long as you don't push a key, you remain inside the using statement and your server stays active (undisposed). That's why, when you remove 'Console.ReadKey();' your server does nothing as it gets disposed almost immediately.
For you the fix is basically: Remove the using block.
GlobalConfiguration.Configuration.UseMemoryStorage();
BackgroundJob.Enqueue(() => DownloadDocument());
var server = new BackgroundJobServer())
UpdateDocumentStatus();
Console.ReadKey();
I am trying to make a Discord.Net bot for my Discord Server. I would like the Bot to share data between commands, but currently because of the nature of the async tasks, the class variables are reset to null as a new instance is created on each task. I was wondering how other Discord Bots handle this problem.
I know I am able to save the values to a file/DB but I was hoping to use something simpler to allow for multiple concurrent commands.
using Discord.Commands;
using System.Threading.Tasks;
namespace UsefulDiscordBot.Modules
{
public class Foo : ModuleBase<SocketCommandContext>
{
string test;
[Command("initializeString")]
public async Task initializeString()
{
test = "Initialized";
}
[Command("addToString")]
public async Task addToString([Remainder]string s)
{
test += s;
}
[Command("printString")]
public async Task printString()
{
await ReplyAsync(test);
}
}
}
Command modules in Discord.Net are Transient (they are spawned when required and disposed of once executed). That said, the general best practice (when building a bot with Discord.Net) for what you wish to achieve is to use a Service - a Class to store your data external to the command module.
You can read up on how to do this via the Discord.Net documentation. There's a section in the FAQ (linked) specifically for this. Hope it helps.
I'm trying to create a dotnet core console app. The app is a simply utility app and should start, do its thing and exit.
It's easy to achieve using standard console application template generated by Visual Studio.
But these days we have HostBuilder which seems to be more attractive because it brings unifying experience with WebHostBuilder, DI, Loggin, etc.
But it looks like this approach only designed for the long running background services. It will start and sit forever until the external termination event (like Ctrl-C).
Is there a way to end this type of application from the inside?
You can use IHostApplicationLifetime to stop running of your application, you can access it from constructor and call StopApplication() method.
IHostApplicationLifetime _lifeTime;
public MyClass(IHostApplicationLifetime lifeTime)
{
_lifeTime = lifeTime;
}
then StopApplication()
public void Exit()
{
_lifeTime.StopApplication();
}
Edited to use IHostApplicationLifetime as IApplicationLifetime is deprected.
Even though you are using the HostBuilder to register all dependencies in your application, you don't have to use the IHost to execute a cmd line app. You can just execute your app via creating a service scope like so:
var hostbuilder = new HostBuilder();
builder.ConfigureServices(ConfigureServices); //Configure all services for your application here
var host = hostbuilder.Build();
using (var scope = host.Services.CreateScope())
{
var myAppService = scope.ServiceProvider.GetRequiredService<IMyAppServiceToRun>(); //Use IHost DI container to obtain instance of service to run & resolve all dependencies
await myAppService.StartAsync(); // Execute your task here
}
using (host)
host.StopAsync();
I had the same problem. I needed to terminate my web service. So I used the approach like the solution marked answer, but the service didn't terminate instantly he continued with commands. So I needed to return right after the exit.
my killing method:
private void Exit(IHostApplicationLifetime lifetime, ExitCode code)
{
Environment.ExitCode = (int)code; //just shows in debug
lifetime.StopApplication();
// add log if you want to see the exitCode
}
the Configure method inside the Startup.cs
public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime)
{
// code
bool hasPendingMigrations = HasDatabasePendingMigrations(app);
if (hasPendingMigrations)
{
Exit(lifetime, ExitCode.HasPendingMigrations);
// we need to return explicit after exit
return;
}
// code
}
You need to return.
The ExitCode is a custom created enum.
Edit:
After some time I moved the seeding into the Program.cs of my project which allows me to add test data / seed the database before even starting the server.
1- Use the UseConsoleLifetime() while building the host in Program.cs
Program.cs:
Host.CreateDefaultBuilder(args).UseConsoleLifetime(opts => opts.SuppressStatusMessages = true);
2- Registered ApplicationStopped event. So that you can brute force terminate the app by calling Kill() method of the current process.
Startup.cs:
public void Configure(IHostApplicationLifetime appLifetime) {
appLifetime.ApplicationStarted.Register(() => {
Console.WriteLine("Press Ctrl+C to shut down.");
});
appLifetime.ApplicationStopped.Register(() => {
Console.WriteLine("Terminating application...");
System.Diagnostics.Process.GetCurrentProcess().Kill();
});
}
The following works as of .NET Core 5.x/6.x
I needed the ability to shut down a service running on my web host.
They don't provide the granular ability to choose a specific web service that is running & I was having to continually shut down my entire site. I wanted to be able to call a WebAPI method in my service & shut it down just the one service instead of having to stop IIS & restart.
Here's how you can add the ability to shutdown your service from your HomeController (or any other controller in your project).
In your HomeController add the following code:
public class HomeController : Controller
{
private IHostApplicationLifetime _lifeTime; // add private member
// add appLifetime parameter to constructor
public HomeController(ILogger<HomeController> logger, IHostApplicationLifetime appLifetime)
{
_logger = logger;
_lifeTime = appLifetime; // init the private member var
}
Now, we can add a method that you can access via JavaScript fetch() for example.
Further down in the HomeController add the following method.
[HttpGet("StopService")]
public ActionResult StopService(){
_lifeTime.StopApplication();
return new JsonResult(new {result="true",message="<YourServiceName> is shutting down."});
}
Now you can make a call to your service (even from the browser console window) like the following:
fetch("http://localhost:5243/StopService")
.then(result => result.json())
.then(data => console.log(data));
When that call completes you will see something like the following in your console.
If you want to protect the method (I suggest you do) from outsiders then you can simply add a pwd as a parameter to the StopService method & take a hashed pwd to insure the user who is calling it is allowed. I'll let you work out those details.
I have created a WCF service and hosted it in IIS. I can access the WCF service by creating a web reference in my asp.net web forms application. I need to have the WCF web service run a long running method (async). Does anyone have good example code of how to call a WCF asynchronous method from an asp.net web forms application button click method?
I have looked at IAsyncResult, EAP, and TAP...
What currently is the best way to make an asynchronous call to a WCF asynchronous method from a ASP.NET web forms application?
I did now change my code to use a Service Reference instead of a Web reference.
Service ReceiptFooterService (ServiceContract, OperationsContract, DataContract):
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading.Tasks;
namespace ReceiptFooterDeploymentService
{
[ServiceContract]
public interface IReceiptFooterService
{
[OperationContract(Name = "GetRegisterPingResults")]
Task<List<PingReply>> GetRegisterPingResults(List<StoreIP> potentialIPs);
}
[DataContract]
public class StoreIP
{
private int _storeNumber;
private string _ipAddress;
[DataMember]
public int StoreNumber
{
get
{
return _storeNumber;
}
set
{
_storeNumber = value;
}
}
[DataMember]
public string IPAddress
{
get
{
return _ipAddress;
}
set
{
_ipAddress = value;
}
}
}
}
ReceiptFooterService class:
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace ReceiptFooterDeploymentService
{
public class ReceiptFooterService : IReceiptFooterService
{
public async Task<List<PingReply>> GetRegisterPingResults(List<StoreIP> potentialIPs)
{
var tasks = potentialIPs.Select(sip => new Ping().SendPingAsync(sip.IPAddress, 1000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
}
}
ASP.NET web forms client: Only first few lines of the method (for brevity)
private List<StoreDetail> PingStoreAndUpdateStatus(List<StoreDetail> storeDetails)
{
ReceiptFooterService.StoreIP[] potentialIPs = GetStoreRegisterIps(storeDetails).ToArray();
ReceiptFooterService.ReceiptFooterServiceClient client = new ReceiptFooterService.ReceiptFooterServiceClient();
List<PingReply> pingReplies = client.GetRegisterPingResultsAsync(potentialIPs).Result.ToList();
I am attempting to ping approximately 2000 IP addresses asynchronously. I am getting back four results that show success. Although, none of the other PingReply details are showing. I need to know what IP address was pinged.
Should I be awaiting somewhere... is that why it is returning to soon, or is there an error causing it to fail. Any suggestions would be appreciated.
Below, is a QuickWatch of my results:
First, when using WCF why don't you use service reference?
(Web vs Service reference)
Then, when using SOAP client, it automatically generates SOAP WCF client with each method with synchronous and asynchronous variant.
Then you just call this async method
MyServiceClient.DummyMethodAsync(someParameters...)
I found that using a WCF web service for the kind of asynchronous work I needed to do (was not working from in the web service).
I switched to creating a console application that I call with arguments, and then store the results in a database. I then have the web site access the results stored in the database.
The console application context is much more friendly to completing the amount of asynchronous methods that I wanted to run at the same time.
I'm using an Azure WebJob, but now I get the following error message:
No functions found. Try making job classes public and methods public static.
My code is so simple:
static void Main()
{
var host = new JobHost();
host.RunAndBlock();
}
public static async Task BlobTrigger(
[BlobTrigger("test/{name}")] Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob input,
TextWriter log)
{
//code
}
Also, I create a zip file from my debug folder and upload it, and the job is configure to run continuously.
so sorry, the error is so simple, I added the access public to the class and it's fine (I'm using the final version of web jobs here), but I have some jobs with the webjobs prerelease and the public it's not necessary.
Thanks to all, regards.