I'm building a asp.net mvc web application. And I'm running quartz in the asp.net context.
I'm using fluent nhibernate for my or mappings.
I'm building a simple job that goes writes an entry in the database.
public void Execute(JobExecutionContext context)
{
ISession session = DataSourceConfiguration.GetSessionFactory().OpenSession();
session.SaveOrUpdate(new JobLogEntry() { Created = DateTime.Now, Message = "Twitter feed read" });
session.Close();
session.Dispose();
}
public static ISessionFactory GetSessionFactory()
{
return Fluently.Configure()
.Database(CurrentDataBaseConfiguration)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Entry>()
.Where(t => t.Namespace == "QuickBlog.BlogModel.Entities")
))
.BuildSessionFactory();
}
Here is where the error occurs:
public static IPersistenceConfigurer CurrentDataBaseConfiguration
{
get
{
if (_dataBaseConfiguration != null)
return _dataBaseConfiguration;
var config = MsSqlConfiguration.MsSql2005
.ConnectionString(c => c.FromConnectionStringWithKey("QuickBlogDB"))
.UseReflectionOptimizer()
.Cache(c => c.Not
.UseQueryCache())
.ShowSql();
_dataBaseConfiguration = config;
return _dataBaseConfiguration;
}
}
The problem is that c.FromConnectionStringWithKey("QuickBlogDB") is null or empty. How do I get a hold of the configuration info in the quartz.net job?
First of all, you probably should not create your session factory inside your job. I would recommend of having a static class to hold session factory and initialize it in earlier stage, say applications Application_Start method.
It's more resource efficient (a lot) and makes it easier to debug problems as your app won't even start before configuration and preconditions are right.
Related
I'm using .Net Core 2.1 and an Aggregate / Facade pattern for my dependencies (which I happily do elsewhere using Ninject / .net 4.6). But when I try to pass through options I get a null (Debugging I can see there being picked up) but there not passed to Autofac (I'm fairly sure its my as they weren't when I tried Ninject either).
I've made a simple test project (new .net core web application /2.1) and then added a minimal amount of code to replicate
Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<ApiEndpointsConfiguration>(Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the container builder.
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterAggregateService<IViewModelProvider>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("TEST")).ToArray();
builder.RegisterAssemblyTypes(assemblies)
.Where(t => t.IsClass)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
builder.RegisterAggregateService<IDomainServiceProvider>();
ApplicationContainer = builder.Build();
var chkOptions = ApplicationContainer.Resolve<IOptions<ApiEndpointsConfiguration>>();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(ApplicationContainer);
}
Program.cs
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(services => services.AddAutofac())
.UseStartup<Startup>();
}
IViewModelProvider.cs
public interface IViewModelProvider
{
IProgrammeViewModelBuilder ProgrammeViewModel { get; }
}
IProgrammeViewModelBuilder.cs
public interface IProgrammeViewModelBuilder
{
ProgrammeViewModel GetProgrammeViewModel();
}
My initial issue was that in my service, controller calls the injected viewmodelbuilder
var viewModel = _viewModels.ProgrammeViewModel.GetProgrammeViewModel();
which in turn calls the service -
readonly IOptions<ApiEndpointsConfiguration> _apiSettings;
public ProgrammeService(IOptions<ApiEndpointsConfiguration> apiSettings) : base (new Uri(apiSettings.Value.BaseAddress))
{
_apiSettings = apiSettings;
}
but at that point (the constructor firing) the service configuration items were null so I've stepped through and I can see that services has the values for "ApiEndpointsConfiguration" picked up but when they get passed through to the "builder" the values are null
ApplicationContainer.Resolve<IOptions<ApiEndpointsConfiguration>>();
shows null for the values inside.
Not sure what it is I'm doing wrong?
:( Truly this is when the answer is so much simpler thank it looks. Kudos to anyone who spots it;
services.Configure<ApiEndpointsConfiguration>(Configuration.GetSection("ApiEndpointsConfiguration"));
rather than
services.Configure<ApiEndpointsConfiguration>(Configuration);
So essentially whilst I thought I could see it debugging I was seeing the raw JSON provided values not the "configured service". I'll leave this here as a lesson to myself to check the simple things first.
Not sure what what was actually being "registered" in my first effort.
I have a project with my mappings and entities stored in other class libraries and NHibernate layers in another project. In my testing project I would like to add these mapping via fluently configure... Mappings... via assebly and not individually. In my code below you can see I added just one entity.. But I would like to configure it to scan my other assemblies. I am sure I am just missing the obvious here.. any pointers would be greatly appreciated...
[Test]
public void Can_generate_schemaFluently()
{
var cfg = new Configuration();
cfg.Configure();
Configuration configuration = null;
ISessionFactory SessionFactory = null;
ISession session = null;
SessionFactory = Fluently.Configure(cfg)
*** WOULD LIKE TO ADD MY ASSEBLIES and autoscan for objects instead ***
.Mappings(m => m.FluentMappings
.Add(typeof(StudentEOMap))
)
.ExposeConfiguration(x => configuration = x)
.BuildSessionFactory();
session = SessionFactory.OpenSession();
object id;
using (var tx = session.BeginTransaction())
{
var result = session.Get<StudentEO>(1541057);
tx.Commit();
Assert.AreEqual(result.StudId, 1541057);
}
session.Close();
}
AutoMapping
If you want to filter through types, you can use the IAutomappingConfiguration and derive from DefaultAutomappingConfiguration like this:
public class StandardConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
// Entity is the base type of all entities
return typeof(Entity).IsAssignableFrom(type);
}
}
You can also use DefaultAutomappingConfiguration if you have no need to filter. But my further example uses the StandardConfiguration.
Change your configuration like this, to populate your types to FluentNHibernate:
SessionFactory = Fluently.Configure(cfg)
.Mappings(m => MapMyTypes(m))
.ExposeConfiguration(x => configuration = x)
.BuildSessionFactory();
And the MapMyTypes method should look like this:
private void MapMyTypes(MappingConfiguration m)
{
m.AutoMappings.Add(AutoMap.Assemblies(new StandardConfiguration(),
Assembly.GetAssembly(typeof(Entity)),
Assembly.GetAssembly(typeof(OtherAssemblyEntity)))
);
}
You can add multiple Assemblies and all get filtered through the StandardConfiguration.
Edit
FluentMappings
It seems that i misread your question. To add mappings you can use a similar method to achieve that but without a IAutomappingConfiguration.
Just change the MapMyTypes method to:
private void MapMyTypes(MappingConfiguration m)
{
m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(EntityMap)));
}
Combine
You can also combine the FluentMapping and the AutoMapping like this:
private Action<MappingConfiguration> MapMyTypes()
{
return m =>
{
MapFluent(m);
MapAuto(m);
};
}
I have the following configuration:
_container = new WindsorContainer ();
var factory = new SessionFactoryManager().CreateSessionFactory();
_container.Register(Component.For<NHibernate.ISessionFactory>().Instance(factory));
And then elsewhere I have:
var authRepo = new NHibernateUserAuthRepository (_container.Resolve<NHibernate.ISessionFactory>());
_container.Register (Component.For<IAuthRepository>().Instance(authRepo));
public class SessionFactoryManager
{
public ISessionFactory CreateSessionFactory()
{
try {
var autoMap = AutoMap.AssemblyOf<Artist>()
.Where(t => typeof(Entity).IsAssignableFrom(t))
.UseOverridesFromAssemblyOf<LocationMappingOverride>();
return Fluently.Configure()
.Database(PostgreSQLConfiguration.PostgreSQL82.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")).AdoNetBatchSize(50))
.Mappings(m => m.AutoMappings.Add(autoMap))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ServiceStack.Authentication.NHibernate.UserAuthMap>())
.ExposeConfiguration(TreatConfiguration)
.BuildSessionFactory();
} catch (Exception ex) {
var m = ex;
}
return null;
}
protected virtual void TreatConfiguration(NHibernate.Cfg.Configuration configuration)
{
configuration.SetProperty("generate_statistics", "true");
configuration.SetProperty("current_session_context_class", "thread");
var update = new SchemaUpdate(configuration);
update.Execute(false, true);
}
}
This all works for the rest of my app but when i try to use anything to do with the NH auth repo I get:
No CurrentSessionContext configured (set the property current_session_context_class)!
Anyone got any clues wha gwarn?
[UPDATE]
In my AppHost.Configure method I have added the following:
this.GlobalRequestFilters.Add ((req, res, vm) => {
CurrentSessionContext.Bind(container.Resolve<NHibernate.ISession>());
});
To no avail - I also have no idea how i would dispose of that :p
Also I am confused as ISessionFactory is injected in like every other part of my app:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Authentication.NHibernate/NHibernateUserAuthRepository.cs#L20
So surely that should just work?
Yes, you need to explicitly bind the current session to the context:
CurrentSessionContext.Bind(session);
After you built the session. Don't forget to dispose of it too when you no longer need it.
I have an identity mapping like so:
Id(x => x.GuidId).Column("GuidId")
.GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
When I retrieve an object from the database, the GuidId property of my object is Guid.Empty, not the actual Guid (the property in the class is of type System.Guid). However, all of the other properties in the object are populated just fine.
The database field's data type (SQL Server 2005) is uniqueidentifier, and marked as RowGuid.
The application that is connecting to the database is a VB.NET Web Site project (not a "Web Application" or "MVC Web Application" - just a regular "Web Site" project). I open the NHibernate session through a custom HttpModule. Here is the HttpModule:
public class NHibernateModule : System.Web.IHttpModule
{
public static ISessionFactory SessionFactory;
public static ISession Session;
private static FluentConfiguration Configuration;
static NHibernateModule() {
if (Configuration == null) {
string connectionString = cfg.ConfigurationManager.ConnectionStrings["myDatabase"].ConnectionString;
Configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(cs => cs.Is(connectionString)))
.ExposeConfiguration(c => c.Properties.Add("current_session_context_class", "web"))
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<LeadMap>().ExportTo("C:\\Mappings"));
}
SessionFactory = Configuration.BuildSessionFactory();
}
public void Init(HttpApplication context) {
context.BeginRequest += delegate {
Session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(Session);
};
context.EndRequest += delegate {
CurrentSessionContext.Unbind(SessionFactory);
};
}
public void Dispose() {
Session.Dispose();
}
}
The strangest part of all, is that from my unit test project, the GuidId property is returned as I would expect. I even rigged it to go for the exact row in the exact database as the web site was hitting. The only differences I can think of between the two projects are
The unit test project is in C#
Something with the way the session is managed between the HttpModule and my unit tests
The configuration for the unit tests is as follows:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(cs => cs.Is(connectionString)))
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<LeadDetailMap>());
I am fresh out of ideas. Any help would be greatly appreciated.
Thanks
I had a similar problem when in the mapping attribute Name was not set. You can try to save your fluent mappings into Xmls and check them.
I'm new to NHibernate...
I have been following this NHibernate Tutorial from Gabriel Schenker :
http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx
However, this tutorial uses hbm files. I would like to know - what do I need to do to modify the hepler class below (which creates a session factory) so that it uses my ClassMap files instead of hbm?
Also, is this the best way to deal with factory creation? How often will the factory be created in this example - once per request? (I'm not really sure I understand the lifetime of _sessionFactory in this case).
Thank you!
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if(_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof(Product).Assembly);
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
Session factory usually should only be created once (using the singleton pattern) for the lifetime of the app.
And here is sample code for creating the SessionFactory with Fluent Nhibernate:
var mssqlConfig = MsSqlConfiguration
.MsSql2008
.ConnectionString(c => c.Is(connectionstring))
.UseOuterJoin()
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
var sessionFactory = Fluently.Configure()
.Database(mssqlConfig)
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(Product).Assembly))
.BuildSessionFactory();
Not using Fluent Config (from the top of my head, syntax might not be exact):
var config = new NHibernate.Cfg.Configuration().Configure();
var model = new PersistenceModel();
model.Configure(config);
model.AddMappingsFromAssembly(typeof(Product).Assembly);
var sessionFactory = config.BuildSessionFactory();