I'm using S#arp Architecture 1.6 and have implemented the Rhino Security integration as per
Rhino Security - S#arp Architecture
I'm using the latest build from Rhino.Commons
My Application_EndRequest method contains
ISession session = NHibernateSession.Current;
My ComponentRegister.cs contains
container.Kernel.Register(
Component.For<IAuthorizationService>()
.ImplementedBy<AuthorizationService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IAuthorizationRepository>()
.ImplementedBy<AuthorizationRepository>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsBuilderService>()
.ImplementedBy<PermissionsBuilderService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsService>()
.ImplementedBy<PermissionsService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IUnitOfWorkFactory>()
.ImplementedBy<NHibernateUnitOfWorkFactory>()
.LifeStyle.Is(LifestyleType.Singleton),
Component.For<Rhino.Commons.IRepository<User>>()
.ImplementedBy<NHRepository<User>>()
.LifeStyle.Is(LifestyleType.Transient)
);
container.AddFacility<FactorySupportFacility>()
.Register(Component.For<ISession>()
.UsingFactoryMethod(() => NHibernateSession.Current)
.LifeStyle.Is(LifestyleType.Transient));
I have also added RhinoSecurityPersistenceConfigurer() as per instructions.
The error I'm recieving on calling
UnitOfWork.Start()
is
An association from the table Permissions refers to an unmapped class: Rhino.Security.IUser
Does anyone know what the cause of this error may be?
Has anyone successfully integrated Rhino.Security with S#arp Architecture?
Any help would be great.
Thanks
Rich
-- Additional Details --
Thanks for all the replies so far.
I've still not been able to resolve this, so thought I'd add more details.
In my Global.asax.cs I have
private void InitializeNHibernateSession()
{
NHibernateSession.Init(
webSessionStorage,
new string[] { Server.MapPath("~/bin/SwitchSnapshot.Data.dll") },
new AutoPersistenceModelGenerator().Generate(),
Server.MapPath("~/NHibernate.config"),
null, null, new RhinoSecurityPersistenceConfigurer());
}
RhinoSecurityPersistenceConfigurer :
public Configuration ConfigureProperties(Configuration nhibernateConfig)
{
Security.Configure<User>(nhibernateConfig, SecurityTableStructure.Prefix);
return nhibernateConfig;
}
I have an AuthorizationAttribute which calls
using (UnitOfWork.Start())
The error is occuring in NHibernateUnitOfWorkFactory.cs as
sessionFactory = cfg.BuildSessionFactory();
You need an NHibernate mapping for your User class (i.e. the class that implements the IUser interface). You also need a table in the database with the correct fields for your User class.
You have to let RS do some configuration work before the SessionFactory is created. Look at the second issue here http://groups.google.com/group/sharp-architecture/browse_frm/thread/4093c52596f54d23/194f19cd08c8fdd7?q=#194f19cd08c8fdd7. It should get you in the right direction.
Thanks to all who helped out.
In the end it was my own fault.
All I needed to do was to follow the S#arp Architecture Instructions a little better.
From an old version of S#arp I had 2 config files hibernate.cfg.xml and NHibernate.config. I thought I still needed both, but all I needed was hibernate.cfg.xml for S#arp version 1.6 and mapped User.cs using Fluent NHibernate.
Other changes I did was in ComponentRegister.cs
container.Kernel.Register(
Component.For<IAuthorizationService>()
.ImplementedBy<AuthorizationService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IAuthorizationRepository>()
.ImplementedBy<AuthorizationRepository>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsBuilderService>()
.ImplementedBy<PermissionsBuilderService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsService>()
.ImplementedBy<PermissionsService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IUnitOfWorkFactory>()
.ImplementedBy<NHibernateUnitOfWorkFactory>()
.LifeStyle.Is(LifestyleType.Singleton),
Component.For<Rhino.Commons.IRepository<User>>()
.ImplementedBy<NHRepository<User>>()
.LifeStyle.Is(LifestyleType.Transient)
);
container.Kernel.AddFacility<FactorySupportFacility>()
.Register(Component.For<ISession>()
.UsingFactoryMethod(() => NHibernateSession.Current)
.LifeStyle.Is(LifestyleType.Transient)
);
Then use the following in my code.
var authorizationService = IoC.Resolve<IAuthorizationService>();
using (UnitOfWork.Start())
{
}
Related
userManager.FindByEmailAsync(myEmail) throws an exception if there are multiple users with the same email.
I could use:
await context.ApplicationUsers
.FirstOrDefaultAsync(x => x.NormalizedEmail == myEmail.ToUpperInvariant());
That seems to work okay. But I'm not sure if ToUpperInvariant is the right way to check, because System.Text also has Normalize(). It won't matter right now since we are using SQL Server with a case-insensitive configuration, but I don't want things to break if we ever change that.
Am I normalizing in a way that is consistent with how Entity Framework does it? I tried to find the source code, but what I found doesn't use the NormalizedEmail field, so it's likely old.
The normalization is not done by the EF Core, but the UserManager class (using ILookupNormalizer service injected via constructor or set via KeyNormalizer property).
UserManager.FindByEmailAsync method does the normalization for you before calling the store method. The problem is that EF Core store method implementation uses SingleOrDefaultAsync which throws if there are duplicate normalized emails in the database.
To fix that, you could use UserManager.NormalizeEmail method to do the normalization, and then use FirstOrDefaultAsync query as in your sample:
var normalizedEmail = userManager.NormalizeEmail(myEmail);
var firstDuplicate = await userManager.Users
.FirstOrDefaultAsync(x => x.NormalizedEmail == normalizedEmail);
I'm relatively new to NHibernate and I've got a question about it.
I use this code snippet in my MVC project in Controller's method:
MyClass entity = new MyClass
{
Foo = "bar"
};
_myRepository.Save(entity);
....
entity.Foo = "bar2";
_myRepository.Save(entity);
The first time entity saved in database succesfully. But the second time not a single request doesnt go to database. My method save in repository just does:
public void Save(T entity)
{
_session.SaveOrUpdate(entity);
}
What should I do to be able to save and then update this entity during one request? If I add _session.Flush(); after saving entity to database it works, but I'm not sure, if it's the right thing to do.
Thanks
This is the expected behavior.
Changes are only saved on Flush
Flush may be called explicitly or implicitly (see 9.6. Flush)
When using an identity generator (not recommended), inserts are sent immediately, because that's the only way to return the ID.
you should be using transactions.
a couple of good sources: here and here.
also, summer of nHibernate is how I first started with nHibernate. it's a very good resource for learning the basics.
I have a need to fluently configure nhibernate in my S#arp application so that I can use a custom NHibernate.Search directory for each of my tenants in a multi-tenant app.
However I have googled for hours looking for a solution but can't seem to find anything current that works.
Thanks,
Paul
I haven't tried this myself, but AddConfiguration takes a dictionary of cfgProperties, which I guess you can pass the tenant specific hibernate.search.default.indexBase value to.
I had a look at this, adding the key as described above will cause a problem if you attempt to use CfgHelper.LoadConfiguration() since it will return null.
But you can configure NHSearch to use different directories for each factory using the factory key:
<nhs-configuration xmlns="urn:nhs-configuration-1.0">
<search-factory sessionFactoryName="YOUR_TENANT1_FACTORY_KEY">
<property name="hibernate.search.default.indexBase">~\IndexTenant1</property>
</search-factory>
<search-factory sessionFactoryName="YOUR_TENANT2_FACTORY_KEY">
<property name="hibernate.search.default.indexBase">~\Tenant2</property>
</search-factory>
</nhs-configuration>
If you are following instructions on
http://wiki.sharparchitecture.net/Default.aspx?Page=NHibSearch
You would need to change the method GetIndexDirectory to
private string GetIndexDirectory() {
INHSConfigCollection nhsConfigCollection = CfgHelper.LoadConfiguration();
string factoryKey = SessionFactoryAttribute.GetKeyFrom(this); // Change this with however you get the factory key for your tenants,
string property = nhsConfigCollection.GetConfiguration(factoryKey).Properties["hibernate.search.default.indexBase"];
var fi = new FileInfo(property);
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fi.Name);
}
We have a situation where we have multiple databases with identical schema, but different data in each. We're creating a single session factory to handle this.
The problem is that we don't know which database we'll connect to until runtime, when we can provide that. But on startup to get the factory build, we need to connect to a database with that schema. We currently do this by creating the schema in an known location and using that, but we'd like to remove that requirement.
I haven't been able to find a way to create the session factory without specifying a connection. We don't expect to be able to use the OpenSession method with no parameters, and that's ok.
Any ideas?
Thanks
Andy
Either implement your own IConnectionProvider or pass your own connection to ISessionFactory.OpenSession(IDbConnection) (but read the method's comments about connection tracking)
The solution we came up with was to create a class which manages this for us. The class can use some information in the method call to do some routing logic to figure out where the database is, and then call OpenSession passing the connection string.
You could also use the great NuGet package from brady gaster for this. I made my own implementation from his NHQS package and it works very well.
You can find it here:
http://www.bradygaster.com/Tags/nhqs
good luck!
Came across this and thought Id add my solution for future readers which is basically what Mauricio Scheffer has suggested which encapsulates the 'switching' of CS and provides single point of management (I like this better than having to pass into each session call, less to 'miss' and go wrong).
I obtain the connecitonstring during authentication of the client and set on the context then, using the following IConnectinProvider implementation, set that value for the CS whenever a session is opened:
/// <summary>
/// Provides ability to switch connection strings of an NHibernate Session Factory (use same factory for multiple, dynamically specified, database connections)
/// </summary>
public class DynamicDriverConnectionProvider : DriverConnectionProvider, IConnectionProvider
{
protected override string ConnectionString
{
get
{
var cxnObj = IsWebContext ?
HttpContext.Current.Items["RequestConnectionString"]:
System.Runtime.Remoting.Messaging.CallContext.GetData("RequestConnectionString");
if (cxnObj != null)
return cxnObj.ToString();
//catch on app startup when there is not request connection string yet set
return base.ConnectionString;
}
}
private static bool IsWebContext
{
get { return (HttpContext.Current != null); }
}
}
Then wire it in during NHConfig:
var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.Provider<DynamicDriverConnectionProvider>() //Like so
I have been successfully using NHibernate, but now I am trying to move to Fluent NHibernate. I have created all of my mapping files and set up my session manager to use a Fluent Configuration. I then run my application and it runs successfully, but no data is returned.
There are no errors or any indication that there is a problem, but nothing runs.
when using NHibernate, if I don't set my hbm xml files as an embedded resource, this same thing happens. This makes me wonder what I have to set my Map classes to. Right now, they are just set to Compile, and they are compiled into the dll, which I can see by disassembling it.
Does anyone have any thoughts as to what may be happening here?
Thanks
private ISessionFactory GetSessionFactory()
{
return Fluently.Configure()
.Database(
IfxOdbcConfiguration
.Informix1000
.ConnectionString("Provider=Ifxoledbc.2;Password=mypass;Persist Security Info=True;User ID=myuser;Data Source=mysource")
.Dialect<InformixDialect1000>()
.ProxyFactoryFactory<ProxyFactoryFactory>()
.Driver<OleDbDriver>()
.ShowSql()
)
.Mappings(
x => x.FluentMappings.AddFromAssembly(System.Reflection.Assembly.GetExecutingAssembly())
//.ExportTo("C:\\mappings")
)
.BuildSessionFactory();
}
Does the executing assembly contain the fluent mapping classes? I would try:
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<MappedType>())
Where MappedType is a class that has a fluent mapping.
They should just be set to compile, that's fine. Nothing special needed here. The problem is most likely in your fluent configuration rather than the mapping.