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();
Related
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 am trying to use fluent with session per request. I am following a "recipe" from nhibernate cookbook however it uses the nhibernate config file.
I am not sure what is better but right now I am sticking with fluent config just because I would not know how to set the nhibernate config file to use fluent mapping and vanilla nhibernate mapping(hbm files).
namespace Demo.WebUI
{
public class MvcApplication : NinjectHttpApplication
{
public static ISessionFactory SessionFactory { get; private set; }
protected override void OnApplicationStarted()
{
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(
c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf
<Demo.Framework.Data.NhibernateMapping.UserMap>())
.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = SessionFactory.OpenSession();
//CurrentSessionContext.Bind(session);
}
protected void Application_EndRequest(object sender, EventArgs e)
{
//var session = CurrentSessionContext.Unbind(SessionFactory);
SessionFactory.Dispose();
}
}
}
As you can see in the Begin_Request the books tutorial had
CurrentSessionContext.Bind(session);
However if I use this it throws a error since I don't have the nhibernate config file in use.
So how do I change it to use fluent configuration? Or do I not even need to do this step?(ie is it done internally?)
You need to tell NHibernate how to handle the session context. The following might work:
Fluently.Configure()
...
.ExposeConfiguration(cfg => cfg.SetProperty(
Environment.CurrentSessionContextClass,
"web")
Also, unrelated to this: you are disposing the SessionFactory on EndRequest. That is an error.
If your project is Asp.Net web site (not web application ), in Global.asax you should use like that
NHibernate.Cfg.Environment.CurrentSessionContextClass
I'm trying to get my schema export to work with Validators Expressed in my Domain Object. I have a [NotNull] attribute AND a ValidatioDef on a property but the column is still expressed as nullable by the schema export. Im sure its a config issue, but not sure where. Some wiring has gone haywire. Here is my config and gerneration code.
[Test]
public void GenerateSchemaWithValidation()
{
var nhvConfiguration = new FluentConfiguration();
nhvConfiguration
.SetDefaultValidatorMode(ValidatorMode.UseExternal)
.Register(Assembly.Load("MyDomainAssembly")
.ValidationDefinitions())
.IntegrateWithNHibernate
.ApplyingDDLConstraints()
.And
.RegisteringListeners();
var nhibernateConfig = new Configuration().Configure();
var validatorEngine = new ValidatorEngine();
validatorEngine.Configure(nhvConfiguration);
nhibernateConfig.Initialize(validatorEngine);
ConfigureDatabaseAndMappings()
.ExposeConfiguration(cfg => new SchemaExport(cfg).Create(true, true))
.BuildSessionFactory();
}
protected static FluentConfiguration ConfigureDatabaseAndMappings()
{
return Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005.ConnectionString(c => c.FromConnectionStringWithKey("MyDb"))
.ShowSql())
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<MediaDescriptionMap>()
.Conventions.AddFromAssemblyOf<WellNamedForeignKeyColumnConvention>());
}
Yes you can. You have to tell Fluent to tell NHibernate via ExposeConfiguration:
This works.
[Test]
public void DoGenerateSchema()
{
ConfigureDatabaseAndMappings()
.ExposeConfiguration(ExportSchema)
.BuildSessionFactory();
}
private static void ExportSchema(Configuration cfg)
{
var nhvConfiguration = new FluentConfiguration();
nhvConfiguration
.SetDefaultValidatorMode(ValidatorMode.UseAttribute)
.Register(Assembly.Load("MyDomainAssembly")
.ValidationDefinitions())
.IntegrateWithNHibernate
.ApplyingDDLConstraints()
.And
.RegisteringListeners();
var validatorEngine = new ValidatorEngine();
validatorEngine.Configure(nhvConfiguration);
cfg.Initialize(validatorEngine);
new SchemaExport(cfg).Create(true, true);
}
and for those wondering like I was I know see that ValidatorMode.UseAttribute only picks up e.g.[NotNull] and ValidatorMode.UseExternal picks up ValidationDefs
All this so that business rules are encapsulated in the domain/business tier not by the database mappings. (check HunabKu's blog for some good discussions and examples)
NHibernate Validator doesn't alter the mappings that are created, it just validates your entity against your rules before saving. You need to also specify in your mappings that you don't want your columns to be nullable.
Map(x => x.Property)
.Not.Nullable();
If you're doing that for a lot of properties, it might be worth looking into using a convention; specifically a PropertyAttributeConvention would work well in your case.
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.
I'm using StructureMap as my IoC container and NHibernate as my ORM. I found an example online that shows how to have StructureMap build the ISessionFactory and the ISession so the Factory is a singleton and the Session is based on the HttpContext. This works great, but then I started using NH Profiler which told me I should always be explicitly using Transactions. So, I thought, why not let StructureMap handle that for me too? Using the code below, I got that all working fine, except, I don't know how/where to commit/rollback my transaction.
Here is how I initialize StructureMap:
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<ISessionFactory>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.ConstructedBy(cfg.BuildSessionFactory);
x.ForRequestedType<ISession>()
.CacheBy(InstanceScope.Hybrid)
.TheDefault.Is.ConstructedBy(context => context.GetInstance<ISessionFactory>().OpenSession());
x.ForRequestedType<ITransaction>()
.CacheBy(InstanceScope.Hybrid)
.TheDefault.Is.ConstructedBy(context => context.GetInstance<ISession>().BeginTransaction());
x.Scan(y =>
{
y.TheCallingAssembly();
y.WithDefaultConventions();
});
});
All my repositories look like the following:
public UserRepository(ISession session, ITransaction transaction)
{
_session = session;
_transaction = transaction;
}
And a typical method inside a repository looks like:
public void Remove(int id)
{
Remove(_session.Get<User>(id));
}
What I'm trying to do is have all the methods that I call in one HttpContext share the same Session and Transaction. Is this possible or is this totally wrong and I'm barking up the wrong tree?
Thanks in advance!
-Dan
This issue was why I created a simple UnitOfWork that combines ISession and ITransaction.
In tests, I would write the following code:
var product = new Product {Name = "Apple", Category = "Fruits"};
using (var session = _sessionFactory.OpenSession())
using (var transaction = _session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
when I really wanted:
var product = new Product {Name = "Apple", Category = "Fruits"};
using (var unitOfWork = new UnitOfWork(_sessionFactory))
{
unitOfWork.CurrentSession.Save(product);
unitOfWork.Commit();
}
Here is my unit of work implementation,
using NHibernate;
namespace NHibernateBootstrap.Core.Persistence
{
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
CurrentSession = _sessionFactory.OpenSession();
_transaction = CurrentSession.BeginTransaction();
}
public ISession CurrentSession { get; private set;}
public void Dispose()
{
CurrentSession.Close();
CurrentSession = null;
}
public void Commit()
{
_transaction.Commit();
}
}
}
As far as the when to call Commit() (either using ITransaction or UnitOfWork), I believe that it should be done explicitly before the end of your request. If you don't call it, then the UnitOfWork should clean itself up, but not commit. Alternatively, you could use an HttpModule to commit in an EndRequest handler.