I am having an issue with NHibernate not deleting rows from the database. Nhibernate is saving and updating to the same database without issue.
After running SQL profiler it appears that there is no delete SQL being sent to the database - which makes me think it's a configuration issue but nothing is standing out to me...
Config
Nhibernate version : 3.3.1.4000
FluentNHibernate Version : 1.3.0.733
SQL Server Version : 2008R2
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.connection_string_name">IntermediateDatabase</property>
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="show_sql">true</property>
<property name="connection.release_mode">auto</property>
<property name="adonet.batch_size">500</property>
<!-- Mapping assemblies -->
<!-- Can't map it for Fluent NHibernate here; instead, load the mapping assembly in Global.asax.cs.
If you're still using HBMs, you can use the mapping here or pass the assembly via Global.asax.cs
as well, just like you can do with the Fluent NHibernate assembly(s). -->
</session-factory>
</hibernate-configuration>
Thanks
Based on the information from your comment... It seems that your working method is like:
UPDATE
public T SaveOrUpdate(T entity)
{
using (Session)
{
using (TransactionScope scope = new TransactionScope())
{
Session.SaveOrUpdate(entity); scope.Complete();
}
return entity;
}
}
And this is absolutely correct... because your session FlushMode would most likely be:
session.FlushMode = FlushMode.Commit;
Pleae see more details here: Nhibernate Flush works commit doesn't
DELETE
But the poor sister Delete() is not fully supported as mighty Update()
public void Delete(T entity)
{
using (Session)
{
this.Session.Delete(entity);
}
}
So, while even for Delete() the session FlushMode is still hooked on a transaction commit ... there is no transaction. And that's for sure (well most likely) the real reason (as suspected)
Summary, treat both, Update and Delete as the twins... and give them the same care - i.e. transaction
If the deleted object is a part of collection of another object then in the mapping give .Cascade.AllDeleteOrphan() and just remove the item from the collection, then nhibernate will send the delete statement to DB.
Related
I have an object of type Video. I am storing Videos in my database, but I want each video entry to be unique. Uniqueness is determined by an 11-digit string provided by the client.
As such, I've generated this Video.hbm.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Streamus" namespace="Streamus.Domain">
<class name="Video" table="[Videos]" lazy="false" mutable="false">
<id name="Id" length="11" type="String" />
<property name="Title" not-null="true" />
<property name="Duration" not-null="true" />
<property name="Author" not-null="true" />
</class>
</hibernate-mapping>
I have no intention of deleting or updating any entries to this table. So, I've tagged my class as immutable.
Now, I thought this was a pretty good way of going about things, but I am experiencing issues with cascade-updating from a parent object. Because my ID is generated client-side, and I do not leverage a second identifier, NHibernate is unable to reliably determine whether a Video needs to be Inserted or Updated (note that it should never update due to being immutable.)
Something like:
Video video = new Video("s91jgcmQoB0", "Tristam - Chairs", 219, "MeoMix");
new VideoManager().Save(video);
// Create a new PlaylistItem and write it to the database.
string title = video.Title;
PlaylistItem playlistItem = new PlaylistItem(title, video);
Playlist.AddItem(playlistItem);
PlaylistManager.SavePlaylistItem(playlistItem);
where PlaylistItem's hbm.xml references Video as:
<many-to-one name="Video" column="VideoId" not-null="true" cascade="save-update" />
The above code generates a NonUniqueObjectException. This is because the VideoManager works with the Video in one session, closes it, then PlaylistManager re-references the same Video in a new Session, but NHibernate can't accomodate because it doesn't have good IDs to go off of.
Removing the option of cascade="save-update" fixes the issue, but then I don't get the benefit of having my Video object created implicitly in the database by NHibernate. Instead, I would have to manually save the Video object and, afterwards, save the PlaylistItem object. This is clearly not ideal.
So, I'm wondering -- should I be assigning a GUID to my Video object? If so, how can I enforce the fact that I only wish to have unique Video entries in my table where uniqueness is defined by the 11 digit string ID?
Let me know if I can provide any more information to make this more clear.
First of all modify your ID section in you video.hbm.xml to have an "assigned" generator
<id name="id" ....>
<generator class="assigned" />
</id>
That tells nhibernate that ur IDs are manually generated from the client.
Also having ur same object being manipulated by multiple session will cause consistency errors. So either use a unique session for your batch of actions or make sure ur object is properly tracked in all sessions by using session.Merge
My goal is to configure NHibernate.Envers by a configuration file. As far as I understod the documentation, it should be easily possible by adding a property entry in the NHibernate Core xml file.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="NHibernate.Test">
....
<property name="nhibernate.envers.audit_table_suffix">_history</property>
</session-factory>
</hibernate-configuration>
But this leads to an Exception:
The 'name' attribute is invalid - The value 'nhibernate.envers.audit_table_suffix' is invalid according to its datatype 'String' - The Enumeration constraint failed.
I do understand the exception, NHibernate core checks for valid content in the name attribute. How can I set a NHibernate.Envers property?
The same property works when I set it by code:
cfg = new Configuration();
cfg.Configure("NHibernate.cfg.xml");
// NHibernate.Envers Configuration
cfg.SetProperty("nhibernate.envers.audit_table_suffix", "_history");
Unfortunately the documentation is wrong. You cannot put this in your NH Core configuration because NH Core will do schema validation on your xml (and is unaware of the envers properties).
I will update Envers docs shortly.
We're building a project using NHibernate and Castle with the Validators project. I'm trying to upgrade it to the latest supported version between all of those. I've gotten the application working without errors, but I'm getting the exception below in a few of my unit tests. These are tests that don't actually touch the database in any way, but test functionality around the mapped entities.
NHibernate.Bytecode.ProxyFactoryFactoryNotConfiguredException:
The ProxyFactoryFactory was not configured.
Initialize 'proxyfactory.factory_class' property of the session-factory
configuration section with one of the available NHibernate.ByteCode providers.
Example:
<property name='proxyfactory.factory_class'>
NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
</property>
Example:
<property name='proxyfactory.factory_class'>
NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
</property>
[Continues down stack trace...]
Below is my config file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="Linx2">
<property
name="connection.driver_class">NHibernate.Driver.NpgsqlDriver</property>
<property name="dialect">Linx2.Common.Framework.PostgreSQL83Dialect,
Linx2.Common.Framework</property>
<property name="connection.connection_string">[Hidden so I don't get fired.]</property>
<property name="adonet.batch_size">10</property>
<property name="show_sql">false</property>
<property name="use_outer_join">true</property>
<property name="command_timeout">60</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
</property>
<property name="connection.release_mode">after_transaction</property>
<mapping assembly="NHibernate.Collection.Observable" />
</session-factory>
</hibernate-configuration>
I have the config mapping there, and it works in the application. I'm also including the NHibernate.ByteCode dll. However, in these tests it is ignored. I've tried manually starting the configuration in individual test and even stopped and confirmed mid-test that the configuration has the item. However, the exception is thrown in the code below on the IsInitialized call.
if (NHibernateUtil.IsInitialized(ChildAssociations))
{
ChildAssociations.ForEach(x => isValid = isValid && x.Element.IsValid(validatedObjects));
}
This worked previously with no problems in NHibernate build for 2.2. Any help would be greatly appreciated. I've been beating my head on it for the last 4 hours.
Apparently NHibernateUtil needs not only to have the configuration initialized, but needs the session factory to be built. I was able to get it to work by manually running the config and building the session factory in the tests. It wasn't a problem in the app because the session factory had been built before hand.
var cfg = new NHibernate.Cfg.Configuration().Configure();
var sessionFactory = cfg.BuildSessionFactory();
I've got an interesting error with an ASP.NET webforms application I've been working on. I am using NHibernate (regular, not fluent), connecting to an sqlite database, with the following mapping and configuration:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SQLiteDriver</property>
<property name="connection.connection_string">Data Source=C:\Path\To\Database.db;Version=3</property>
<property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
<property name="query.substitutions">true=1;false=0</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
There are a number of entities, but the following is a simple example:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="XBMC.Data"
namespace="XBMC.Data.Model.Domain">
<class name="Genre" table="genre">
<id name="Id" column="idGenre" type="int">
<generator class="native"/>
</id>
<property name="Name" column="strGenre" type="string" />
</class>
</hibernate-mapping>
This file is configured as an Embedded resource. This goes hand in hand with:
public class Genre : IRecord
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
There is also an NHibernateHelper class which is used to grab a session (I've gleaned most of this from http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx which is a fantastic tutorial on getting started with NHibernate).
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
Configuration config = new Configuration();
config.Configure();
config.AddAssembly(typeof(IRecord).Assembly);
_sessionFactory = config.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
So far so good. I've been following a test-driven-development approach, and have some test-cases like the following:
[Test]
public void CanLoadGenre()
{
using (ISession session = NHibernateHelper.OpenSession())
{
Genre g = session.Get<Genre>(759);
Assert.That(g.Name, Is.EqualTo("Action"));
}
}
These work fine (I'm using the latest version of NUnit with TestDriven.NET FWIW). I can see the SQL generated by NHibernate and everything looks perfect. The problems start when I try to use my library in an ASP.NET application.
What I would like to do is bind a ListView to an ObjectDataSource. To do this I've written a GenreProvider and this just gets a list of the available Genre objects. In the unit tests, this works fine. As soon as anything NHibernate-related gets run in the ASP application, I get NHibernate.MappingException: No persister for: Genre exceptions thrown and everything goes awry.
Specifically, it fails on the following line (the return statement):
using (ISession session = NHibernateHelper.OpenSession())
return session.CreateCriteria(typeof(T).Name).List<T>();
(This is in a generic Repository class - however it works perfectly during unit tests).
So in summary: NHibernate seems to be working correctly during my unit tests, but fails when used in an ASP application. I'm confident that it is a problem in my configuration, but have scoured Google for 'no persister' errors (solutions have included ensuring the 'Embedded resource' setting for mapping files, various combinations of AddClass, AddAssembly etc on the configuration, setting <mapping assembly="..." /> in my config etc, but with no success) but most likely cannot see the wood for the trees.
Can anyone shed any light on this and stop me from going insane? (I can post more code if needed, have held back to prevent information overload!)
Thanks!
Try adding this in your hibernate-configuration file (or indeed your web.config if thats where it lives)
<property name="current_session_context_class">web</property>
I have the Membase server installed with a couple buckets setup and I was looking for a good tutorial or example of how to use this as the 2nd level cache with NHibernate.
I am interested in what a sample configuration would look like and if there is anything I need to do in code or if I can handle it all from my NHibernate mappings.
Thanks for any assistance.
In your mapping files, you will need to include the property:
<class name="ClassName" table="Table">
<cache usage="read-write" />
<!-- SNIP -->
</class>
Options are read-write (read committed isolation), nonstrict-read-write (objects that are rarely written, better performance but increased chance of stale data), or read-only (data that never changes).
Then, in your web (or app) config you need a section to configure memcached:
<configuration>
<configSections>
<!-- SNIP -->
<section name="memcache" type="NHibernate.Caches.MemCache.MemCacheSectionHandler,NHibernate.Caches.MemCache" />
</configSections>
<memcache>
<memcached host="127.0.0.1" port="11211" weight="2" />
</memcache>
<!-- SNIP -->
</configuration>
Finally, in your session factory configuration be sure to use:
<hibernate-configuration>
<session-factory>
<!-- SNIP -->
<property name="expiration">300</property> <!--memcache uses seconds -->
<property name="cache.provider_class">NHibernate.Caches.MemCache.MemCacheProvider,NHibernate.Caches.MemCache</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">false</property> <!-- true if you want to cache query results -->
</session-factory>
</hibernate-configuration>
Of course you will need to download and reference a dll from the appropriate version of NHibernate.Caches to get the right cache provider. The memcached one takes a dependency on ICSharpCode.SharpZipLib and Memcached.ClientLibrary as well (s/b included in the download)
If you're using fluent NHibernate, there is a .Cache method in the setup chain for a session factory that you can use, though some of the properties need to be set manually through a call to .ExposeConfiguration.