Using FluentScheduler - ASP.NET Core MVC - asp.net-core-mvc

I currently have a simple website setup with ASP.NET Core MVC (.NET 4.6.1), and I would like to periodically do some processes like automatically send emails at the end of every day to the registered members.
After doing some searching, I came across two common solutions - Quartz.NET and FluentScheduler.
Based on this SO thread, I found the approach of using FluentScheduler more easier to digest and use for my simple task. After quickly implementing the following lines of code into my Program.cs class, I had the emails going out successfully every minute (for testing purposes).
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
var registry = new Registry();
JobManager.Initialize(registry);
JobManager.AddJob(() => MyEmailService.SendEmail(), s => s
.ToRunEvery(1)
.Minutes());
host.Run();
}
}
However, now apart from sending emails I also need to do some back-end processing for e.g. updating the user records in the DB when mails are being sent out. For this, I normally inject my Entity Framework Context into the constructor of my controllers and use it to get/update SQL records.
My question is, since I cannot really inject these services into the main method, where would be the appropriate place to initialize the registry and add jobs for scheduling?
Thanks for the help, I am a little new to this so a little guidance would be much appreciated!

Instead of Program's Main function, I initialized the same in Startup.cs before app.UseMvc..
public void Configure(...., IDependencyObject dependencyObject)
{
....
JobManager.Initialize(new MyRegistry(dependencyObject));
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{controller}/{action}/{id?}");
});
}
My registry class looks like this:
public class MyRegistry: Registry
{
public MyRegistry(IDependencyObject dependencyObject)
{
Schedule(() => new SyncUpJob(dependencyObject)).ToRunNow().AndEvery(10).Seconds();
}
}
My Job class looks like this:
public class SyncUpJob: IJob
{
public SyncUpJob(IDependencyObject dependencyObject)
{
DependencyObject= dependencyObject;
}
public IDependencyObject DependencyObject{ get; set; }
public void Execute()
{
// call the method to run weekly here
}
}

You can define all your jobs and their schedules, by subclassing from FluentScheduler Registry class. something like:
public class JobRegistry : Registry {
public JobRegistry() {
Schedule<EmailJob>().ToRunEvery(1).Days();
Schedule<SomeOtherJob>().ToRunEvery(1).Seconds();
}
}
public class EmailJob : IJob {
public DbContext Context { get; } // we need this dependency, right?!
public EmailJob(DbContext context) //constructor injection
{
Context = context;
}
public void Execute()
{
//Job implementation code: send emails to users and update database
}
}
For injecting dependencies into jobs, you need to implement FluentScheduler IJobFactory interface. GetJobIntance method is called by FluentScheduler for creating job instances. Here you can use any DI library you want; In this sample implementation, I'm going to assume that you use Ninject:
public class MyNinjectModule : NinjectModule {
public override void Load()
{
Bind<DbContext>().To<MyDbContextImplemenation>();
}
}
public class JobFactory : IJobFactory {
private IKernel Kernel { get; }
public JobFactory(IKernel kernel)
{
Kernel = kernel;
}
public IJob GetJobInstance<T>() where T : IJob
{
return Kernel.Get<T>();
}
}
Now you can start your jobs in main method by calling:
JobManager.JobFactory = new JobFactory(new StandardKernel(new MyNinjectModule()));
JobManager.Initialize(new JobRegistry());

Related

Entity Framework 6 "DbContext has been disposed" exception

Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.

Adding custom data for an operation to Application Insights telemetry

I'm trying to add a bunch of custom data fields to every piece of telemetry I can, and this data is consistent across a single operation, but varies from operation to operation.
I have a custom ITelemetryInitializer, and within that I can do something like:
public class MyInitializer : ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = "PC";
}
}
But I don't understand how I'm suppose to push this data into this initializer.
I've added something like this:
public class MyInitializer : ITelemetryInitializer
{
private string mPlatform = "unknown";
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = mPlatform;
}
public void SetPlatform(string platform)
{
mPlatform = platform
}
}
And then at the controller level I do something like this:
foreach (var init in TelemetryConfiguration.Active.TelemetryInitializers)
{
var customInit = init as MyInitializer;
if (customInit != null)
{
customInit.SetPlatform(requestPlatform);
}
}
But this is horribly clunky, and prone to error (e.g. if a piece of telemetry gets sent before this function is called), and I'm not really sure if this is thread-safe.
What's the intended way of passing around this kind of data?
I think I've solved this now, the solution is to write to the properties of the TelemetryClient within the controller like this:
[Route( "[controller]" )]
public class MyController : Controller
{
private readonly TelemetryClient mTelemetryClient;
public MyController(
TelemetryClient TelemetryClientArg )
{
mTelemetryClient = TelemetryClientArg;
mTelemetryClient.Context.Properties.Remove("platform");
}
[HttpPost]
[Produces( "application/json" )]
public IActionResult Post( [FromBody] RequestClass RequestData )
{
mTelemetryClient.TrackTrace("Test trace 1"); // doesn't have platform set
mTelemetryClient.Context.Properties["platform"] = RequestData.platform;
mTelemetryClient.TrackTrace("Test trace 2"); // has platform set correctly
}
}
This seems to be safe as the controller constructor appears to be called before each http request is processed and the context within the TelemetryClient is unique per thread. I would like to get confirmation from the team that this is reasonable.

How can I access previously set fields in a seam #Observer method?

My current setup is JBoss Seam 2.2 on JBoss 4.2.3.GA.
I have two Beans like so:
#Name("mailingManager")
#Scope(ScopeType.PAGE)
public class MailingMgr {
private Mailing selectedMailing;
#Observer("mailing.letter.success")
public void recordSuccess(final Object arg) {
if (null != selectedMailing) { // store arg }
}
public void send() {
selectedMailing = new Mailing();
if ('EMAIL' == determineType()) {
EmailSender mailer = (EmailSender) Component.getInstance(EmailSender.class);
mailer.send(getAddresses());
}
// ... more options
}
}
#Name("emailSender")
#Scope(ScopeType.PAGE)
public class EmailSender {
public void send(final Set<String> addresses) {
for (String addr : addresses) {
// ... create a mail
Events.instance().raiseEvent("mailing.letter.success", getGeneratedMail());
}
}
}
The problem is that when recordSuccess() is called selectedMailing is always null.
As a workaround I'm setting selectedMailing in the conversation context manually before calling any code that could potentially trigger my events, and then annotate my field with #In(required=false) to inject it again before recordSuccess is called. But is there a more elegant solution (keeping the decoupling intact)? And why isn't the calling bean reused to handle the event?

How to fake an HttpContext and its HttpRequest to inject them in a service constructor

In a console application, I would like to use a service that would normally need the current http context to be passed to its constructor. I am using Ninject, and I think I can simply fake an http context and define the proper binding, but I have been struggling with this for a few hours without success.
The details:
The service is actually a mailing service that comes from an ASP.Net MVC project. I am also using Ninject for IoC. The mail service needs the current http context to be passed to its constructor. I do the binding as follows:
kernel.Bind<IMyEmailService>().To<MyEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new HttpContextWrapper(HttpContext.Current));
However, I would like now to use this mailing service in a console application that will be used to run automated tasks at night. In order to do this, I think I can simply fake an http context, but I have been struggling for a few hours with this.
All the mailing service needs from the context are these two properties:
httpContext.Request.UserHostAddress
httpContext.Request.RawUrl
I thought I could do something like this, but:
Define my own fake request class:
public class AutomatedTaskHttpRequest : SimpleWorkerRequest
{
public string UserHostAddress;
public string RawUrl;
public AutomatedTaskHttpRequest(string appVirtualDir, string appPhysicalDir, string page, string query, TextWriter output)
: base(appVirtualDir, appPhysicalDir, page, query, output)
{
this.UserHostAddress = "127.0.0.1";
this.RawUrl = null;
}
}
Define my own context class:
public class AutomatedTasksHttpContext
{
public AutomatedTaskHttpRequest Request;
public AutomatedTasksHttpContext()
{
this.Request = new AutomatedTaskHttpRequest("", "", "", null, new StringWriter());
}
}
and bind it as follows in my console application:
kernel.Bind<IUpDirEmailService>().To<UpDirEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new AutomatedTasksHttpContext());
Unfortunately, this is not working out. I tried various variants, but none was working. Please bear with me. All that IoC stuff is quite new to me.
I'd answered recently about using a HttpContextFactory for testing, which takes a different approach equally to a console application.
public static class HttpContextFactory
{
[ThreadStatic]
private static HttpContextBase _serviceHttpContext;
public static void SetHttpContext(HttpContextBase httpContextBase)
{
_serviceHttpContext = httpContextBase;
}
public static HttpContextBase GetHttpContext()
{
if (_serviceHttpContext!= null)
{
return _serviceHttpContext;
}
if (HttpContext.Current != null)
{
return new HttpContextWrapper(HttpContext.Current);
}
return null;
}
}
then in your code to this:
var rawUrl = HttpContextFactory.GetHttpContext().Request.RawUrl;
then in your tests use the property as a seam
HttpContextFactory.SetHttpContext(HttpMocks.HttpContext());
where HttpMocks has the following and would be adjusted for your tests:
public static HttpContextBase HttpContext()
{
var context = MockRepository.GenerateMock<HttpContextBase>();
context.Stub(r => r.Request).Return(HttpRequest());
// and stub out whatever else you need to, like session etc
return context;
}
public static HttpRequestBase HttpRequest()
{
var httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
httpRequest.Stub(r => r.UserHostAddress).Return("127.0.0.1");
httpRequest.Stub(r => r.RawUrl).Return(null);
return httpRequest;
}

How to Integrate Prism, Unity, and Enterprise Library

I'm building a WPF application. I'm using Prism 4, and Unity. I want to add two Enterprise Library 5 blocks to the application, Logging and Exception Handling. I have a singleton LoggerFacadeCustom.cs in my Infrastructure class that supports the ILoggerFacade and I've created it in my bootstrapper, and it is generating log files. It "news" up a unity container in its constructor (second code block)
Where do I add the container.resolve for ExceptionManager? How do I connect the Exception handling block to ILoggerFacade in my bootstrapper? How do I get all the exceptions to come out in the same log? Here is my existing bootstrapper.cs
public class Bootstrapper : UnityBootstrapper {
protected override ILoggerFacade CreateLogger() {
return LoggerFacadeCustom.Instance;
}
protected override DependencyObject CreateShell() {
return Container.Resolve<Shell>();
}
protected override void InitializeShell() {
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
//Other shell stuff...
}
protected override IModuleCatalog CreateModuleCatalog() {
var catalog = new ModuleCatalog();
//These primary modules must register their own services as if they were acting independantly
catalog.AddModule(typeof(XmlCommentMergeModule));
//These support modules require at least one primary module above to be added first
catalog.AddModule(typeof(ToolboxHeaderModule));
catalog.AddModule(typeof(ToolboxFooterModule));
catalog.AddModule(typeof(ToolboxStartModule));
return catalog;
}
}
LoggerFacadeCustom:
public class LoggerFacadeCustom : ILoggerFacade {
private static readonly LoggerFacadeCustom _instance = new LoggerFacadeCustom();
public static LoggerFacadeCustom Instance { get { return _instance; } }
private LoggerFacadeCustom() {
var container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
_logWriter = container.Resolve<LogWriter>();
}
private readonly LogWriter _logWriter;
public void Write(string message) { Write(message, null); }
public void Write(string message, string category, int priority) {
_logWriter.Write(message, category, priority);
}
public void Write(string message, Dictionary<string, object> properties) {
_logWriter.Write(message, LiteralString.LogCategoryProcess, properties);
}
#region ILoggerFacade Members
public void Log(string message, Category category, Priority priority) {
throw new NotImplementedException();
}
#endregion
}
Your bootstrapper is the Composition Root of your application. You should register all dependencies there. And only there. You should never reference the container directly outside the composition root.
If your classes have a dependency you should inject that dependency using a pattern like constructor injection.
Don't use static classes. Static kills dependency injection and testability and it hides dependencies to a point where everything is referenced from everywhere.
Make your logger facade a constructor parameter. You can do the same with the error handling block.
Don't use the container as a ServiceLocator. That is considered an anti-pattern in modern software architecture.

Resources