Use session variables in to Hangfire Recurring Job - asp.net

I have integrated hangfire in to Asp.net web application and trying to use session variables in to Hangfire Recurring Job as like below :
public class Startup
{
public void Configuration(IAppBuilder app)
{
HangfireSyncServices objSync = new HangfireSyncServices();
var options = new DashboardOptions
{
Authorization = new[] { new CustomAuthorizationFilter() }
};
app.UseHangfireDashboard("/hangfire", options);
app.UseHangfireServer();
//Recurring Job
RecurringJob.AddOrUpdate("ADDRESS_SYNC", () => objSync.ADDRESS_SYNC(), Cron.MinuteInterval(30));
}
}
My “HangfireSyncServices” class as below:
public partial class HangfireSyncServices : APIPageClass
{
public void ADDRESS_SYNC()
{
string userName = Convert.ToString(Session[Constants.Sessions.LoggedInUser]).ToUpper();
//Exception throwing on above statement..
//........Rest code.......
}
}
public abstract class APIPageClass : System.Web.UI.Page
{
//common property & methods...
}
but I am getting run time exception as below at the time of getting value in to “userName”:
Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the
section in the application configuration.
I have tried to resolve above error using this LINK & other solution also but not able to resolved yet. can anyone help me on this issue.
Thanks in advance,
Hiren

Hangfire jobs don't run in the same context as asp.net, it has it's own thread pool. In fact, Hangfire jobs may even execute on a different server than the one that queued the job if you have multiple servers in your hangfire pool.
Any data that you want to have access to from within the job needs to be passed in as a method parameter. For example:
public partial class HangfireSyncServices //: APIPageClass <- you can't do this..
{
public void ADDRESS_SYNC(string userName)
{
//........Rest code.......
}
}
string userName = Convert.ToString(Session[Constants.Sessions.LoggedInUser]).ToUpper();
RecurringJob.AddOrUpdate("ADDRESS_SYNC", () => objSync.ADDRESS_SYNC(userName), Cron.MinuteInterval(30));
Note that doing the above creates a recurring task that will always execute for the same user, the one that was triggered the web request that created the job.
Next problem: you're trying to create this job in the server startup, so there is no session yet. You only get a session when a web request is in progress. I can't help you with that because I don't have any idea what you're actually trying to do.

Related

ASP.NET Some service register after mediator

We are working on service which collect data from AWS SQS then send batch to client. We are using mediator to publish notifications. The diagram of program looks like:
The problem is in first NotificationHandler from Mediatr.
private readonly EventCollectorHostedService _collector;
public CollectIncomingEventNotificationHandler(EventCollectorHostedService collector)
{
_collector = collector;
}
Class EventCollectorHostedService is register after Mediator so is not visible during registering this NotificationHandler and additionally it use Mediator to publish notification that batch is ready to send.
The error is that cannot construct CollectIncomingEventNotificationHandler because -> Unable to resolve service for type 'Api.Services.HostedServices.EventCollectorHostedService'.
services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
services.AddHostedService<EventCollectorHostedService>();
The ugly solution is to declare some functionality in EventCollectorHostedService as static or instead of injecting EventCollectorHostedService, inject IServiceProvider.
But these solution don't look clean for me so do you have any other better solution ?
Thanks in advance.
Maybe someone encountered with similar problem so finally i have a brilliant solution.
Background services have to be treat like separate microservices based on event driven architecture so we have to make internal message broker mechanism.
The very simple solution which cover my case is:
public class NotificationChannel : INotificationChannel
{
public event EventHandler<IncomingEventNotificataionEventArgs> IncomingEventReceived;
public void Publish<T>(T notification)
{
if(notification is IncomingEventNotification incomingEventNotification)
{
OnIncomingEventReceived(incomingEventNotification);
}
}
protected virtual void OnIncomingEventReceived(IncomingEventNotification notification)
{
if(IncomingEventReceived != null)
{
var args = new IncomingEventNotificataionEventArgs(notification);
IncomingEventReceived(this, args);
}
}
}

Changing property after specific time in ASP Core MVC

I'm working on a subscription-based system developed using Asp Core 3 MVC and Sql Server. The payment is handled externally, not linked to the application in any way. All I need to do in the application is to check the user's status, that is managed by an admin. When a user registers the status will be Pending, when the admin approves the user, Approval Date will be saved in the database, and the status will be changed to Approved.
The tricky thing for me is that I want the application to wait for 365 days before it changes the user status to Expired. I've no idea from where to start this part and would appreciate your help.
The simplest way i can think of without using hosted services is to add a check on user login that subtracts the approval date from today's date and check if the difference is equal or greater than 365 days
Something like this:
if ((DateTime.Now - user.ApprovalDate).TotalDays >= 365)
{
//Mark the user as expired...
}
You really shouldn't trigger a background thread from your main application code.
The correct way to do this is with a background worker process that has been designed specifically for this scenario.
ASP.NET Core 3 has a project type that is specifically for this, and will continue to run the back ground and can be used for all of your maintenance tasks. You can create a worker process using dotnet new worker -o YourProjectName or selecting Worker Service from the project selection window in Visual Studio.
Within that service you can then create a routine that will be used to determine if the user has expired. Encapsulate this logic in a class that makes testing easy.
Working repl has been posted here.
using System;
public class MainClass {
public static void Main (string[] args) {
var user = new User(){ ApprovedDate = DateTime.Today };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be false
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-180) };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be false
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-365) };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be true
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-366) };
Console.WriteLine (UserHelper.IsUserExpired(user));
}
}
public class User {
public DateTime ApprovedDate {get; set;}
}
public static class UserHelper
{
public static bool IsUserExpired(User user){
//... add all the repective logic in here that you need, for example;
return (DateTime.Today - user.ApprovedDate.Date).TotalDays > 365;
}
}

Re-instantiate a singleton with Prism in Xamarin Forms

How can I dispose and re-instantiate a singleton with Prism/DryIoC in Xamarin Forms?
I'm working with Azure Mobile Apps for offline data. Occasionally, I need to delete the local sqlite database and re-initialize it. Unfortunately the MobileServiceClient occasionally holds the db connection open and there's no method exposed to close it. The suggested solution (https://github.com/Azure/azure-mobile-apps-net-client/issues/379) is to dispose of MobileServiceClient. Only problem is that is registered with DryIoC as a singleton.
I'm not overly familiar with DryIoC, or Prism and Forms for that matter... But for the life of me, I can't see a way to do this.
I did cook up a pretty elaborate scheme that almost worked.
In my ViewModel method, when I needed the db freed up, I fired off an event -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(false);
Then in App.xaml.cs, I wired up a listener and a handler like so -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Subscribe(OnRegisterDatabaseEventPublished);
private void OnRegisterDatabaseEventPublished()
{
Container.GetContainer().Unregister<IAppMobileClient>();
Container.GetContainer().Unregister<IMobileServiceClient>();
Container.GetContainer().Register<IMobileServiceClient, AppMobileClient>(new SingletonReuse());
Container.GetContainer().Register<IAppMobileClient, AppMobileClient>(new SingletonReuse());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
Lastly, back in the ViewModel constructor, I had a final listener that handled the event coming back from App.xaml and finished processing.
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Subscribe(OnRegisterDatabaseCompletedEventPublished);
So the amazing thing is that this worked. The database was able to be deleted and all was good. But then I navigated to a different page and BOOM. DryIoC said it couldn't wire up the ViewModel for that page. I assume the unregister/register jacked up DryIoC for all injection... So how can I accomplish what needs to be done?
FINAL SOLUTION
Thanks so much to dadhi for taking the time to help. You are certainly a class act and I'm now considering using DryIoC elsewhere.
For anyone who stumbles on this, I'm posting the final solution below. I'll be as verbose as I can to avoid any confusion.
First, in my App.xaml.cs, I added a method for registering my database.
public void RegisterDatabase(IContainer container)
{
container.RegisterMany<AppMobileClient>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true),
ifAlreadyRegistered: IfAlreadyRegistered.Replace,
serviceTypeCondition: type =>
type == typeof(IMobileServiceClient) || type == typeof(IAppMobileClient));
}
I simply add a call to that method in RegisterTypes in place of registering the types in there directly.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.GetContainer().Rules.WithoutEagerCachingSingletonForFasterAccess();
...
RegisterDatabase(containerRegistry.GetContainer());
...
}
Note also the added rule for eager caching, per dadhi.
Later on when I need to release the database in the ViewModel... I kick things off by resetting my local db variable and sending an event to App.xaml.cs
_client = null;
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(true);
In App.xaml.cs, I have subscribed to that event and tied it to the following method.
private void OnRegisterDatabaseEventPublished()
{
RegisterDatabase(Container.GetContainer());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
Here I just call RegisterMany again, exactly the same as I do when the app starts up. No need to unregister anything. With the setup and ifAlreadyRegistered arguments (thanks, dadhi!), DryIoC allows the object to be replaced. Then I raise an event back to the VM letting it know the database has been released.
Finally, back in the ViewModel, I'm listening for the completed event. The handler for that event updates the local copy of the object like so.
_client = ((PrismApplication)App.Current).Container.Resolve<IAppMobileClient>();
And then I can work with the new object, as needed. This is key. Without setting _client to null above and resolving it again here, I actually ended up with 2 copies of the object and calls to methods were being hit 2x.
Hope that helps someone else looking to release their Azure Mobile Apps database!
I am not sure how exactly XF handles these things.
But in DryIoc in order for service to be fully deleted or replaced it need to be registered with setup: Setup.With(asResolutionCall: true). Read here for more details: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache#markdown-header-unregister-and-resolution-cache
Update
Here are two options and considerations that work in pure DryIoc and may not work XF. But it probably may help with solution.
public class Foo
{
public IBar Bar { get; private set; }
public Foo(IBar bar) { Bar = bar; }
}
public interface IBar {}
public class Bar : IBar {}
public class Bar2 : IBar { }
[Test]
public void Replace_singleton_dependency_with_asResolutionCall()
{
var c = new Container(rules => rules.WithoutEagerCachingSingletonForFasterAccess());
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.Register<IBar, Bar>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true)); // required
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.Register<IBar, Bar2>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true), // required
ifAlreadyRegistered: IfAlreadyRegistered.Replace); // required
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}
[Test]
public void Replace_singleton_dependency_with_UseInstance()
{
var c = new Container();
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.UseInstance<IBar>(new Bar());
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.UseInstance<IBar>(new Bar2());
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}

how to get HttpContext.Current.GetOwinContext() in startup

I very read for this problem but i can not fixed this so i think create a new question in this site.
HttpContext.Current.GetOwinContext();
i want get GetOwinContext values with above code . above code there are in my startup.cs
[assembly: OwinStartupAttribute(typeof(OwinTest.Startup))]
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var c = HttpContext.Current.GetOwinContext();
}
}
and i get this error
//No owin.Environment item was found in the context
but var c = HttpContext.Current.GetOwinContext(); work for me in HomeController fine.!
I just get GetOwinContext in my startup.cs class.
thankfull
You can't do that. The OWIN context does not exist without a request, and the Startup class only runs once for the application, not for each request. Your Startup class should initialize your middleware and your application and the middleware and the application should access the OWIN context when needed.
As mentioned, what you are asking isn't possible. However, depending on your requirements, the following is possible and gives you access within the context of creating object instances. This is something I needed in order to check for whether an instance was already added else where (I have multiple startup classes in different projects).
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
// Ensure we have our "main" access setup
app.CreatePerOwinContext<DataAccessor>(
(options, owinContext) =>
{
// Check that an instance hasn't already been added to
// the OwinContext in another plugin
return owinContext.Get<DataAccessor>() ?? DataAccessor.CreateInstance(options, owinContext);
}
);
}
Within the CreatePerOwinContext we have access to the OwinContext, so we can access it at the point of creating a new type. This might not help everyone as it's a little more specific to a person's needs, but is useful to know.

SignalR - access clients from server-side business logic

I have a requirement to start a process on the server that may run for several minutes, so I was thinking of exposing the following hub method:-
public async Task Start()
{
await Task.Run(() => _myService.Start());
}
There would also be a Stop() method that allows a client to stop the running process, probably via a cancellation token. I've also omitted code that prevents it from being started if already running, error handling, etc.
Additionally, the long-running process will be collecting data which it needs to periodically broadcast back to the client(s), so I was wondering about using an event - something like this:-
public async Task Start()
{
_myService.AfterDataCollected += AfterDataCollectedHandler;
await Task.Run(() => _myService.Start());
_myService.AfterDataCollected -= AfterDataCollectedHandler;
}
private void AfterDataCollectedHandler(object sender, MyDataEventArgs e)
{
Clients.All.SendData(e.Data);
}
Is this an acceptable solution or is there a "better" way?
You don't need to use SignalR to start the work, you can use the applications already existing framework / design / API for this and only use SignalR for the pub sub part.
I did this for my current customers project, a user starts a work and all tabs belonging to that user is updated using signalr, I used a out sun library called SignalR.EventAggregatorProxy to abstract the domain from SignalR. Disclaimer : I'm the author of said library
http://andersmalmgren.com/2014/05/27/client-server-event-aggregation-with-signalr/
edit: Using the .NET client your code would look something like this
public class MyViewModel : IHandle<WorkProgress>
{
public MyViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(WorkProgress message)
{
//Act on work progress
}
}

Resources