Dynamically change the id generator to "assigned" in NHibernate class mapping
I am doing some bulk inserts in a winforms application based on NHibernate. It would be of great help to me If I could dynamically change the ID generator type from "guid.comb" to "assigned" during program execution.
I came across James Kovacs' Webl开发者_JAVA百科og entry Testing Immutable Entities with NHibernate
where he does a similar thing when he changes the class from immutable to mutable, but I just can't apply this to changing the generator type. Any help would be appreciated.
As stated in the answer to the question I linked to in my comment, you cannot change it after the SessionFactory has been created. Therefore, the only option you have is keeping a second instance of the SessionFactory (preferably also as a Singleton) around. That doesn't have to be created at the same time as the first. You can create it when needed, but since the creation is quite expensive, the recommendation is to create it once and keep it.
However, if you really only need it for a bulk insert that occurs only once or twice during the runtime of the application, then you could also get rid of it after the operation.
That was the theory, now to the practical part. The easy way would be having a copy of the Entity.hbm.xml file where you just change the generator attribute. For the creation of the SessionFactory you need to provide a parameter (maybe an Enum) so that you can decide which .hbm.xml files to use and which ones to ignore.
I suggest naming the default hbm file Entity.Default.hbm.xml and the modified one Entity.Special.hbm.xml. All the other hbm files can keep their names.
Here is a modifed version of the method I use to create the SessionFacory. (I put a bool as parameter here but in my code I use an Enum.)
private ISessionFactory BuildSessionFactory(bool useSpecialHbmFiles)
{
Configuration config = new Configuration();
config.SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, "...");
config.SetProperty(NHibernate.Cfg.Environment.Dialect, "...");
config.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, "...");
config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "...");
config.SetProperty(NHibernate.Cfg.Environment.Isolation, "Serializable");
config.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, "...");
config.SetProperty(NHibernate.Cfg.Environment.ShowSql, "true");
config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, "none");
// filter hbm Files
// Set reference to entity assembly
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(MyEntity));
// get Resource-files
string[] resources = assembly.GetManifestResourceNames();
// scan through all the hbm files and filter them according to the parameter
foreach (string hbmFile in resources)
{
// This filtering here could probably be done simpler, but this is easy to understand
bool addFile = false;
// ignore any file that does not end with .hbm.xml
if (hbmFile.EndsWith(".hbm.xml"))
{
if (hbmFile.ToLower().EndsWith(".default.hbm.xml"))
{
if (!useSpecialHbmFiles)
{
// we want that file for this SessionFactory
addFile = true;
}
}
else if (hbmFile.ToLower().EndsWith(".special.hbm.xml"))
{
if (useSpecialHbmFiles)
{
// we want that file for this SessionFactory
addFile = true;
}
}
else
{
// neither default nor special -> we want that file no matter what
addFile = true;
}
if (addFile)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(assembly.GetManifestResourceStream(hbmFile)))
{
string resourceContent = sr.ReadToEnd();
config.AddXmlString(resourceContent);
}
}
}
}
// create Sessionfactory with the files we filtered
ISessionFactory sessionFactory = config.BuildSessionFactory();
return sessionFactory;
}
Edit:
If you only want to modify the generator class at runtime, you can modify the configuation before building the SessionFactory like this:
// get the mapping's Key property
NHibernate.Mapping.SimpleValue keyValue =
config.GetClassMapping(typeof(MyEntity)).Key as NHibernate.Mapping.SimpleValue;
if (keyValue != null)
{
// set GeneratorStrategy (the same string you would put in generator class="..." in the hbm file)
keyValue.IdentifierGeneratorStrategy = "assigned";
}
Now you can pass a parameter to your CreateSessionFactory() method and modify the configuration. You will still need a second SessionFactory. You cannot modify an existing one.
Edit 2 (disabling Many-To-One):
To disable a NOT-NULL property in the mapping for many-to-one properties, try the following:
NHibernate.Mapping.PersistentClass mapping = config.GetClassMapping(typeof(MyEntity));
foreach (NHibernate.Mapping.Property prop in mapping.PropertyIterator)
{
if (prop.Value is NHibernate.Mapping.ManyToOne)
{
prop.IsOptional = true;
}
}
Of course, this will only work if the foreign-key column in the DB allows NULL values.
精彩评论