ASP.NET Web API Startup Questions - asp.net

I am creating an ASP.NET Web API using .NET 4.5.2. The API needs to connect to a runspace on startup. I have questions about when this Startup.Configuration method actually runs though. It does not seem to run when I start the website or app pool. It seems to wait until the first time somebody tries to access the website. Is this correct? Also, it seems to run again at random times. I have seen it run after 2 hours, 4 hours, and 16 hours. It doesn't really make any sense. Can somebody clear up for me when these methods should run? Also, if you have a suggestion for a better place to put them given that I want it to be a shared runspace for all connections and that I want it to run before anybody tries to connect to the API. Perhaps a separate service?
Also, would it be worth looking into ASP.NET CORE? I don't need it to run on anything other than IIS, however if there is a benefit to using CORE I am at a point where it will be easy to switch.
public partial class Startup
{
public Cache GlobalCache;
public static PowershellRunspace PSRunspace;
public static ActiveDirectory ADObjects = new ActiveDirectory();
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
GlobalCache = new Cache();
AppLog log = new AppLog();
log.InfoLog("Starting PowerShell Runspace in Hangfire...", true);
GlobalConfiguration.Configuration.UseSqlServerStorage("Hangfire");
BackgroundJob.Enqueue(() => log.InfoLog("Hangfire started!", true));
BackgroundJob.Enqueue(() => ADObjects.Startup(true));
BackgroundJob.Enqueue(() => StaticRunspace.Start());
app.UseHangfireDashboard();
app.UseHangfireServer();
}
}

Assuming you're running this application in IIS (and not self-hosting), the following rules apply:
The Configuration method runs once per application start.
The application is started lazily (on the first request to it via HTTP/S).
IIS has a few settings that affect the application:
An idle timeout. If the app isn't accessed via a request in 20 minutes then the application is unloaded / put offline. The next request starts it again.
A regular app pool recycle. It just straight up restarts the application by recycling the app pool every 1740 minutes.
So the behavior you're seeing is likely due to the infrequent access of the application, combined with the IIS defaults. If you'd like to see or configure the settings, you can do so by going into IIS, right clicking on your app pool, and selecting Advanced Settings.

Related

How to configure Hangfire with Autofac in a dotnet core console app

I'm trying to port a working Hangfire setup embedded in a Kestrel webserver to a console app. I've modified the web app so it still provides the Hangfire dashboard but doesn't start its own Hangfire server.
The code I must port uses Autofac. I've added the Hangfire.Autofac package to the console app and have already performed all the steps detailed in the answer to Hangfire Autofac .net core 3.1
When I create a job (using the web app) the console app Hangfire server tries to execute the job but I get this failure message:
The requested service 'AED.ServicesLayer.JobProcessing.ProcessManager' has not been registered.
Investigating this we examine the setup of Autofac in the console app. This is how I set up my container.
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
var containerBuilder = new Autofac.ContainerBuilder();
containerBuilder.RegisterInstance(Log.Logger).AsImplementedInterfaces();
containerBuilder.RegisterModule(new RepositoryModule(config));
containerBuilder.RegisterType<UserService>().As<IUserService>();
containerBuilder.RegisterInstance(config).As<IConfiguration>();
containerBuilder.RegisterModule(new JobProcessingModule(config));
var container = containerBuilder.Build();
When the app is executed, hitting a breakpoint in JobProcessingModule proves the following line of code is executed.
builder.RegisterType<ProcessManager>().As<IProcessManager>();
It is very curious that the containerBuilder instance passed to JobProcessingModule.Load(containerBuilder) is not the same containerBuilder object on which RegisterModule is invoked.
However, experiments with simplified injectables suggest that this is normal, and the injected items are nevertheless visible in the registrations for the container that is returned.
Re-examining the logged failure we note that the class is mentioned by class name and not by interface. Changing the registration by removing the interface registration, like so
builder.RegisterType<ProcessManager>();//.As<IProcessManager>();
caused the ProcessManager to be found in the Hangfire console host but caused run-time errors in the web application when creating the job.
Registering it both ways caused ProcessManager to be found by both, with a new problem surfacing: cannot resolve dependencies. This, however, is merely a new case of the same problem.
While this allows me to move forward with getting a console host working, I do not like code I do not understand. Why should the console host require registration by class name when the web app does not?
Whatever is causing this has also caused Hangfire.IBackgroundJobClient to fail to resolve to the background job client. This is a hangfire class so it really does seem like there is a fundamental problem.
A lengthy investigation eventually revealed, confirmed by experiments, that this code
_recurringJobManager.AddOrUpdate(
insertResult.ToString(),
pm => pm.RunScheduledJobs(insertResult), interval.CrontabExpression
);
is responsible for the behaviour described in the question. AddOrUpdate is a generic method. When it is not explicitly typed it acquires its type from the class of the object passed to it. When the method is explicitly typed as the interface, like so
_recurringJobManager.AddOrUpdate<IProcessManager>(
insertResult.ToString(),
pm => pm.RunScheduledJobs(insertResult), interval.CrontabExpression
);
it remains compatible with the object, but the type acquired by Hangfire is the interface, and the console application can resolve ProcessManager from its interface.
Why the problem was not manifest in the web hosted Hangfire server remains a puzzle, but at least now I'm puzzled by the absence of a problem in a situation I don't have.

Start Process from .NET Web Application with Impersonalisation

I try to call an .exe file from a webapplication.
But I want the file called by the user that is impersonalisated by windoes authentication from the website.
Process process = new Process();
try
{
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = ConfigData.PVDToBudgetDBexePath;
process.StartInfo.CreateNoWindow = false;
process.Start();
log.Info("Process started by " + WindowsIdentity.GetCurrent().Name + " with ID: " + process.Id);
process.WaitForExit();
log.Info("After WaitForExit Process ID: " + process.Id);
}
catch (Exception ex)
{
log.Error("Error executing file with message " + ex.Message);
}
Both info log texts are logged correctly. There is no error occuring.
But the called Program does not do anything. No logging, no writing in Database.
The user has executable rights on the file.
When I call the same Code from Development Server it works fine.
I use .Net 4.5 and IIS 7
I found posts concerning this topic only for very old versions of .Net and IIS and that could not help me.
What am i doing wrong?
Or how can I find out whats going wrong?
many thanks,
EDIT:
To better make clear what I intend:
I have this (self made) exe file that imports Data from Excel Sheets into a Database. That needs some time. While doing this it logs its Progress whith log4net also into the database.
I want an UI (web application) were the user can trigger the import.
on this UI there is also an ajax progressbar that shows the progress of the import takten from the log table in the database.
I want maximum one instance of this import process to run in the same time. So I have a function that checks wheter the process is still running.
If so it does not allow to start another process. If not you can start it again.
private bool IsRunning(string name)
{
Process[] processlist = Process.GetProcesses();
if (Process.GetProcessesByName(name).Length > 0 )
{
return true;
}
else
{
return false;
}
}
I solved the problem now by starting the exe file via TimeScheduler.
path = filepath to the exe file
arguments = arguments to start the exe file with
using Microsoft.Win32.TaskScheduler;
using (TaskService taskService = new TaskService())
{
var taskDefinition = taskService.NewTask();
taskDefinition.RegistrationInfo.Author = WindowsIdentity.GetCurrent().Name;
taskDefinition.RegistrationInfo.Description = "Runs exe file";
var action = new ExecAction(path, arguments);
taskDefinition.Actions.Add(action);
taskService.RootFolder.RegisterTaskDefinition("NameOfTask", taskDefinition);
//get task:
var task = taskService.RootFolder.GetTasks().Where(a => a.Name == "NameOfTask").FirstOrDefault();
try
{
task.Run();
}
catch (Exception ex)
{
log.Error("Error starting task in TaskScheduler with message: " + ex.Message);
}
}
If you mean by development server the web server that is launched by Visual Studio, than this gives you a false test case since that server is launched by Visual Studio and uses your Windows account to run, while a standard configured IIS does not run under a "user" account but a very limited system account (luckily !!). Even if the user is logged in with a domain account in your website, the IIS process will not run under this account (that wouldn't make sense anyway). That is the reason why this code will not run in IIS and will run in your development server. Even if you get the exe to launch, it will run using the system account of IIS since you didn't supply any account, which is a limited account which will again run the exe different than you expected.
You will have to use impersonation, if you really want to go this way, but you will have to launch that process "impersonating" the user that is logged in in the website, asuuming that user account used to login even makes sense at that point. E.g. if it is a domain account, this might work, but if you use some other kind of authentication, like forms authentication, this has no meaning on OS level and thus you cannot use those credentials for impersonation in IIS.
In my experience, and I have done this a few times, impersonation in IIS is always a bad thing and is always creating issues, the same goes for launching command line process by the way.Luckily there is always a better/alternative solution when you think about it. Also the wait for a process to end in your code is not really a good practice. What if the process blocks? It will block website.
Luckily there is always a better/alternative solution when you think about it. A better/possible solution here is to use message queuing for example, where you just push a message to execute the task, and on the other end an application which processes the messages, which might use this command line tool then. That application can run under any user account you want, without you having to let IIS run under a different account. Later on you must of course come back to find the result of the operation, but that can be done using a callback in the background of your website. though this solution is a little bigger than what you are trying to do, it will have a better result on almost every field (responsiveness of your site, maintainability, scalability,..) the only thing where it is worse is the lines of code that you will need, but that is seldomly a valid factor to take into account
If you write the appplication for excel processing yourself, you can use a table in the DB as some kind of queue instead of using a message bus. Your web application then just needs to add rows with all necesarry info for the process in that table, the status and progress being one of them. Extend your processing application to monitor this table continuously and as soon as it detects a new record, it can then start to do the necessary task and update the db accordingly progress and status and end result). This avoids the messaging sub-system, will work equally good and will avoid you to have to launch a process with impersonation, which was the evil thing to start with.
You can modify the excel process to a windows service so that it runs continuously and starts with the system, but, if you don't want to, there are also tools to run any command line application as a windows service).
This technique would be much easier than the impersonation and allows your website to run in it's protected environment

Asp.net MVC web site on iis goes idle after few minutes

I have an asp.net mvc 5 site running on iis. In the Application_Start I have a call to this method called Run();.
private void Run()
{
Task t = new Task(() => new XyzServices().ProcessXyz());
t.Start();
t.ContinueWith((x) =>
{
Thread.Sleep(ConfigReader.CronReRunTimeInSeconds);
Run();
});
}
I am running a task which process some data, and as soon as the task completes I wait fr 20-30 seconds and rerun the task again.
Now all this works fine. But after a certain time, the process stops and its resumes only when I reopen the site url.
How do I overcome this ? Any ideas or suggestions ?
Might be your application pool is shutdown automatically
1) Open IIS.
2) Right click on your Application-Pool for the web application and select "Advanced Settings".
3) Set "Rapid-Fail Protection" -> "Enabled" to False.
If this is true, the Application pool is shut down if there are a specified number of worker process crashes within a specified time period. By default, an application pool is shutdown if there are 5 crashes within a 5 minutes interval.
for more info https://sharepoint.stackexchange.com/questions/22885/application-pool-is-auto-stopped-when-browse-web-application-in-iis7]

How to do scheduled tasks in ASP.NET?

I am coding a text based web browser game in ASP.NET. But i need a lil bit info so i decided to ask here.
When user enter any quest, lets say that quest take 10 mins to proceed. If user exits from game, how can i make my script to run automaticly and finish the quests and upgrade players power etc? I heard something like CronJob. But i dont know if it works for ASP.NET so i wanna hear any idea before i do this. Thank you for your help.
You could just add a cache item with a callback function.
public void SomeMethod() {
var onRemove = new CacheItemRemovedCallback(this.RemovedCallback);
Cache.Add("UserId_QuestId", "AnyValueYouMightNeed", null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration, CacheItemPriority.High, onRemove);
}
public void RemovedCallback(String key, Object value, CacheItemRemovedReason r){
// Your code here
}
The cache item has an expiration of 10 minutes, and as soon as it is removed from memory the RemovedCallback will be invoked.
Note: just to completely answer your question:
You could also use some of the frameworks available to schedule tasks in asp.net (such as Quartz.net).
Create a Console project in your solution and schedule it on the Web server (using the Windows Scheduler).
Create a Web Job project, if you are deploying your web in Azure.
But in your situation, using the cache is probably the simplest solution.

How to give a web request to windows task Scheduler

I have a static webmethod i.e (http://localhost:61176/trunk/MusteriKontrol.aspx/CheckMusteri) I want to call this method from Windows Task Scheduler. How should I do this?
You could use a PowerShell script. This has a check for the time, a commented-out Try..Catch in case you want to do something if an error is raised from the query, and records when it has run in the Application event log:
# Download the HTML of a web page.
# Make sure an event log source is created with New-EventLog -LogName Application -Source MyPSscript
# Only do this if the time is 5a.m. or later, to give the server a rest from midnight.
$currHour = (Get-Date).Hour
if ($currHour -ge 5) {
$web = New-Object Net.WebClient
#try {
$temp = $web.DownloadString("http://localhost:61176/trunk/MusteriKontrol.aspx/CheckMusteri")
#}
#catch {
# do nothing.
#}
write-eventlog -logname Application -source MyPSscript -eventID 1001 -entrytype Information -message "Fetched web page." -category 0
}
Have considered a different alternative to scheduling the call of a web method from Windows Task Scheduler?
For example, scheduling tasks from within an ASP.NET project is possible using the Revalee open source project.
Revalee is a service that allows you to schedule web callbacks to your web application. In your case, you would schedule a callback that would call your web method at a specific time. Revalee works very well with tasks that are discrete transactional actions, like updating some database values or sending an automated email message (read: not long running). The upside is that the code that schedules the callback, as well as the code that performs your action (i.e., your web method), would all reside within your app.
To use Revalee, you would:
Install the Revalee Service, a Windows Service, on your server. The Windows Service is available in the source code (which you would compile yourself) or in a precompiled version available at the Revalee website.
Use the Revalee client library in your Visual Studio project. (There is an MVC-specific version too.) The client library is available in the source code (which, again, you would compile yourself) or in a precompiled version available via NuGet.
You would register a future callback when your code calls the ScheduleWebMethodCallback() method (this example is assuming that you need your action to run 12 hours from now).
private void ScheduleWebMethodCallback()
{
DateTimeOffset callbackTime = DateTimeOffset.Now.AddHours(12.0);
// The callback should be in 12 hours from now
Uri callbackUrl = new Uri(string.Format("http://localhost:61176/trunk/MusteriKontrol.aspx/CheckMusteri"));
// Register the callback request with the Revalee service
RevaleeRegistrar.ScheduleCallback(callbackTime, callbackUrl);
}
When Revalee calls your application back, your app would perform whatever action you have coded it to do in the web method you have listed above.
In case it was not clear above, the Revalee Service is not an external 3rd party online scheduler service, but instead a Windows Service that you install and fully control on your own network. It resides and runs on a server of your own choosing, most likely your web server (but this is not a requirement), where it can receive callback registration requests from your ASP.NET application.
I hope this helps.
Note: The code example above uses a synchronous version of ScheduleCallback(), the Revalee client library also supports asynchronous calls à la:
RevaleeRegistrar.ScheduleCallbackAsync(callbackTime, callbackUrl);
Disclaimer: I was one of the developers involved with the Revalee project. To be clear, however, Revalee is free, open source software. The source code is available on GitHub.

Resources