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>();
})
Related
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 successfully configured StrctureMap and FluentNHibernate to work together with a parameter-less constructor. What I need to do now is set the interceptor with parameters.
The following code below works well in my MVC application:
protected void Application_Start()
{
//Code removed from here to save space...
ObjectFactory.Initialize(x => {
x.For<IInterceptor>().Singleton().Use(context => new MyInterceptor());
x.For<ISessionFactory>().Singleton().Use(context => CreateSessionFactory(context.GetInstance<IInterceptor>()));
x.For<ISession>().HttpContextScoped().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
x.For<IAuditDao>().Use<AuditDao>().Ctor<ISessionFactory>().Is(context => context.GetInstance<ISessionFactory>());
});
}
public static ISessionFactory CreateSessionFactory(IInterceptor interceptor)
{
return Fluently.Configure()
.Database(MySQLConfiguration
.Standard
.ConnectionString(c => c.FromConnectionStringWithKey("MySqlConnectionString")))
.Mappings(m => { m.FluentMappings.Add<AuditItemMap>(); })
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(true, true))
.ExposeConfiguration(config => config.SetInterceptor(interceptor))
.BuildSessionFactory();
}
What I now need to do is pass an instance of AuditDao into the the interceptor. At the moment 'MyInterceptor' which inherits from the NHibernate 'EmptyInterceptor' has two constructors:
public class MyInterceptor : EmptyInterceptor
{
private IAuditDao AuditDao;
public MyInterceptor()
{
}
public MyInterceptor(IAuditDao auditDao)
{
AuditDao = auditDao;
}
}
Now obviously an instance of AuditDao needs an instance of SessionFactory and SessionFactory would need an instance of AuditDao.
How do I solve this circular reference problem?
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();
I'm a n00b. Here's what I want to do:
Use AutoMapping to configure every property between the model -> table. Then I would like to override 2 specific items in a fluent map. The two items are: Id & Table name.
So my Maps look like this:
public class BillMasterMap : ClassMap<BillMaster>
{
public BillMasterMap()
{
Table("BILLMAST");
Id(x => x.SYSKEY);
}
}
And my factory settings look like this:
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Where(x => x.Namespace.EndsWith("Entities"))))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<BillInvoiceMap>())
.BuildSessionFactory();
}
return _sessionFactory;
}
The problem is that FNH finds the Id override for BillMaster, but not for BillInvoice which looks like this (identical it seems)
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Id(x => x.SYSKEY);
Table("BILLINV");
}
}
I've gotten around the problem by configuring my automapping with the Setup() as shown below:
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities"))))
But I would like to combine auto & fluent as other tables don't use "SYSKEY" as their Id column.
Thoughts? Is this a FNH bug?
Got it working.
1.) I have to setup AutoMapping with the Setup method I described above
2.) Additionally I have to setup fluentmappings with the Id method
when both are setup like this, then it works for me.
So
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(
c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<BaseEntity>();
m.AutoMappings.Add(AutoMap.AssemblyOf<BaseEntity>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities")));
})
.BuildSessionFactory();
}
return _sessionFactory;
}
And the map
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Table("BILLINV");
Id(x => x.SYSKEY);
}
}
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.