NHibernate Validator with Fluent - fluent-nhibernate

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.

Related

NHibernate Fluent Add external Assembly mappings

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);
};
}

Servicestack UserAuth Persistence using Nhibernate (using ServiceStack.Authentication.NHibernate)

I'm trying to use the ServiceStack IUserAuthRepository implementation for Nhibernate.
I have registered NHibernateUserAuthRepository in my service IOC container but I don't know how to tell Nhibernate to map the AuthUser and roles to database table.
My mapping happens when the container instanciates ISessionFactory (using FluentNhibernate).
Here's AppHost code:
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<IDatabaseFactory>(c => new Oracle10DatabaseFactory(_DomainAssembly, _DomainAssemblyName,
c.Resolve<ConfigurationParameters>()));
// Register EventPublisher
container.RegisterAutoWiredAs<EventPublisher, IEventPublisher>().ReusedWithin(ReuseScope.Request);
container.RegisterAutoWiredAs<EventPublisherInterceptor, IInterceptor>().ReusedWithin(ReuseScope.Request);
// Register Session & UnitOfWork
container.Register<NHibernate.ISession>(item =>
container.Resolve<IDatabaseFactory>().SessionFactory.OpenSession(new EventPublisherInterceptor(container.Resolve<IEventPublisher>())))
.ReusedWithin(ReuseScope.Request);
container.Register<IUnitOfWork>(item => new UnitOfWork(container.Resolve<NHibernate.ISession>())).ReusedWithin(ReuseScope.Request);
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] { new CredentialsAuthProvider() }));
Here's the Oracle10DatabaseFactory constructor:
public Oracle10DatabaseFactory(Assembly assembly, string namespace, ConfigurationParameters parameters)
{
var fileCache = new ConfigurationFileCache(assembly, parameters.PathToConfigurationFolder);
var config = fileCache.LoadConfigurationFromFile();
if (config == null)
{
var mapping = AutoMap.Assembly(assembly, new MappingConfiguration(new List<string>() { namespace }))
.Conventions.Add<ReferenceIndexConvention>()
.Conventions.Add<GuidIndexConvention>()
.UseOverridesFromAssemblyOf<MappingConfiguration>();
this.sessionFactory =
Fluently.Configure()
.Database(OracleClientConfiguration.Oracle10.ConnectionString(c => c.FromConnectionStringWithKey("ZetesMobility_DataAccess_Connectionstring_Oracle"))
.UseReflectionOptimizer())
.Mappings(m => m.AutoMappings.Add(mapping)) //.Add(baseMapping)
.CurrentSessionContext("web")
.ExposeConfiguration(c => c.BuildSchema(NHibernateExtensions.RecreateSchema()))
.ExposeConfiguration(x => x.SetProperty("hbm2ddl.keywords", "auto-quote"))
.ExposeConfiguration(fileCache.SaveConfigurationToFile)
.BuildSessionFactory();
config = fileCache.LoadConfigurationFromFile();
this.sessionFactory = config.BuildSessionFactory();
}
else
{
this.sessionFactory = config.BuildSessionFactory();
}
}
How do I add the Authentication classes to mapping configuration?
Many Thanks everyone.
In your Fluently.Config(), the section Mappings(m -> m..., you need to tell FluentNhibernate to include the mappings in the package, like so:
.Mappings(m =>
m.FluentMappings
.AddFromAssemblyOf<UserAuthPersistenceDto>())
Yours should probably end up looking something like
.Mappings(m =>
{
m.AutoMappings.Add(mapping);
m.FluentMappings
.AddFromAssemblyOf<UserAuthPersistenceDto>();
})

remove the default Cache setting in Fluent NHibernate

I'm glad to set common cache for almost all the entity like the following with Fluent NHibernate:
public static FluentConfiguration Create(string connStr)
{
return Fluently.Configure()
.Cache(x => x.UseSecondLevelCache().ProviderClass<SysCacheProvider>())
.Mappings(m =>
{
m.FluentMappings
.AddFromAssemblyOf<UserMap>()
.Conventions.Add(
// here the cache is set ReadWrite
Cache.Is(x => x.ReadWrite()),
);
})
}
But there is still some entities that I DO NOT want to cache them. How should I do?
public class ActivityMap : EntityMap<Activity>
{
public ActivityMap()
{
References<User>(m => m.Executor);
//Note: I'm not want to change the cache but REMOVE the cache!
//Cache.ReadOnly();
}
}
I'm not sure it's a real solution or just a walk around that is to set the expiration=0 in the cache region node in web.config, but whatever it works.

Fluent NHibernate - override table names

Application has many extension assemblies and they contain mappings for their classes. I need to add prefix to all (base, join, many-to-many, ...) table names in those mappings.
E.g.
Assembly: ~/bin/Extensions/Foo.dll
Original table: Page
New table: Ext_Foo_Page
Assembly: ~/bin/Extensions/Bar.dll
Original table: Page
New table: Ext_Bar_Page
What's the most straightforward way to do that?
I tried this
public class TableNameConvention : IClassConvention, IJoinedSubclassConvention, IHasManyToManyConvention
{
private string getPrefix()
{
return "Ext_Test_";
}
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
public void Apply(FluentNHibernate.Conventions.Instances.IJoinedSubclassInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
public void Apply(FluentNHibernate.Conventions.Instances.IManyToManyCollectionInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
}
but it doesn't change table names even though those methods are executed.
EDIT - The configuration
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(b => b.Server(#".\SQLEXPRESS").Database("test123").Username("sa").Password("...")))
.Mappings(m => m.FluentMappings.AddFromAssembly(assembly).Conventions.Add<TableNameConvention>().ExportTo(sw))
.ExposeConfiguration(c => { new SchemaUpdate(c).Execute(false, true); })
.BuildSessionFactory();
var xml = sb.ToString();
with FNH 1.2.0.712
it works with
var model = new PersistenceModel();
model.Add(typeof(EntityMap));
model.Conventions.Add<TableNameConvention>();
model.WriteMappingsTo(Console.Out);
but not with
m.FluentMappings.Add(typeof(EntityMap)).Conventions.Add<TableNameConvention>().ExportTo(Console.Out)
it's not called in the second example, maybe bug. but the following works
.Mappings(m =>
{
m.AutoMappings.Add(() => new AutoPersistenceModel().Conventions.Add<TableNameConvention>());
m.FluentMappings.Add(typeof(EntityMap)).ExportTo(Console.Out);
})
The following syntax works for me:
return Fluently.Configure()
.Database(...)
.Mappings(m =>
{
m.AutoMappings.Add(
AutoMap.AssemblyOf<EntityMap>(new ImporterAutomappingConfiguration())
.Conventions.Add<TableNameConvention>());
})
.BuildSessionFactory();

NHibernate questions - modifying this example for Fluent NHibernate

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();