Quartz Job exception - quartz.net

I have sceheduled few quartz jobs and triggers for each job, and im using Database store to store the job details and i use simple triggers. I'm using Azure multiple instance. The jobs are scheduled properly, but sometimes the triggers are not firing and the trigger status is "error" and its an intermitent issue. In the console i see the exception as follows,
A first chance exception of type 'System.ArgumentException' occurred in Quartz.dll
A first chance exception of type 'Quartz.SchedulerException' occurred in Quartz.dll
A first chance exception of type 'Quartz.SchedulerException' occurred in Quartz.dll
Could anyone please help me solve the issue? Thanks
EDIT:
Im using ninject to open the Isession when the service starts as follows:
private void InitService()
{
IKernel kernel = CreateKernel();
_intraClockAuctionService = kernel.Get<IIntraClockAuctionService>();
}
private IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<ISessionFactory>().ToProvider<SessionFactoryBuilder>().InSingletonScope();
kernel.Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession())
.InCallScope();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
kernel.Bind<IJobFactory>().To<JobFactory>();
kernel.Bind<Func<Type, IJob>>().ToConstant(new Func<Type, IJob>(type => (IJob)kernel.Get(type)));
kernel.Bind<ISchedulerFactory>().ToConstant(new StdSchedulerFactory(GetQuartzProperties()));
kernel.Bind<MembershipProvider>().ToConstant(Membership.Providers["DefaultMembershipProvider"]);
kernel.Bind<IMembershipService>().To<AccountMembershipService>();}
and i create my job class as follows:
[DisallowConcurrentExecution]
public class AuctionActivateJob : IJob
{
private readonly ISession _session;
private readonly IAuctionService _auctionService;
private readonly JobManager _jobManager;
private readonly List<IScheduler> _schedulers = new List<IScheduler>();
public AuctionActivateJob(ISession session, JobManager jobManager, IAuctionService auctionService)
{
Utils.LogUtils.LogEvent("inside AuctionActivateJob");
_session = session;
_auctionService = auctionService;
_jobManager = jobManager;
}
public void Execute(IJobExecutionContext context)
{
try
{
SessionTransaction.BeginTransation(_session);
var auctionId = (int)context.MergedJobDataMap["AuctionId"];
var auction = _auctionService.GetAuction(auctionId);
LogUtils.LogEvent(auction.AuctionId + "_Activation starts:" + DateTime.Now);
_auctionService.ActivationStart(auction.AuctionId);
LogUtils.LogEvent(auction.AuctionId + "_Activation ends:" + DateTime.Now);
SessionTransaction.CommitTrans(_session);
}
catch (Exception e)
{
LogUtils.LogException(e);
Email.GenerateServiceExceptionEmail(e);
if (_session.Transaction != null && _session.Transaction.IsActive)
{
_session.Transaction.Dispose();
}
}
}
}
}
and i have common class to add job as
public class JobManager
{
private readonly IJobFactory _jobFactory;
private readonly ISchedulerFactory _schedulerFactory;
public JobManager(ISchedulerFactory schedulerFactory, IJobFactory jobFactory)
{
_schedulerFactory = schedulerFactory;
_jobFactory = jobFactory;
}
public IScheduler Add<T>(ITrigger trigger) where T : IJob
{
string name = trigger.Key.Name;
IScheduler scheduler = _schedulerFactory.GetScheduler();
try
{
scheduler.JobFactory = _jobFactory;
scheduler.Start();
var jobName = typeof (T).Name + "_" + name;
var jobDetail = new JobDetailImpl(jobName, typeof (T));
var isScheduled = scheduler.CheckExists(new JobKey(jobName));
Utils.LogUtils.LogEvent("isScheduled in quartz: " + isScheduled);
if (isScheduled)
return null;
scheduler.ScheduleJob(jobDetail, trigger);
}
catch (JobPersistenceException exception)
{
Utils.LogUtils.LogException(exception);
return null;
//do not do anything
}
catch (Exception ex)
{
Utils.LogUtils.LogException(ex);
return null;
}
return scheduler;
}
public class AuctionManagementService : IAuctionManagementService
{
private static readonly TimeSpan SchedulePoolingInterval =
TimeSpan.FromSeconds(Convert.ToInt32(ConfigurationManager.AppSettings["SchedulePoolingInter
val"]));
private readonly string _appName = ConfigurationManager.AppSettings["AppName"];
private readonly Timer _eventTimer;
private readonly ISession _session;
private readonly IAuctionService _auctionService;
private readonly JobManager _jobManager;
private readonly List<IScheduler> _schedulers = new List<IScheduler>();
private readonly ISchedulerFactory _schedularFactory;
public AuctionManagementService(ISession session, JobManager jobManager, IAuctionService auctionService, ISchedulerFactory schedulerFactory)
{
try
{
_session = session;
_eventTimer = new Timer();
_eventTimer.Elapsed += Refresh;
_eventTimer.Interval = SchedulePoolingInterval.TotalMilliseconds;
_jobManager = jobManager;
_auctionService = auctionService;
_schedularFactory = schedulerFactory;
_schedulers.Add(_schedularFactory.GetScheduler());
}
catch (Exception ex)
{
LogUtils.LogEvent(ex.Message + ex.StackTrace);
Utils.Email.GenerateServiceExceptionEmail(ex);
}
}

some parameter is passing as null, may be schedule of job, check null before passing to method to call.
Sample code is required to solve.

Related

Some services are not able to be constructed Error while validating the service descriptor Lifetime: Scoped Unable to resolve service for type

my usermanager services is :
public class UserManagerService : IUserManagerService
{
private readonly UserManager<UserModel> userManager;
private readonly UserServiceHelper userServiceHelper;
public UserManagerService(UserManager<UserModel> _userManager)
{
this.userManager = _userManager;
userServiceHelper = new UserServiceHelper();
}
public async Task<bool> CreateUser(UserViewModel user)
{
try
{
var new_user = userServiceHelper.GetNewItems(user);
await userManager.CreateAsync(new_user);
return true;
}
catch(Exception e)
{
throw;
}
}
IUsermanagerServices interface is :
public interface IUsermanagerServices
{
Task<bool> CreateUser(UserViewModel user);
Task<bool> CreateUserBatch(List<UserViewModel> users);
Task<bool> DeleteUser(UserViewModel user);
}
And userapicontroller is :
[Route("api/[controller]")]
[ApiController]
public class UserApiController : ControllerBase
{
private readonly IUserManagerService userManager;
public UserApiController(IUserManagerService _userManager)
{
userManager = _userManager;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<UserViewModel> users)
{
try
{
var _result = await userManager.CreateUserBatch(users);
var _response = new ResponseModel<bool>()
{
Content = _result,
Message = "np"
};
return Ok(_response);
}
catch(Exception e)
{
var _response = new ResponseModel<bool>()
{
Content = false,
ExceptionMessage = e.Message,
HasError = true
}.ToString();
return Problem(_response);
}
}
service extension is :
public static void AddUserManagerServices(this IServiceCollection services)
{
services.AddScoped<IUserManagerService, UserManagerService>();
}
and program :
#region
builder.Services.AddUserManagerServices();
#endregion
var app = builder.Build();
in this last line i have exception
Some services are not able to be constructed (Error while validating the service
descriptor 'ServiceType: Lifetime: Scoped ImplementationType:
Unable to resolve service for type while attempting to activate)
how can i fix it ?

How to inject cancellation token in a HostedService timer with Simple Injector

I'm trying to create a console application that run ahosted service with Simple Injector and I looked to the example at generichostintegration.
Now I would like to change IProcessor.DoSomeWork to be an async function with a cancellation token as parameter so DoSomeWork can be canceled:
public async void DoSomeWork(CancellationToken cancellationToken)
{
await Task.Delay(_settings.Delay, cancellationToken);
}
How can i inject the cancellation token from HostedService
private void DoWork()
{
try
{
using (AsyncScopedLifestyle.BeginScope(this.container))
{
var service = this.container.GetInstance<TService>();
this.settings.Action(service);
}
}
catch (Exception ex)
{
this.logger.LogError(ex, ex.Message);
}
}
and configurate in the right way the container
container.RegisterInstance(new TimedHostedService<IProcessor>.Settings(
interval: TimeSpan.FromSeconds(10),
action: processor => processor.DoSomeWork()));
I'm a bit stack with that. Maybe i'm thinking wrong?
*** Updated ***
This is what I did in the end. I kept it as simple as possible.
class Program
{
public static async Task Main(string[] args)
{
var container = new Container();
IHost host = CreateHostBuilder(args, container)
.Build()
.UseSimpleInjector(container);
ConfigureContainer(container);
await host.RunAsync();
}
private static void ConfigureContainer(Container container)
{
container.Register<IWorkScheduler, WorkScheduler>(Lifestyle.Singleton);
// Sets the schedule timer interval
container.RegisterInstance(new WorkSchedulerSettings(TimeSpan.FromSeconds(1)));
container.Register<DoSomethingWorker>();
container.RegisterInstance(new DoSomethingSettings(new TimeSpan(0, 0, 5)));
container.Register<DoSomethingElseWorker>();
container.RegisterInstance(new DoSomethingElseSettings(new TimeSpan(0, 0, 10)));
container.Verify();
}
public static IHostBuilder CreateHostBuilder(string[] args, Container container) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddSimpleInjector(container, options =>
{
// Registers the hosted service as singleton in Simple Injector
// and hooks it onto the .NET Core Generic Host pipeline.
options.AddHostedService<BackgroundHostedService>();
services.AddLogging();
});
})
.UseConsoleLifetime();
}
the hosted service
public class BackgroundHostedService
: BackgroundService
{
private readonly IWorkScheduler _scheduler;
private readonly Container _container;
private readonly ILogger _logger;
public BackgroundHostedService(IWorkScheduler scheduler, Container container, ILogger<BackgroundHostedService> logger)
{
_scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
_container = container ?? throw new ArgumentNullException(nameof(container));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public override Task StartAsync(CancellationToken cancellationToken)
{
LoadWorkers();
return base.StartAsync(cancellationToken);
}
protected override Task ExecuteAsync(CancellationToken cancellationToken)
{
try
{
_scheduler.Start();
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
return Task.CompletedTask;
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
await _scheduler.Stop();
}
private void LoadWorkers()
{
// Hook up triggers and specify span period and if they have to run once at time.
WorkTrigger trigger1 = new WorkTrigger(_container.GetInstance<DoSomethingWorker>(), new TimeSpan(0, 0, 2), false);
_scheduler.AddTrigger(trigger1);
WorkTrigger trigger2 = new WorkTrigger(_container.GetInstance<DoSomethingElseWorker>(), new TimeSpan(0, 0, 5), true);
_scheduler.AddTrigger(trigger2);
}
public override void Dispose()
{
_scheduler.Dispose();
base.Dispose();
}
the "pseudo" scheduler
public interface IWorkScheduler : IDisposable
{
void Start();
Task Stop();
void AddTrigger(WorkTrigger trigger);
}
public class WorkSchedulerSettings
{
public readonly TimeSpan Interval;
public WorkSchedulerSettings(TimeSpan interval)
{
Interval = interval;
}
}
public class WorkScheduler
: IWorkScheduler, IDisposable
{
private readonly Timer _timer;
private readonly WorkSchedulerSettings _settings;
private readonly ILogger<WorkScheduler> _logger;
private readonly List<Task> _tasks;
private readonly List<WorkTrigger> _triggers;
private readonly CancellationTokenSource _cancTokenSource;
public WorkScheduler(WorkSchedulerSettings settings, ILogger<WorkScheduler> logger)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timer = new Timer(callback: _ => DoWork());
_tasks = new List<Task>();
_triggers = new List<WorkTrigger>();
_cancTokenSource = new CancellationTokenSource();
}
public void Start()
{
_logger.LogInformation("Scheduler started");
_timer.Change(dueTime: TimeSpan.Zero, period: _settings.Interval);
}
public async Task Stop()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_cancTokenSource.Cancel();
await Task.WhenAll(_tasks);
_tasks.Clear();
_logger.LogInformation("Scheduler stopped");
}
public void AddTrigger(WorkTrigger trigger)
{
if (trigger == null) throw new ArgumentNullException(nameof(trigger));
_triggers.Add(trigger);
}
private void DoWork()
{
foreach (var trigger in _triggers)
{
if (trigger.CanExecute(DateTime.Now))
{
var task = trigger
.Execute(_cancTokenSource.Token)
.ContinueWith(x => HandleError(x));
_tasks.Add(task);
}
}
_tasks.RemoveAll(x => x.IsCompleted);
}
private void HandleError(Task task)
{
if (task.IsFaulted)
_logger.LogError(task.Exception.Message);
}
public void Dispose()
{
_timer?.Dispose();
_cancTokenSource?.Dispose();
}
}

How do you inject a repo into a service in ASP.Net Core MVC

I have a really odd problem.
I have defined a Repository for a DbSet Called PageHits.
This is the interface:
public interface IPageHitRepo
{
IQueryable<PageHit> PageHits { get; }
void AddPageHit(PageHit pageHit);
void SaveChanges();
}
This is the implementation:
public class PageHitRepo : IPageHitRepo
{
private CtasContext _context;
public PageHitRepo(CtasContext context)
{
_context = context;
}
public IQueryable<PageHit> PageHits => _context.PageHits;
public void AddPageHit(PageHit pageHit)
{
_context.Add(pageHit);
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
I configure in startup.ConfigureServices like this:
services.AddScoped<IPageHitRepo, PageHitRepo>();
This is all just normal stuff. I use this to inject into controllers all the time using constructor injection.
But right now I want to inject this repo service into another service called PageHitService.
It has a lot going on but the injection part should be simple.
I have a IPageHitService interface defined like this:
public interface IPageHitService
{
// Default implimentation overridden in PageHitService.
Task Invoke(IServiceProvider sp) => Task.FromResult(true);
Task EmailSiteHitNotification(PageHit pageHit, IServiceProvider sp);
Task LogPageHitToDB(PageHit pageHit, IServiceProvider sp);
}
Then the top of the PageHitService looks like this:
public class PageHitService : IPageHitService
{
private IPageHitRepo _pageHitRepo;
public PageHitService(IPageHitRepo pageHitRepo)
{
_pageHitRepo = pageHitRepo;
}
public async Task Invoke(IServiceProvider sp)
{
HttpContext httpContext =
sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
IWebHostEnvironment env = sp.GetRequiredService<IWebHostEnvironment>();
IConfiguration config = sp.GetRequiredService<IConfiguration>();
// _context = sp.GetRequiredService<CtasContext>();
bool visitSet =
!string.IsNullOrEmpty(httpContext.Session.GetString("VisitSet"));
and here is the registration in StartUp.ConfigureServices:
services.AddScoped<IPageHitService, PageHitService>();
But when I try to compile I get this error:
There is no argument given that corresponds to the required formal parameter 'pageHitRepo' of 'PageHitService.PageHitService(IPageHitRepo)'
Why do we not get this error in constructors?
The way I understand DI in .Net Core is it looks at the constructor in the controller and if it sees a dependency it resolves it by the registration in startup and then if it sees a dependency in the service it is resolving it resolves that service's dependency and so on.
Why is it giving me this error.
I understand if I make a default paramterless constructor the error goes away but then ASP.Net Core DI does not know which constructor to use and both constructors get hit and my page ends up with a 500 error and cannot be shown.
What am I missing here?
Update 1:
Don't know if this can help.
But in answer to Steve's comment below I do have a BaseController class and a Base PageModel.
public class BaseController : Controller
{
public BaseController(IServiceProvider sp)
{
IPageHitService pageHitService = sp.GetRequiredService<IPageHitService>();
// pageHitService.Invoke(sp).Await();
pageHitService.Invoke(sp).Wait();
}
#region Callbacks if you use AsyncMethod.Await() extension method.
private void Completed()
{
Console.WriteLine("Completed pageHitService.Invoke from BaseController");
}
private void HandleError(Exception ex)
{
// TODO: Log Error to DB;
}
#endregion
}
public class BasePageModel : PageModel
{
public BasePageModel(IServiceProvider sp)
{
IPageHitService pageHitService = sp.GetRequiredService<IPageHitService>();
// pageHitService.Invoke(sp).Await();
pageHitService.Invoke(sp).Wait();
}
#region Callbacks if you use AsyncMethod.Await() extension method.
private void Completed()
{
Console.WriteLine("Completed pageHitService.Invoke from BaseController");
}
private void HandleError(Exception ex)
{
// TODO: Log Error to DB;
}
#endregion
}
But I can't see this affecting the constructor and causing an error there.
Update 2
I found the offending code in the PageHitService:
PageHitService pageHitService = new PageHitService();
Invoke was static because I was fooling around with going the factory route.
And I could not call the non-static methods so I had to instantiate an instance to call the 2 methods in the API.
Nothing is static now so I am going to comment out that line and just try to call the methods straight up.
Here is the full class:
public class PageHitService : IPageHitService
{
private IPageHitRepo _pageHitRepo;
public PageHitService(IPageHitRepo pageHitRepo)
{
_pageHitRepo = pageHitRepo;
}
public async Task Invoke(IServiceProvider sp)
{
HttpContext httpContext =
sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
IWebHostEnvironment env = sp.GetRequiredService<IWebHostEnvironment>();
IConfiguration config = sp.GetRequiredService<IConfiguration>();
// _context = sp.GetRequiredService<CtasContext>();
bool visitSet =
!string.IsNullOrEmpty(httpContext.Session.GetString("VisitSet"));
PageHit PageHit = new PageHit
{
SessionID = httpContext.Session.Id,
UserIP = httpContext.Connection.RemoteIpAddress.ToString(),
EnvironmentName = env.EnvironmentName,
Url = httpContext.Request.PathBase + httpContext.Request.Path,
ReferrerUrl = httpContext.Request.Headers["Referer"].ToString(),
DateTime = DateTime.Now
};
PageHitService pageHitService = new PageHitService();
bool logPageHitsOn = bool.Parse(config["LogPageHitsOn"]);
bool emailSiteHitNotificationsOn = bool.Parse(config["EmailSiteHitNotificationsOn"]);
if (!visitSet)
{
// create session
httpContext.Session.SetString("VisitSet", "true");
// Emails (1st visit of session)
if (emailSiteHitNotificationsOn)
{
await pageHitService.EmailSiteHitNotification(PageHit, sp);
}
}
// Logs all PageHits to DB.
if (logPageHitsOn)
{
await pageHitService.LogPageHitToDB(PageHit, sp);
}
}
public async Task EmailSiteHitNotification(PageHit pageHit,
IServiceProvider sp)
{
IEmailService emailService = sp.GetRequiredService<IEmailService>();
StringBuilder body = new StringBuilder();
body.AppendFormat($"<b>SessionID:</b> {pageHit.SessionID}");
body.Append("<br /><br />");
body.AppendFormat($"<b>User Ip:</b> {pageHit.UserIP}");
body.Append("<br /><br />");
body.AppendFormat($"<b>Environment:</b> {pageHit.EnvironmentName}");
body.Append("<br /><br />");
body.AppendFormat($"<b>Url (Page Hit):</b> {pageHit.Url}");
body.Append("<br /><br />");
body.AppendFormat($"<b>ReferrerUrl:</b> {pageHit.ReferrerUrl}");
body.Append("<br /><br />");
body.AppendFormat($"<b>DateTime:</b> {pageHit.DateTime}");
body.Append("<br /><br />");
string subject = "CTAS Hit Notification";
await emailService.SendEmail(body.ToString(), subject);
}
public async Task LogPageHitToDB(PageHit pageHit,
IServiceProvider sp)
{
CtasContext context = sp.GetRequiredService<CtasContext>();
await context.PageHits.AddAsync(pageHit);
await context.SaveChangesAsync();
}
}

Inject into the Startup class

Is it possible to use an IOC framework like Castle Windsor to inject into the Startup method. I mean something like this:
public class Startup()
{
IMyObject MyObject = new MyObject();
public Startup(MyObject myObject)
{
MyObject = myObject();
}
}
I am trying to drop and create a database on startup using NHibernate. Alternatively is there a "better" place to drop and create the database using NHibernate?
I do something like this for integration tests using specflow.
I have a NHibernateInitializer class that I inherit in all my projects that looks like this
public abstract class NHibernateInitializer : IDomainMapper
{
protected Configuration Configure;
private ISessionFactory _sessionFactory;
private readonly ModelMapper _mapper = new ModelMapper();
private Assembly _mappingAssembly;
private readonly String _mappingAssemblyName;
private readonly String _connectionString;
protected NHibernateInitializer(String connectionString, String mappingAssemblyName)
{
if (String.IsNullOrWhiteSpace(connectionString))
throw new ArgumentNullException("connectionString", "connectionString is empty.");
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ArgumentNullException("mappingAssemblyName", "mappingAssemblyName is empty.");
_mappingAssemblyName = mappingAssemblyName;
_connectionString = connectionString;
}
public ISessionFactory SessionFactory
{
get
{
return _sessionFactory ?? (_sessionFactory = Configure.BuildSessionFactory());
}
}
private Assembly MappingAssembly
{
get
{
return _mappingAssembly ?? (_mappingAssembly = Assembly.Load(_mappingAssemblyName));
}
}
public void Initialize()
{
Configure = new Configuration();
Configure.EventListeners.PreInsertEventListeners = new IPreInsertEventListener[] { new EventListener() };
Configure.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[] { new EventListener() };
Configure.SessionFactoryName(System.Configuration.ConfigurationManager.AppSettings["SessionFactoryName"]);
Configure.DataBaseIntegration(db =>
{
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = _connectionString;
db.BatchSize = 20;
db.Timeout = 10;
db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
});
Configure.SessionFactory().GenerateStatistics();
Map();
}
public virtual void InitializeAudit()
{
var enversConf = new Envers.Configuration.Fluent.FluentConfiguration();
enversConf.Audit(GetDomainEntities());
Configure.IntegrateWithEnvers(enversConf);
}
public void CreateSchema()
{
new SchemaExport(Configure).Create(false, true);
}
public void DropSchema()
{
new SchemaExport(Configure).Drop(false, true);
}
private void Map()
{
_mapper.AddMappings(MappingAssembly.GetExportedTypes());
Configure.AddDeserializedMapping(_mapper.CompileMappingForAllExplicitlyAddedEntities(), "MyWholeDomain");
}
public HbmMapping HbmMapping
{
get { return _mapper.CompileMappingFor(MappingAssembly.GetExportedTypes()); }
}
public IList<HbmMapping> HbmMappings
{
get { return _mapper.CompileMappingForEach(MappingAssembly.GetExportedTypes()).ToList(); }
}
/// <summary>
/// Gets the domain entities.
/// </summary>
/// <returns></returns>
/// <remarks>by default anything that derives from EntityBase and isn't abstract or generic</remarks>
protected virtual IEnumerable<System.Type> GetDomainEntities()
{
List<System.Type> domainEntities = (from t in MappingAssembly.GetExportedTypes()
where typeof(EntityBase<Guid>).IsAssignableFrom(t)
&& (!t.IsGenericType || !t.IsAbstract)
select t
).ToList();
return domainEntities;
}
}
Then in my global.asax Application_Begin event handler I configure it
public class MvcApplication : HttpApplication
{
private const String Sessionkey = "current.session";
private static IWindsorContainer Container { get; set; }
private static ISessionFactory SessionFactory { get; set; }
public static ISession CurrentSession
{
get { return (ISession) HttpContext.Current.Items[Sessionkey]; }
private set { HttpContext.Current.Items[Sessionkey] = value; }
}
protected void Application_Start()
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
Application["Version"] = String.Format("{0}.{1}", version.Major, version.Minor);
Application["Name"] = ConfigurationManager.AppSettings["ApplicationName"];
//create empty container
//scan this assembly for any installers to register services/components with Windsor
Container = new WindsorContainer().Install(FromAssembly.This());
//API controllers use the dependency resolver and need to be initialized differently than the mvc controllers
GlobalConfiguration.Configuration.DependencyResolver = new WindsorDependencyResolver(Container.Kernel);
//tell ASP.NET to get its controllers from Castle
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container.Kernel));
//initialize NHibernate
ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[Environment.MachineName];
if (connectionString == null)
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
if (String.IsNullOrWhiteSpace(connectionString.ConnectionString))
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
string mappingAssemblyName = ConfigurationManager.AppSettings["NHibernate.Mapping.Assembly"];
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ConfigurationErrorsException(
"NHibernate.Mapping.Assembly key not set in application config file.");
var nh = new NHInit(connectionString.ConnectionString, mappingAssemblyName);
nh.Initialize();
nh.InitializeAudit();
SessionFactory = nh.SessionFactory;
AutoMapConfig.RegisterMaps();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ModelBinderConfig.RegisterModelBinders(ModelBinders.Binders);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
protected void Application_OnEnd()
{
//dispose Castle container and all the stuff it contains
Container.Dispose();
}
protected void Application_BeginRequest() { CurrentSession = SessionFactory.OpenSession(); }
protected void Application_EndRequest()
{
if (CurrentSession != null)
CurrentSession.Dispose();
}
}
}

recommendation pattern to use for handling sessions per web requests in mvc using NHibernate

For example.
My session factory is located in MyDomain.SessionProvider class.
Session can be open using ISession session = SessionProvider.Instance.OpenSession()
Step: SessionProvider.cs
public static SessionProvider Instance { get; private set; }
private static ISessionFactory _SessionFactory;
static SessionProvider()
{
var provider = new SessionProvider();
provider.Initialize();
Instance = provider;
}
private SessionProvider()
{
}
private void Initialize()
{
string csStringName = "ConnectionString";
var cfg = Fluently.Configure()
//ommiting mapping and db conf.
.ExposeConfiguration(c => c.SetProperty("current_session_context_class", "web"))
.BuildConfiguration();
_SessionFactory = cfg.BuildSessionFactory();
}
public ISession OpenSession()
{
return _SessionFactory.OpenSession();
}
public ISession GetCurrentSession()
{
return _SessionFactory.GetCurrentSession();
}
Step: Global.asax.cs
public static ISessionFactory SessionFactory { get; private set; }
Application Start
SessionFactory = SessionProvider.Instance.OpenSession().SessionFactory;
App_BeginRequest
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
EndRequest
dispose session
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
Step3.HomeController
I should be using current session like
var session = SessionProvider.Instance.GetCurrentSession();
using (var tran = session.BeginTransaction())
{
//retrieve data from session
}
Now, with trying to retrieve data on my controller like desc. in Step3. I got error message that my session is closed. I tried to remove Application_EndRequest block inside global.asax cause my transaction is wrapped with session but with no success. Still same error.
Second/side question: is this pattern accepted widely, or it is better to wrapped inside custom attributes on mvc controllers. Thanks.
Updated:
On my controller when try to instantiate current session in line
var session = SessionProvider.Instance.GetCurrentSession();
I'm getting following error:
**Connection = 'session.Connection' threw an exception of type 'NHibernate.HibernateException'**
**base {System.ApplicationException} = {"Session is closed"}**
Thanks #LeftyX
I solved this problem using TekPub video Mastering NHibernate with some customizations.
Global.asax
//Whenever the request from page comes in (single request for a page)
//open session and on request end close the session.
public static ISessionFactory SessionFactory =
MyDomain.SessionProvider.CreateSessionFactory();
public MvcApplication()
{
this.BeginRequest += new EventHandler(MvcApplication_BeginRequest);
this.EndRequest +=new EventHandler(MvcApplication_EndRequest);
}
private void MvcApplication_EndRequest(object sender, EventArgs e)
{
CurrentSessionContext.Unbind(SessionFactory).Dispose();
}
private void MvcApplication_BeginRequest(object sender, EventArgs e)
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
}
protected void Application_Start()
{
SessionFactory.OpenSession();
}
and inside my controller
var session = MvcApplication.SessionFactory.GetCurrentSession();
{
using (ITransaction tx = session.BeginTransaction())
{... omitting retrieving data}
}
You can find a couple of simple and easy implementations here and here and find some code here.
I like Ayende's approach to keep everything simple and clean:
public class Global: System.Web.HttpApplication
{
public static ISessionFactory SessionFactory = CreateSessionFactory();
protected static ISessionFactory CreateSessionFactory()
{
return new Configuration()
.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "hibernate.cfg.xml"))
.BuildSessionFactory();
}
public static ISession CurrentSession
{
get{ return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
protected void Global()
{
BeginRequest += delegate
{
CurrentSession = SessionFactory.OpenSession();
};
EndRequest += delegate
{
if(CurrentSession != null)
CurrentSession.Dispose();
};
}
}
In my projects I've decided to use a IoC container (StructureMap).
In case you're interested you can have a look here.

Resources